diff options
Diffstat (limited to 'views/touchui/touch_selection_controller_impl.cc')
-rw-r--r-- | views/touchui/touch_selection_controller_impl.cc | 183 |
1 files changed, 179 insertions, 4 deletions
diff --git a/views/touchui/touch_selection_controller_impl.cc b/views/touchui/touch_selection_controller_impl.cc index 10bf572..0de16ce 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,103 @@ 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) { + RefreshButtonsAndSetWidgetPosition(position); + } + + 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 RefreshButtonsAndSetWidgetPosition(const gfx::Point& position) { + 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); + 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(); + } + } + gfx::Rect widget_bounds(position.x() - total_width / 2, + position.y() - height, + total_width, + height); + gfx::Rect monitor_bounds = + gfx::Screen::GetMonitorAreaNearestPoint(position); + widget_->SetBounds(widget_bounds.AdjustToFit(monitor_bounds)); + Layout(); + } + + 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 +277,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 +305,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 +346,60 @@ 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() { + // Get selection end points in client_view's space. + gfx::Point p1(kSelectionHandleRadius, 0); + ConvertPointToClientView(selection_handle_1_.get(), &p1); + gfx::Point p2(kSelectionHandleRadius, 0); + ConvertPointToClientView(selection_handle_2_.get(), &p2); + + // 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. + gfx::Point menu_pos; + if (client_view_->bounds().Contains(p1) && + client_view_->bounds().Contains(p2)) { + menu_pos.set_x((p1.x() + p2.x()) / 2); + menu_pos.set_y(std::min(p1.y(), p2.y()) - kContextMenuVerticalOffset); + } else { + menu_pos.set_x(client_view_->x() + client_view_->width() / 2); + menu_pos.set_y(client_view_->y()); + } + + View::ConvertPointToScreen(client_view_, &menu_pos); + + context_menu_->SetScreenPosition(menu_pos); + context_menu_->SetVisible(true); +} + +void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1, + const gfx::Point& p2) { + // Hide context menu to be shown when the timer fires. + HideContextMenu(); + + // If there is selection, we restart the context menu timer. + if (p1 != p2) { + context_menu_timer_.Start( + base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs), + this, + &TouchSelectionControllerImpl::ContextMenuTimerFired); + } +} + +void TouchSelectionControllerImpl::HideContextMenu() { + context_menu_->SetVisible(false); + context_menu_timer_.Stop(); +} + gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { return selection_handle_1_->GetScreenPosition(); } |