diff options
author | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-26 20:11:53 +0000 |
---|---|---|
committer | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-26 20:11:53 +0000 |
commit | 4723a449a5e77386344de98ce09667c5acab6aa7 (patch) | |
tree | 7e8fc621cb231d424eb94c67b7fa39c9d23175c3 /views/touchui | |
parent | de09c7d0478d7a82785aa5080a9499ee8b36b1d8 (diff) | |
download | chromium_src-4723a449a5e77386344de98ce09667c5acab6aa7.zip chromium_src-4723a449a5e77386344de98ce09667c5acab6aa7.tar.gz chromium_src-4723a449a5e77386344de98ce09667c5acab6aa7.tar.bz2 |
Implement touch selection menu.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/7740024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98473 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/touchui')
-rw-r--r-- | views/touchui/touch_selection_controller_impl.cc | 183 | ||||
-rw-r--r-- | views/touchui/touch_selection_controller_impl.h | 23 |
2 files changed, 202 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(); } diff --git a/views/touchui/touch_selection_controller_impl.h b/views/touchui/touch_selection_controller_impl.h index 6cb7816..480485d 100644 --- a/views/touchui/touch_selection_controller_impl.h +++ b/views/touchui/touch_selection_controller_impl.h @@ -6,6 +6,7 @@ #define VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ #pragma once +#include "base/timer.h" #include "ui/gfx/point.h" #include "views/touchui/touch_selection_controller.h" #include "views/view.h" @@ -32,6 +33,7 @@ class VIEWS_EXPORT TouchSelectionControllerImpl private: friend class TouchSelectionControllerImplTest; class SelectionHandleView; + class TouchContextMenuView; // Callback to inform the client view that the selection handle has been // dragged, hence selection may need to be updated. @@ -41,6 +43,21 @@ class VIEWS_EXPORT TouchSelectionControllerImpl // system to that of the client view. void ConvertPointToClientView(SelectionHandleView* source, gfx::Point* point); + // Checks if the client view supports a context menu command. + bool IsCommandIdEnabled(int command_id) const; + + // Sends a context menu command to the client view. + void ExecuteCommand(int command_id); + + // Time to show context menu. + void ContextMenuTimerFired(); + + // Convenience method to update the position/visibility of the context menu. + void UpdateContextMenu(const gfx::Point& p1, const gfx::Point& p2); + + // Convenience method for hiding context menu. + void HideContextMenu(); + // Convenience methods for testing. gfx::Point GetSelectionHandle1Position(); gfx::Point GetSelectionHandle2Position(); @@ -50,6 +67,12 @@ class VIEWS_EXPORT TouchSelectionControllerImpl TouchSelectionClientView* client_view_; scoped_ptr<SelectionHandleView> selection_handle_1_; scoped_ptr<SelectionHandleView> selection_handle_2_; + scoped_ptr<TouchContextMenuView> context_menu_; + + // Timer to trigger |context_menu| (|context_menu| is not shown if the + // selection handles are being updated. It appears only when the handles are + // stationary for a certain amount of time). + base::OneShotTimer<TouchSelectionControllerImpl> context_menu_timer_; // Pointer to the SelectionHandleView being dragged during a drag session. SelectionHandleView* dragging_handle_; |