Chromium Code Reviews| Index: views/touchui/touch_selection_controller_impl.cc |
| diff --git a/views/touchui/touch_selection_controller_impl.cc b/views/touchui/touch_selection_controller_impl.cc |
| index 10bf572820a0f7849de07cf8cce17b290d2123c6..b24cb511e1b4138cd7d7c58706c4e1619ee894a0 100644 |
| --- a/views/touchui/touch_selection_controller_impl.cc |
| +++ b/views/touchui/touch_selection_controller_impl.cc |
| @@ -4,15 +4,23 @@ |
| #include "views/touchui/touch_selection_controller_impl.h" |
| +#include "base/time.h" |
| +#include "grit/ui_strings.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/canvas_skia.h" |
| #include "ui/gfx/path.h" |
| #include "ui/gfx/rect.h" |
| +#include "ui/gfx/screen.h" |
| #include "ui/gfx/size.h" |
| #include "ui/gfx/transform.h" |
| -#include "views/widget/widget.h" |
| -#include "views/controls/label.h" |
| #include "views/background.h" |
| +#include "views/controls/button/button.h" |
| +#include "views/controls/button/text_button.h" |
| +#include "views/controls/label.h" |
| +#include "views/layout/box_layout.h" |
| +#include "views/widget/widget.h" |
| namespace { |
| @@ -23,6 +31,17 @@ const int kSelectionHandleAlpha = 0x7F; |
| const SkColor kSelectionHandleColor = |
| SkColorSetA(SK_ColorBLUE, kSelectionHandleAlpha); |
| +const int kContextMenuCommands[] = {IDS_APP_CUT, |
| + IDS_APP_COPY, |
| +// TODO(varunjain): PASTE is acting funny due to some gtk clipboard issue. |
| +// Uncomment the following when that is fixed. |
| +// IDS_APP_PASTE, |
| + IDS_APP_DELETE, |
| + IDS_APP_SELECT_ALL}; |
| +const int kContextMenuPadding = 2; |
| +const int kContextMenuTimoutMs = 1000; |
| +const int kContextMenuVerticalOffset = 10; |
| + |
| // Convenience struct to represent a circle shape. |
| struct Circle { |
| int radius; |
| @@ -31,7 +50,7 @@ struct Circle { |
| }; |
| // Creates a widget to host SelectionHandleView. |
| -views::Widget* CreateSelectionHandleWidget() { |
| +views::Widget* CreateTouchSelectionPopupWidget() { |
| views::Widget* widget = new views::Widget; |
| views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); |
| params.can_activate = false; |
| @@ -68,7 +87,7 @@ class TouchSelectionControllerImpl::SelectionHandleView : public View { |
| public: |
| SelectionHandleView(TouchSelectionControllerImpl* controller) |
| : controller_(controller) { |
| - widget_.reset(CreateSelectionHandleWidget()); |
| + widget_.reset(CreateTouchSelectionPopupWidget()); |
| widget_->SetContentsView(this); |
| widget_->SetAlwaysOnTop(true); |
| @@ -141,11 +160,101 @@ class TouchSelectionControllerImpl::SelectionHandleView : public View { |
| DISALLOW_COPY_AND_ASSIGN(SelectionHandleView); |
| }; |
| +// A View that displays the touch context menu. |
| +class TouchSelectionControllerImpl::TouchContextMenuView |
| + : public ButtonListener, |
| + public View { |
| + public: |
| + TouchContextMenuView(TouchSelectionControllerImpl* controller) |
| + : controller_(controller) { |
| + widget_.reset(CreateTouchSelectionPopupWidget()); |
| + widget_->SetContentsView(this); |
| + widget_->SetAlwaysOnTop(true); |
| + |
| + // We are owned by the TouchSelectionController. |
| + set_parent_owned(false); |
| + SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, kContextMenuPadding, |
| + kContextMenuPadding, kContextMenuPadding)); |
| + } |
| + |
| + virtual ~TouchContextMenuView() { |
| + widget_->Close(); |
| + } |
| + |
| + virtual void SetVisible(bool visible) OVERRIDE { |
| + // We simply show/hide the container widget. |
| + if (visible != widget_->IsVisible()) { |
| + if (visible) |
| + widget_->Show(); |
| + else |
| + widget_->Hide(); |
| + } |
| + View::SetVisible(visible); |
| + } |
| + |
| + void SetScreenPosition(const gfx::Point& position) { |
| + RefreshButtonsAndLayout(); |
| + gfx::Rect widget_bounds(position.x() - width() / 2, position.y() - height(), |
| + width(), height()); |
| + gfx::Rect monitor_bounds = |
| + gfx::Screen::GetMonitorAreaNearestPoint(position); |
| + widget_->SetBounds(widget_bounds.AdjustToFit(monitor_bounds)); |
|
sky
2011/08/26 15:45:33
Might you end up showing the context menu over the
varunjain
2011/08/26 16:31:15
This is possible in theory. But in practice, selec
|
| + } |
| + |
| + gfx::Point GetScreenPosition() { |
| + return widget_->GetClientAreaScreenBounds().origin(); |
| + } |
| + |
| + // ButtonListener |
| + virtual void ButtonPressed(Button* sender, const views::Event& event) { |
| + controller_->ExecuteCommand(sender->tag()); |
| + } |
| + |
| + private: |
| + // Queries the client view for what elements to show in the menu and sizes |
| + // the menu appropriately. |
| + void RefreshButtonsAndLayout() { |
|
sky
2011/08/26 15:45:33
nit: this doesn't layout, so maybe just RefreshBut
varunjain
2011/08/26 16:31:15
Done.
|
| + RemoveAllChildViews(true); |
| + int total_width = 0; |
| + int height = 0; |
| + for (size_t i = 0; i < arraysize(kContextMenuCommands); i++) { |
| + int command_id = kContextMenuCommands[i]; |
| + if (controller_->IsCommandIdEnabled(command_id)) { |
| + TextButton* button = new TextButton(this, |
| + UTF16ToWide(l10n_util::GetStringUTF16(command_id))); |
| + button->set_focusable(true); |
|
sky
2011/08/26 15:45:33
Is there a reason why we aren't using a standard m
|
| + button->set_request_focus_on_press(false); |
| + button->set_prefix_type(TextButton::PREFIX_HIDE); |
| + button->SetEnabledColor(SK_ColorWHITE); |
| + button->SetHoverColor(SK_ColorWHITE); |
| + button->set_background( |
| + Background::CreateSolidBackground(SK_ColorBLACK)); |
| + button->set_alignment(TextButton::ALIGN_CENTER); |
| + button->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont( |
| + ui::ResourceBundle::LargeFont)); |
| + button->set_tag(command_id); |
| + AddChildView(button); |
| + gfx::Size button_size = button->GetPreferredSize(); |
| + total_width += button_size.width() + kContextMenuPadding; |
| + if (height < button_size.height()) |
| + height = button_size.height(); |
| + } |
| + } |
| + SetBounds(0, 0, total_width, height); |
|
sky
2011/08/26 15:45:33
I don't think this is necessary. What matters is t
varunjain
2011/08/26 16:31:15
Done.
varunjain
2011/08/26 16:41:39
This is not really working. What I am seeing is th
|
| + } |
| + |
| + scoped_ptr<Widget> widget_; |
| + TouchSelectionControllerImpl* controller_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TouchContextMenuView); |
| +}; |
| + |
| TouchSelectionControllerImpl::TouchSelectionControllerImpl( |
| TouchSelectionClientView* client_view) |
| : client_view_(client_view), |
| selection_handle_1_(new SelectionHandleView(this)), |
| selection_handle_2_(new SelectionHandleView(this)), |
| + context_menu_(new TouchContextMenuView(this)), |
| dragging_handle_(NULL) { |
| } |
| @@ -166,6 +275,8 @@ void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1, |
| // the start. |
| dragging_handle_->SetScreenPosition(screen_pos_2); |
| } else { |
| + UpdateContextMenu(p1, p2); |
| + |
| // Check if there is any selection at all. |
| if (screen_pos_1 == screen_pos_2) { |
| selection_handle_1_->SetVisible(false); |
| @@ -192,10 +303,18 @@ void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1, |
| void TouchSelectionControllerImpl::ClientViewLostFocus() { |
| selection_handle_1_->SetVisible(false); |
| selection_handle_2_->SetVisible(false); |
| + HideContextMenu(); |
| } |
| void TouchSelectionControllerImpl::SelectionHandleDragged( |
| const gfx::Point& drag_pos) { |
| + // We do not want to show the context menu while dragging. |
| + HideContextMenu(); |
| + context_menu_timer_.Start( |
| + base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs), |
| + this, |
| + &TouchSelectionControllerImpl::ContextMenuTimerFired); |
| + |
| if (client_view_->GetWidget()) { |
| DCHECK(dragging_handle_); |
| // Find the stationary selection handle. |
| @@ -225,6 +344,64 @@ void TouchSelectionControllerImpl::ConvertPointToClientView( |
| View::ConvertPointFromWidget(client_view_, point); |
| } |
| +bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { |
| + return client_view_->IsCommandIdEnabled(command_id); |
| +} |
| + |
| +void TouchSelectionControllerImpl::ExecuteCommand(int command_id) { |
| + HideContextMenu(); |
| + client_view_->ExecuteCommand(command_id); |
| +} |
| + |
| +void TouchSelectionControllerImpl::ContextMenuTimerFired() { |
| + context_menu_->SetScreenPosition(context_menu_screen_pos_); |
| + context_menu_->SetVisible(true); |
| +} |
| + |
| +void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1, |
| + const gfx::Point& p2) { |
| + |
| + // No selection means no context menu. |
| + if (p1 == p2) { |
| + HideContextMenu(); |
| + return; |
| + } |
| + |
| + // if selection is completely inside the view, we display the context menu |
| + // in the middle of the end points on the top. Else, we show the menu on the |
| + // top border of the view in the center. |
| + if (client_view_->bounds().Contains(p1) && |
| + client_view_->bounds().Contains(p2)) { |
| + context_menu_screen_pos_.set_x((p1.x() + p2.x()) / 2); |
|
sky
2011/08/26 15:45:33
Why does this need to be cached here instead of ca
varunjain
2011/08/26 16:31:15
It doesnt. Changed.
|
| + context_menu_screen_pos_.set_y( |
| + std::min(p1.y(), p2.y()) - kContextMenuVerticalOffset); |
| + } else { |
| + context_menu_screen_pos_.set_x( |
| + client_view_->x() + client_view_->width() / 2); |
| + context_menu_screen_pos_.set_y(client_view_->y()); |
| + } |
| + |
| + View::ConvertPointToScreen(client_view_, &context_menu_screen_pos_); |
| + |
| + // If menu is already visible we hide it to be display after the timer fires. |
| + if (context_menu_->IsVisible()) |
| + context_menu_->SetVisible(false); |
| + if (context_menu_timer_.IsRunning()) { |
| + context_menu_timer_.Reset(); |
| + } else { |
| + context_menu_timer_.Start( |
| + base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs), |
| + this, |
| + &TouchSelectionControllerImpl::ContextMenuTimerFired); |
| + } |
| +} |
| + |
| +void TouchSelectionControllerImpl::HideContextMenu() { |
| + context_menu_->SetVisible(false); |
| + if (context_menu_timer_.IsRunning()) |
|
sky
2011/08/26 15:45:33
No need to check if running.
varunjain
2011/08/26 16:31:15
Done.
|
| + context_menu_timer_.Stop(); |
| +} |
| + |
| gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { |
| return selection_handle_1_->GetScreenPosition(); |
| } |