Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Unified Diff: views/touchui/touch_selection_controller_impl.cc

Issue 7740024: Implement touch selection menu. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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();
}

Powered by Google App Engine
This is Rietveld 408576698