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..0de16ce8dec7d1187b42f116d9791469943a63dc 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(); |
} |