summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormohsen <mohsen@chromium.org>2015-05-28 21:12:40 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-29 04:13:09 +0000
commit1d0063dd4fcc1d4ec66af570c8e41d4416649159 (patch)
tree86d0dcff66f0a7ffefda3b23456992936b9330f8
parent7a6acf6c7d45e24f07781c69249d90967013bbe4 (diff)
downloadchromium_src-1d0063dd4fcc1d4ec66af570c8e41d4416649159.zip
chromium_src-1d0063dd4fcc1d4ec66af570c8e41d4416649159.tar.gz
chromium_src-1d0063dd4fcc1d4ec66af570c8e41d4416649159.tar.bz2
Refactor touch selection quick menu
To use the touch selection quick menu in the upcoming unified touch selection, a new interface is added to ui/touch_selection with an implementation for Views. The implementation mostly copied code from the old implementation. COLLABORATOR=mfomitchev BUG=399721 Review URL: https://codereview.chromium.org/1019353003 Cr-Commit-Position: refs/heads/master@{#331924}
-rw-r--r--chrome/chrome_tests.gypi7
-rw-r--r--chrome/test/BUILD.gn3
-rw-r--r--ui/touch_selection/BUILD.gn15
-rw-r--r--ui/touch_selection/touch_selection_menu_runner.cc31
-rw-r--r--ui/touch_selection/touch_selection_menu_runner.h65
-rw-r--r--ui/touch_selection/ui_touch_selection.gyp4
-rw-r--r--ui/views/BUILD.gn2
-rw-r--r--ui/views/DEPS1
-rw-r--r--ui/views/touchui/touch_editing_menu.cc157
-rw-r--r--ui/views/touchui/touch_editing_menu.h83
-rw-r--r--ui/views/touchui/touch_selection_controller_impl.cc99
-rw-r--r--ui/views/touchui/touch_selection_controller_impl.h38
-rw-r--r--ui/views/touchui/touch_selection_controller_impl_unittest.cc70
-rw-r--r--ui/views/touchui/touch_selection_menu_runner_views.cc249
-rw-r--r--ui/views/touchui/touch_selection_menu_runner_views.h45
-rw-r--r--ui/views/touchui/touch_selection_menu_runner_views_unittest.cc110
-rw-r--r--ui/views/views.gyp7
-rw-r--r--ui/views/views_delegate.cc8
-rw-r--r--ui/views/views_delegate.h9
-rw-r--r--ui/views/widget/widget_interactive_uitest.cc32
20 files changed, 624 insertions, 411 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 0a1156b..d02c0d6 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1624,6 +1624,13 @@
'../ui/views/views.gyp:views',
'../ui/views/views.gyp:views_test_support',
],
+ 'conditions': [
+ ['use_aura==1', {
+ 'dependencies': [
+ '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection',
+ ],
+ }],
+ ],
}],
['use_aura==0 or chromeos==1', {
'sources!': [
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1dca8e1..1d5314f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -302,6 +302,9 @@ if (!is_android) {
"../../ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc",
]
}
+ if (use_aura) {
+ deps += [ "//ui/touch_selection" ]
+ }
}
if (is_linux && !is_chromeos) {
diff --git a/ui/touch_selection/BUILD.gn b/ui/touch_selection/BUILD.gn
index e79c563..d680429 100644
--- a/ui/touch_selection/BUILD.gn
+++ b/ui/touch_selection/BUILD.gn
@@ -16,8 +16,6 @@ component("touch_selection") {
"selection_event_type.h",
"touch_handle.cc",
"touch_handle.h",
- "touch_handle_drawable_aura.cc",
- "touch_handle_drawable_aura.h",
"touch_handle_orientation.h",
"touch_selection_controller.cc",
"touch_selection_controller.h",
@@ -27,20 +25,15 @@ component("touch_selection") {
defines = [ "UI_TOUCH_SELECTION_IMPLEMENTATION" ]
deps = [
- "//skia:skia",
"//base:base",
- "//ui/aura:aura",
- "//ui/aura_extra:aura_extra",
"//ui/base:base",
- "//ui/compositor:compositor",
"//ui/events:events",
"//ui/events:gesture_detection",
- "//ui/gfx:gfx",
"//ui/gfx/geometry:geometry",
]
- if (!use_aura) {
- deps -= [
+ if (use_aura) {
+ deps += [
"//skia:skia",
"//ui/aura:aura",
"//ui/aura_extra:aura_extra",
@@ -48,9 +41,11 @@ component("touch_selection") {
"//ui/gfx:gfx",
]
- sources -= [
+ sources += [
"touch_handle_drawable_aura.cc",
"touch_handle_drawable_aura.h",
+ "touch_selection_menu_runner.cc",
+ "touch_selection_menu_runner.h",
]
}
}
diff --git a/ui/touch_selection/touch_selection_menu_runner.cc b/ui/touch_selection/touch_selection_menu_runner.cc
new file mode 100644
index 0000000..860a24b
--- /dev/null
+++ b/ui/touch_selection/touch_selection_menu_runner.cc
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/touch_selection/touch_selection_menu_runner.h"
+
+namespace ui {
+namespace {
+
+TouchSelectionMenuRunner* g_touch_selection_menu_runner = nullptr;
+
+} // namespace
+
+TouchSelectionMenuRunner::~TouchSelectionMenuRunner() {
+ g_touch_selection_menu_runner = nullptr;
+}
+
+TouchSelectionMenuRunner* TouchSelectionMenuRunner::GetInstance() {
+ return g_touch_selection_menu_runner;
+}
+
+TouchSelectionMenuRunner::TouchSelectionMenuRunner() {
+ // TODO(mohsen): Ideally we should DCHECK that |g_touch_selection_menu_runner|
+ // is not set here, in order to make sure we don't create multiple menu
+ // runners accidentally. Currently, this is not possible because we can have
+ // multiple ViewsDelegate's at the same time which should not happen. See
+ // crbug.com/492991.
+ g_touch_selection_menu_runner = this;
+}
+
+} // namespace ui
diff --git a/ui/touch_selection/touch_selection_menu_runner.h b/ui/touch_selection/touch_selection_menu_runner.h
new file mode 100644
index 0000000..8db3213
--- /dev/null
+++ b/ui/touch_selection/touch_selection_menu_runner.h
@@ -0,0 +1,65 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_TOUCH_SELECTION_TOUCH_SELECTION_MENU_RUNNER_H_
+#define UI_TOUCH_SELECTION_TOUCH_SELECTION_MENU_RUNNER_H_
+
+#include "base/macros.h"
+#include "ui/touch_selection/ui_touch_selection_export.h"
+
+namespace aura {
+class Window;
+}
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace ui {
+
+// Client interface for TouchSelectionMenuRunner.
+class UI_TOUCH_SELECTION_EXPORT TouchSelectionMenuClient {
+ public:
+ virtual ~TouchSelectionMenuClient() {}
+
+ virtual bool IsCommandIdEnabled(int command_id) const = 0;
+ virtual void ExecuteCommand(int command_id, int event_flags) = 0;
+
+ // Called when the quick menu needs to run a context menu. Depending on the
+ // implementation, this may run the context menu synchronously, or request the
+ // menu to show up in which case the menu will run asynchronously at a later
+ // time.
+ virtual void RunContextMenu() = 0;
+};
+
+// An interface for the singleton object responsible for running touch selection
+// quick menu.
+class UI_TOUCH_SELECTION_EXPORT TouchSelectionMenuRunner {
+ public:
+ virtual ~TouchSelectionMenuRunner();
+
+ static TouchSelectionMenuRunner* GetInstance();
+
+ // Creates and displays the quick menu, if there is any command available.
+ // |anchor_rect| is in screen coordinates.
+ virtual void OpenMenu(TouchSelectionMenuClient* client,
+ const gfx::Rect& anchor_rect,
+ const gfx::Size& handle_image_size,
+ aura::Window* context) = 0;
+
+ virtual void CloseMenu() = 0;
+
+ virtual bool IsRunning() const = 0;
+
+ protected:
+ TouchSelectionMenuRunner();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TouchSelectionMenuRunner);
+};
+
+} // namespace ui
+
+#endif // UI_TOUCH_SELECTION_TOUCH_SELECTION_MENU_RUNNER_H_
diff --git a/ui/touch_selection/ui_touch_selection.gyp b/ui/touch_selection/ui_touch_selection.gyp
index 11cca1f..c615ff5 100644
--- a/ui/touch_selection/ui_touch_selection.gyp
+++ b/ui/touch_selection/ui_touch_selection.gyp
@@ -34,6 +34,8 @@
'touch_handle_orientation.h',
'touch_selection_controller.cc',
'touch_selection_controller.h',
+ 'touch_selection_menu_runner.cc',
+ 'touch_selection_menu_runner.h',
'ui_touch_selection_export.h',
],
'include_dirs': [
@@ -51,6 +53,8 @@
'sources!': [
'touch_handle_drawable_aura.cc',
'touch_handle_drawable_aura.h',
+ 'touch_selection_menu_runner.cc',
+ 'touch_selection_menu_runner.h',
],
}],
],
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 76e70cd..2769a11 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -99,6 +99,7 @@ component("views") {
sources += gypi_values.views_aura_sources
deps += [
"//ui/aura",
+ "//ui/touch_selection",
"//ui/wm",
]
if (!is_chromeos) {
@@ -235,6 +236,7 @@ test("views_unittests") {
deps += [
"//ui/aura",
"//ui/aura:test_support",
+ "//ui/touch_selection",
"//ui/wm",
]
if (!is_chromeos) {
diff --git a/ui/views/DEPS b/ui/views/DEPS
index 6f7d8abb6..c1d3570 100644
--- a/ui/views/DEPS
+++ b/ui/views/DEPS
@@ -14,6 +14,7 @@ include_rules = [
"+ui/ozone/public",
"+ui/resources/grit/ui_resources.h",
"+ui/strings/grit/ui_strings.h",
+ "+ui/touch_selection",
"+ui/wm/core",
"+ui/wm/public",
diff --git a/ui/views/touchui/touch_editing_menu.cc b/ui/views/touchui/touch_editing_menu.cc
deleted file mode 100644
index 7b2c5b6..0000000
--- a/ui/views/touchui/touch_editing_menu.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/touchui/touch_editing_menu.h"
-
-#include "base/strings/utf_string_conversions.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/font_list.h"
-#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/text_utils.h"
-#include "ui/strings/grit/ui_strings.h"
-#include "ui/views/bubble/bubble_border.h"
-#include "ui/views/bubble/bubble_frame_view.h"
-#include "ui/views/controls/button/custom_button.h"
-#include "ui/views/controls/button/label_button.h"
-#include "ui/views/layout/box_layout.h"
-#include "ui/views/widget/widget.h"
-
-namespace {
-
-const int kMenuCommands[] = {IDS_APP_CUT,
- IDS_APP_COPY,
- IDS_APP_PASTE};
-const int kSpacingBetweenButtons = 2;
-const int kButtonSeparatorColor = SkColorSetARGB(13, 0, 0, 0);
-const int kMenuButtonMinHeight = 38;
-const int kMenuButtonMinWidth = 63;
-const int kMenuMargin = 1;
-
-const char* kEllipsesButtonText = "...";
-const int kEllipsesButtonTag = -1;
-} // namespace
-
-namespace views {
-
-TouchEditingMenuView::TouchEditingMenuView(
- TouchEditingMenuController* controller,
- const gfx::Rect& anchor_rect,
- const gfx::Size& handle_image_size,
- gfx::NativeView context)
- : BubbleDelegateView(NULL, views::BubbleBorder::BOTTOM_CENTER),
- controller_(controller) {
- set_shadow(views::BubbleBorder::SMALL_SHADOW);
- set_parent_window(context);
- set_margins(gfx::Insets(kMenuMargin, kMenuMargin, kMenuMargin, kMenuMargin));
- set_can_activate(false);
- set_adjust_if_offscreen(true);
-
- SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, 0, 0,
- kSpacingBetweenButtons));
- CreateButtons();
-
- // After buttons are created, check if there is enough room between handles to
- // show the menu and adjust anchor rect properly if needed, just in case the
- // menu is needed to be shown under the selection.
- gfx::Rect adjusted_anchor_rect(anchor_rect);
- int menu_width = GetPreferredSize().width();
- if (menu_width > anchor_rect.width() - handle_image_size.width())
- adjusted_anchor_rect.Inset(0, 0, 0, -handle_image_size.height());
- SetAnchorRect(adjusted_anchor_rect);
-
- views::BubbleDelegateView::CreateBubble(this);
- GetWidget()->Show();
-}
-
-TouchEditingMenuView::~TouchEditingMenuView() {
-}
-
-// static
-TouchEditingMenuView* TouchEditingMenuView::Create(
- TouchEditingMenuController* controller,
- const gfx::Rect& anchor_rect,
- const gfx::Size& handle_image_size,
- gfx::NativeView context) {
- if (controller) {
- for (size_t i = 0; i < arraysize(kMenuCommands); i++) {
- if (controller->IsCommandIdEnabled(kMenuCommands[i])) {
- return new TouchEditingMenuView(controller, anchor_rect,
- handle_image_size, context);
- }
- }
- }
- return NULL;
-}
-
-void TouchEditingMenuView::Close() {
- if (GetWidget()) {
- controller_ = NULL;
- GetWidget()->Close();
- }
-}
-
-void TouchEditingMenuView::WindowClosing() {
- views::BubbleDelegateView::WindowClosing();
- if (controller_)
- controller_->OnMenuClosed(this);
-}
-
-void TouchEditingMenuView::ButtonPressed(Button* sender,
- const ui::Event& event) {
- if (controller_) {
- if (sender->tag() != kEllipsesButtonTag)
- controller_->ExecuteCommand(sender->tag(), event.flags());
- else
- controller_->OpenContextMenu();
- }
-}
-
-void TouchEditingMenuView::OnPaint(gfx::Canvas* canvas) {
- BubbleDelegateView::OnPaint(canvas);
-
- // Draw separator bars.
- for (int i = 0; i < child_count() - 1; ++i) {
- View* child = child_at(i);
- int x = child->bounds().right() + kSpacingBetweenButtons / 2;
- canvas->FillRect(gfx::Rect(x, 0, 1, child->height()),
- kButtonSeparatorColor);
- }
-}
-
-void TouchEditingMenuView::CreateButtons() {
- RemoveAllChildViews(true);
- for (size_t i = 0; i < arraysize(kMenuCommands); i++) {
- int command_id = kMenuCommands[i];
- if (controller_ && controller_->IsCommandIdEnabled(command_id)) {
- Button* button = CreateButton(l10n_util::GetStringUTF16(command_id),
- command_id);
- AddChildView(button);
- }
- }
-
- // Finally, add ellipses button.
- AddChildView(CreateButton(
- base::UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag));
- Layout();
-}
-
-Button* TouchEditingMenuView::CreateButton(const base::string16& title,
- int tag) {
- base::string16 label = gfx::RemoveAcceleratorChar(title, '&', NULL, NULL);
- LabelButton* button = new LabelButton(this, label);
- button->SetMinSize(gfx::Size(kMenuButtonMinWidth, kMenuButtonMinHeight));
- button->SetFocusable(true);
- button->set_request_focus_on_press(false);
- const gfx::FontList& font_list =
- ui::ResourceBundle::GetSharedInstance().GetFontList(
- ui::ResourceBundle::SmallFont);
- button->SetFontList(font_list);
- button->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- button->set_tag(tag);
- return button;
-}
-
-} // namespace views
diff --git a/ui/views/touchui/touch_editing_menu.h b/ui/views/touchui/touch_editing_menu.h
deleted file mode 100644
index 6aa1343..0000000
--- a/ui/views/touchui/touch_editing_menu.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_TOUCHUI_TOUCH_EDITING_MENU_H_
-#define UI_VIEWS_TOUCHUI_TOUCH_EDITING_MENU_H_
-
-#include "ui/gfx/geometry/point.h"
-#include "ui/views/bubble/bubble_delegate.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/views_export.h"
-
-namespace gfx {
-class Canvas;
-}
-
-namespace views {
-class TouchEditingMenuView;
-class Widget;
-
-class VIEWS_EXPORT TouchEditingMenuController {
- public:
- // Checks if the specified menu command is supported.
- virtual bool IsCommandIdEnabled(int command_id) const = 0;
-
- // Send a context menu command to the controller.
- virtual void ExecuteCommand(int command_id, int event_flags) = 0;
-
- // Tell the controller that user has selected the context menu button.
- virtual void OpenContextMenu() = 0;
-
- // Called when the menu is closed.
- virtual void OnMenuClosed(TouchEditingMenuView* menu) = 0;
-
- protected:
- virtual ~TouchEditingMenuController() {}
-};
-
-// A View that displays the touch context menu.
-class VIEWS_EXPORT TouchEditingMenuView : public BubbleDelegateView,
- public ButtonListener {
- public:
- ~TouchEditingMenuView() override;
-
- // If there are no actions available for the menu, returns NULL. Otherwise,
- // returns a new instance of TouchEditingMenuView.
- static TouchEditingMenuView* Create(TouchEditingMenuController* controller,
- const gfx::Rect& anchor_rect,
- const gfx::Size& handle_image_size,
- gfx::NativeView context);
-
- void Close();
-
- private:
- TouchEditingMenuView(TouchEditingMenuController* controller,
- const gfx::Rect& anchor_rect,
- const gfx::Size& handle_image_size,
- gfx::NativeView context);
-
- // views::WidgetDelegate overrides:
- void WindowClosing() override;
-
- // Overridden from ButtonListener.
- void ButtonPressed(Button* sender, const ui::Event& event) override;
-
- // Overridden from BubbleDelegateView.
- void OnPaint(gfx::Canvas* canvas) override;
-
- // Queries the |controller_| for what elements to show in the menu and sizes
- // the menu appropriately.
- void CreateButtons();
-
- // Helper method to create a single button.
- Button* CreateButton(const base::string16& title, int tag);
-
- TouchEditingMenuController* controller_;
-
- DISALLOW_COPY_AND_ASSIGN(TouchEditingMenuView);
-};
-
-} // namespace views
-
-#endif // UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_
diff --git a/ui/views/touchui/touch_selection_controller_impl.cc b/ui/views/touchui/touch_selection_controller_impl.cc
index baf8118..4078728 100644
--- a/ui/views/touchui/touch_selection_controller_impl.cc
+++ b/ui/views/touchui/touch_selection_controller_impl.cc
@@ -19,6 +19,7 @@
#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/masked_window_targeter.h"
@@ -60,7 +61,7 @@ const int kSelectionHandleVerticalDragOffset = 5;
const int kSelectionHandleHorizPadding = 10;
const int kSelectionHandleVertPadding = 20;
-const int kContextMenuTimoutMs = 200;
+const int kQuickMenuTimoutMs = 200;
const int kSelectionHandleQuickFadeDurationMs = 50;
@@ -415,7 +416,6 @@ TouchSelectionControllerImpl::TouchSelectionControllerImpl(
cursor_handle_(new EditingHandleView(this,
client_view->GetNativeView(),
true)),
- context_menu_(nullptr),
command_executed_(false),
dragging_handle_(nullptr) {
selection_start_time_ = base::TimeTicks::Now();
@@ -430,7 +430,7 @@ TouchSelectionControllerImpl::TouchSelectionControllerImpl(
TouchSelectionControllerImpl::~TouchSelectionControllerImpl() {
UMA_HISTOGRAM_BOOLEAN("Event.TouchSelection.EndedWithAction",
command_executed_);
- HideContextMenu();
+ HideQuickMenu();
aura::Env::GetInstance()->RemovePreTargetHandler(this);
if (client_widget_)
client_widget_->RemoveObserver(this);
@@ -468,7 +468,7 @@ void TouchSelectionControllerImpl::SelectionChanged() {
selection_bound_2_clipped_ = screen_bound_focus_clipped;
if (client_view_->DrawsHandles()) {
- UpdateContextMenu();
+ UpdateQuickMenu();
return;
}
@@ -504,7 +504,7 @@ void TouchSelectionControllerImpl::SelectionChanged() {
SetHandleBound(non_dragging_handle, anchor, screen_bound_anchor_clipped);
}
} else {
- UpdateContextMenu();
+ UpdateQuickMenu();
// Check if there is any selection at all.
if (screen_bound_anchor.edge_top() == screen_bound_focus.edge_top() &&
@@ -537,9 +537,9 @@ void TouchSelectionControllerImpl::SetDraggingHandle(
EditingHandleView* handle) {
dragging_handle_ = handle;
if (dragging_handle_)
- HideContextMenu();
+ HideQuickMenu();
else
- StartContextMenuTimer();
+ StartQuickMenuTimer();
}
void TouchSelectionControllerImpl::SelectionHandleDragged(
@@ -608,23 +608,16 @@ void TouchSelectionControllerImpl::ExecuteCommand(int command_id,
base::TimeDelta::FromMilliseconds(500),
base::TimeDelta::FromSeconds(60),
60);
- HideContextMenu();
client_view_->ExecuteCommand(command_id, event_flags);
}
-void TouchSelectionControllerImpl::OpenContextMenu() {
+void TouchSelectionControllerImpl::RunContextMenu() {
// Context menu should appear centered on top of the selected region.
- const gfx::Rect rect = context_menu_->GetAnchorRect();
+ const gfx::Rect rect = GetQuickMenuAnchorRect();
const gfx::Point anchor(rect.CenterPoint().x(), rect.y());
- HideContextMenu();
client_view_->OpenContextMenu(anchor);
}
-void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
- if (menu == context_menu_)
- context_menu_ = nullptr;
-}
-
void TouchSelectionControllerImpl::OnAncestorWindowTransformed(
aura::Window* window,
aura::Window* ancestor) {
@@ -659,19 +652,51 @@ void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
client_view_->DestroyTouchSelection();
}
-void TouchSelectionControllerImpl::ContextMenuTimerFired() {
+void TouchSelectionControllerImpl::QuickMenuTimerFired() {
+ gfx::Rect menu_anchor = GetQuickMenuAnchorRect();
+ if (menu_anchor == gfx::Rect())
+ return;
+
+ ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu(
+ this, menu_anchor, GetMaxHandleImageSize(),
+ client_view_->GetNativeView());
+}
+
+void TouchSelectionControllerImpl::StartQuickMenuTimer() {
+ if (quick_menu_timer_.IsRunning())
+ return;
+ quick_menu_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kQuickMenuTimoutMs),
+ this,
+ &TouchSelectionControllerImpl::QuickMenuTimerFired);
+}
+
+void TouchSelectionControllerImpl::UpdateQuickMenu() {
+ // Hide quick menu to be shown when the timer fires.
+ HideQuickMenu();
+ StartQuickMenuTimer();
+}
+
+void TouchSelectionControllerImpl::HideQuickMenu() {
+ if (ui::TouchSelectionMenuRunner::GetInstance()->IsRunning())
+ ui::TouchSelectionMenuRunner::GetInstance()->CloseMenu();
+ quick_menu_timer_.Stop();
+}
+
+gfx::Rect TouchSelectionControllerImpl::GetQuickMenuAnchorRect() const {
// Get selection end points in client_view's space.
ui::SelectionBound b1_in_screen = selection_bound_1_clipped_;
- ui::SelectionBound b2_in_screen =
- cursor_handle_->IsWidgetVisible() ? b1_in_screen
+ ui::SelectionBound b2_in_screen = cursor_handle_->IsWidgetVisible()
+ ? b1_in_screen
: selection_bound_2_clipped_;
// Convert from screen to client.
ui::SelectionBound b1 = ConvertFromScreen(client_view_, b1_in_screen);
ui::SelectionBound b2 = ConvertFromScreen(client_view_, b2_in_screen);
- // 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 it above the
- // visible handle. If no handle is visible, we do not show the menu.
+ // if selection is completely inside the view, we display the quick menu in
+ // the middle of the end points on the top. Else, we show it above the visible
+ // handle. If no handle is visible, we do not show the menu.
gfx::Rect menu_anchor;
if (ShouldShowHandleFor(b1) && ShouldShowHandleFor(b2))
menu_anchor = ui::RectBetweenSelectionBounds(b1_in_screen, b2_in_screen);
@@ -680,39 +705,13 @@ void TouchSelectionControllerImpl::ContextMenuTimerFired() {
else if (ShouldShowHandleFor(b2))
menu_anchor = BoundToRect(b2_in_screen);
else
- return;
+ return menu_anchor;
// Enlarge the anchor rect so that the menu is offset from the text at least
// by the same distance the handles are offset from the text.
menu_anchor.Inset(0, -kSelectionHandleVerticalVisualOffset);
- DCHECK(!context_menu_);
- context_menu_ = TouchEditingMenuView::Create(this, menu_anchor,
- GetMaxHandleImageSize(),
- client_view_->GetNativeView());
-}
-
-void TouchSelectionControllerImpl::StartContextMenuTimer() {
- if (context_menu_timer_.IsRunning())
- return;
- context_menu_timer_.Start(
- FROM_HERE,
- base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
- this,
- &TouchSelectionControllerImpl::ContextMenuTimerFired);
-}
-
-void TouchSelectionControllerImpl::UpdateContextMenu() {
- // Hide context menu to be shown when the timer fires.
- HideContextMenu();
- StartContextMenuTimer();
-}
-
-void TouchSelectionControllerImpl::HideContextMenu() {
- if (context_menu_)
- context_menu_->Close();
- context_menu_ = nullptr;
- context_menu_timer_.Stop();
+ return menu_anchor;
}
gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() {
diff --git a/ui/views/touchui/touch_selection_controller_impl.h b/ui/views/touchui/touch_selection_controller_impl.h
index d7bad80..deafbd6 100644
--- a/ui/views/touchui/touch_selection_controller_impl.h
+++ b/ui/views/touchui/touch_selection_controller_impl.h
@@ -10,11 +10,13 @@
#include "ui/base/touch/selection_bound.h"
#include "ui/base/touch/touch_editing_controller.h"
#include "ui/gfx/geometry/point.h"
-#include "ui/views/touchui/touch_editing_menu.h"
+#include "ui/touch_selection/touch_selection_menu_runner.h"
#include "ui/views/view.h"
#include "ui/views/views_export.h"
+#include "ui/views/widget/widget_observer.h"
namespace views {
+class WidgetDelegateView;
namespace test {
class WidgetTestInteractive;
@@ -25,7 +27,7 @@ class WidgetTestInteractive;
// touch interface.
class VIEWS_EXPORT TouchSelectionControllerImpl
: public ui::TouchEditingControllerDeprecated,
- public TouchEditingMenuController,
+ public ui::TouchSelectionMenuClient,
public aura::WindowObserver,
public WidgetObserver,
public ui::EventHandler {
@@ -69,11 +71,10 @@ class VIEWS_EXPORT TouchSelectionControllerImpl
// |bound| should be the clipped version of the selection bound.
bool ShouldShowHandleFor(const ui::SelectionBound& bound) const;
- // Overridden from TouchEditingMenuController.
+ // Overridden from ui::TouchSelectionMenuClient.
bool IsCommandIdEnabled(int command_id) const override;
void ExecuteCommand(int command_id, int event_flags) override;
- void OpenContextMenu() override;
- void OnMenuClosed(TouchEditingMenuView* menu) override;
+ void RunContextMenu() override;
// Overriden from aura::WindowObserver.
void OnAncestorWindowTransformed(aura::Window* source,
@@ -91,16 +92,20 @@ class VIEWS_EXPORT TouchSelectionControllerImpl
void OnMouseEvent(ui::MouseEvent* event) override;
void OnScrollEvent(ui::ScrollEvent* event) override;
- // Time to show context menu.
- void ContextMenuTimerFired();
+ // Time to show quick menu.
+ void QuickMenuTimerFired();
- void StartContextMenuTimer();
+ void StartQuickMenuTimer();
- // Convenience method to update the position/visibility of the context menu.
- void UpdateContextMenu();
+ // Convenience method to update the position/visibility of the quick menu.
+ void UpdateQuickMenu();
- // Convenience method for hiding context menu.
- void HideContextMenu();
+ // Convenience method for hiding quick menu.
+ void HideQuickMenu();
+
+ // Convenience method to calculate anchor rect for quick menu, in screen
+ // coordinates.
+ gfx::Rect GetQuickMenuAnchorRect() const;
// Convenience methods for testing.
gfx::NativeView GetCursorHandleNativeView();
@@ -119,14 +124,13 @@ class VIEWS_EXPORT TouchSelectionControllerImpl
scoped_ptr<EditingHandleView> selection_handle_1_;
scoped_ptr<EditingHandleView> selection_handle_2_;
scoped_ptr<EditingHandleView> cursor_handle_;
- TouchEditingMenuView* context_menu_;
bool command_executed_;
base::TimeTicks selection_start_time_;
- // 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_;
+ // Timer to trigger quick menu (Quick 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> quick_menu_timer_;
// Pointer to the SelectionHandleView being dragged during a drag session.
EditingHandleView* dragging_handle_;
diff --git a/ui/views/touchui/touch_selection_controller_impl_unittest.cc b/ui/views/touchui/touch_selection_controller_impl_unittest.cc
index 3c9a9cc..78de052 100644
--- a/ui/views/touchui/touch_selection_controller_impl_unittest.cc
+++ b/ui/views/touchui/touch_selection_controller_impl_unittest.cc
@@ -23,6 +23,7 @@
#include "ui/views/touchui/touch_selection_controller_impl.h"
#include "ui/views/views_touch_selection_controller_factory.h"
#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
using base::ASCIIToUTF16;
using base::UTF16ToUTF8;
@@ -36,12 +37,6 @@ const int kBarMinHeight = 5;
// touch_selection_controller.
const int kBarBottomAllowance = 3;
-// Should match kMenuButtonWidth in touch_editing_menu.
-const int kMenuButtonWidth = 63;
-
-// Should match size of kMenuCommands array in touch_editing_menu.
-const int kMenuCommandCount = 3;
-
// For selection bounds |b1| and |b2| in a paragraph of text, returns -1 if |b1|
// is physically before |b2|, +1 if |b2| is before |b1|, and 0 if they are at
// the same location.
@@ -762,69 +757,6 @@ TEST_F(TouchSelectionControllerImplTest, HandlesStackAboveParent) {
targeter->FindTargetForEvent(root, &test_event3));
}
-// A simple implementation of TouchEditingMenuController that enables all
-// available commands.
-class TestTouchEditingMenuController : public TouchEditingMenuController {
- public:
- TestTouchEditingMenuController() {}
- ~TestTouchEditingMenuController() override {}
-
- // Overriden from TouchEditingMenuController.
- bool IsCommandIdEnabled(int command_id) const override {
- // Return true, since we want the menu to have all |kMenuCommandCount|
- // available commands.
- return true;
- }
- void ExecuteCommand(int command_id, int event_flags) override {
- NOTREACHED();
- }
- void OpenContextMenu() override { NOTREACHED(); }
- void OnMenuClosed(TouchEditingMenuView* menu) override {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestTouchEditingMenuController);
-};
-
-// Tests if anchor rect for touch editing quick menu is adjusted correctly based
-// on the distance of handles.
-TEST_F(TouchSelectionControllerImplTest, QuickMenuAdjustsAnchorRect) {
- CreateWidget();
- aura::Window* window = widget_->GetNativeView();
-
- scoped_ptr<TestTouchEditingMenuController> quick_menu_controller(
- new TestTouchEditingMenuController());
-
- // Some arbitrary size for touch editing handle image.
- gfx::Size handle_image_size(10, 10);
-
- // Calculate the width of quick menu. In addition to |kMenuCommandCount|
- // commands, there is an item for ellipsis.
- int quick_menu_width = (kMenuCommandCount + 1) * kMenuButtonWidth +
- kMenuCommandCount;
-
- // Set anchor rect's width a bit smaller than the quick menu width plus handle
- // image width and check that anchor rect's height is adjusted.
- gfx::Rect anchor_rect(
- 0, 0, quick_menu_width + handle_image_size.width() - 10, 20);
- TouchEditingMenuView* quick_menu(TouchEditingMenuView::Create(
- quick_menu_controller.get(), anchor_rect, handle_image_size, window));
- anchor_rect.Inset(0, 0, 0, -handle_image_size.height());
- EXPECT_EQ(anchor_rect.ToString(), quick_menu->GetAnchorRect().ToString());
-
- // Set anchor rect's width a bit greater than the quick menu width plus handle
- // image width and check that anchor rect's height is not adjusted.
- anchor_rect =
- gfx::Rect(0, 0, quick_menu_width + handle_image_size.width() + 10, 20);
- quick_menu = TouchEditingMenuView::Create(
- quick_menu_controller.get(), anchor_rect, handle_image_size, window);
- EXPECT_EQ(anchor_rect.ToString(), quick_menu->GetAnchorRect().ToString());
-
- // Close the widget, hence quick menus, before quick menu controller goes out
- // of scope.
- widget_->CloseNow();
- widget_ = nullptr;
-}
-
TEST_F(TouchSelectionControllerImplTest, MouseEventDeactivatesTouchSelection) {
CreateTextfield();
EXPECT_FALSE(GetSelectionController());
diff --git a/ui/views/touchui/touch_selection_menu_runner_views.cc b/ui/views/touchui/touch_selection_menu_runner_views.cc
new file mode 100644
index 0000000..6f44e52
--- /dev/null
+++ b/ui/views/touchui/touch_selection_menu_runner_views.cc
@@ -0,0 +1,249 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/touchui/touch_selection_menu_runner_views.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "ui/aura/window.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/text_utils.h"
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/views/bubble/bubble_delegate.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace views {
+namespace {
+
+const int kMenuCommands[] = {IDS_APP_CUT, IDS_APP_COPY, IDS_APP_PASTE};
+const int kSpacingBetweenButtons = 2;
+const int kButtonSeparatorColor = SkColorSetARGB(13, 0, 0, 0);
+const int kMenuButtonMinHeight = 38;
+const int kMenuButtonMinWidth = 63;
+const int kMenuMargin = 1;
+
+const char kEllipsesButtonText[] = "...";
+const int kEllipsesButtonTag = -1;
+
+} // namespace
+
+// A bubble that contains actions available for the selected text. An object of
+// this type, as a BubbleDelegateView, manages its own lifetime.
+class TouchSelectionMenuRunnerViews::Menu : public BubbleDelegateView,
+ public ButtonListener {
+ public:
+ // Closes the menu. This will eventually self-destroy the object.
+ void Close();
+
+ // Returns a new instance of Menu if there is any command available;
+ // otherwise, returns |nullptr|.
+ static Menu* Create(TouchSelectionMenuRunnerViews* owner,
+ ui::TouchSelectionMenuClient* client,
+ const gfx::Rect& anchor_rect,
+ const gfx::Size& handle_image_size,
+ aura::Window* context);
+
+ private:
+ Menu(TouchSelectionMenuRunnerViews* owner,
+ ui::TouchSelectionMenuClient* client,
+ const gfx::Rect& anchor_rect,
+ const gfx::Size& handle_image_size,
+ aura::Window* context);
+
+ ~Menu() override;
+
+ // Queries the |client_| for what commands to show in the menu and sizes the
+ // menu appropriately.
+ void CreateButtons();
+
+ // Helper method to create a single button.
+ Button* CreateButton(const base::string16& title, int tag);
+
+ // BubbleDelegateView:
+ void OnPaint(gfx::Canvas* canvas) override;
+ void WindowClosing() override;
+
+ // ButtonListener:
+ void ButtonPressed(Button* sender, const ui::Event& event) override;
+
+ TouchSelectionMenuRunnerViews* owner_;
+ ui::TouchSelectionMenuClient* const client_;
+
+ DISALLOW_COPY_AND_ASSIGN(Menu);
+};
+
+TouchSelectionMenuRunnerViews::Menu*
+TouchSelectionMenuRunnerViews::Menu::Create(
+ TouchSelectionMenuRunnerViews* owner,
+ ui::TouchSelectionMenuClient* client,
+ const gfx::Rect& anchor_rect,
+ const gfx::Size& handle_image_size,
+ aura::Window* context) {
+ DCHECK(client);
+
+ for (size_t i = 0; i < arraysize(kMenuCommands); i++) {
+ if (client->IsCommandIdEnabled(kMenuCommands[i]))
+ return new Menu(owner, client, anchor_rect, handle_image_size, context);
+ }
+
+ // No command is available, so return |nullptr|.
+ return nullptr;
+}
+
+TouchSelectionMenuRunnerViews::Menu::Menu(TouchSelectionMenuRunnerViews* owner,
+ ui::TouchSelectionMenuClient* client,
+ const gfx::Rect& anchor_rect,
+ const gfx::Size& handle_image_size,
+ aura::Window* context)
+ : BubbleDelegateView(nullptr, BubbleBorder::BOTTOM_CENTER),
+ owner_(owner),
+ client_(client) {
+ DCHECK(owner_);
+ DCHECK(client_);
+
+ set_shadow(BubbleBorder::SMALL_SHADOW);
+ set_parent_window(context);
+ set_margins(gfx::Insets(kMenuMargin, kMenuMargin, kMenuMargin, kMenuMargin));
+ set_can_activate(false);
+ set_adjust_if_offscreen(true);
+ EnableCanvasFlippingForRTLUI(true);
+
+ SetLayoutManager(
+ new BoxLayout(BoxLayout::kHorizontal, 0, 0, kSpacingBetweenButtons));
+ CreateButtons();
+
+ // After buttons are created, check if there is enough room between handles to
+ // show the menu and adjust anchor rect properly if needed, just in case the
+ // menu is needed to be shown under the selection.
+ gfx::Rect adjusted_anchor_rect(anchor_rect);
+ int menu_width = GetPreferredSize().width();
+ // TODO(mfomitchev): This assumes that the handles are center-aligned to the
+ // |achor_rect| edges, which is not true. We should fix this, perhaps by
+ // passing down the cumulative width occupied by the handles within
+ // |anchor_rect| plus the handle image height instead of |handle_image_size|.
+ // Perhaps we should also allow for some minimum padding.
+ if (menu_width > anchor_rect.width() - handle_image_size.width())
+ adjusted_anchor_rect.Inset(0, 0, 0, -handle_image_size.height());
+ SetAnchorRect(adjusted_anchor_rect);
+
+ BubbleDelegateView::CreateBubble(this);
+ GetWidget()->Show();
+}
+
+TouchSelectionMenuRunnerViews::Menu::~Menu() {
+}
+
+void TouchSelectionMenuRunnerViews::Menu::CreateButtons() {
+ for (size_t i = 0; i < arraysize(kMenuCommands); i++) {
+ int command_id = kMenuCommands[i];
+ if (!client_->IsCommandIdEnabled(command_id))
+ continue;
+
+ Button* button =
+ CreateButton(l10n_util::GetStringUTF16(command_id), command_id);
+ AddChildView(button);
+ }
+
+ // Finally, add ellipses button.
+ AddChildView(
+ CreateButton(base::UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag));
+ Layout();
+}
+
+Button* TouchSelectionMenuRunnerViews::Menu::CreateButton(
+ const base::string16& title,
+ int tag) {
+ base::string16 label =
+ gfx::RemoveAcceleratorChar(title, '&', nullptr, nullptr);
+ LabelButton* button = new LabelButton(this, label);
+ button->SetMinSize(gfx::Size(kMenuButtonMinWidth, kMenuButtonMinHeight));
+ button->SetFocusable(true);
+ button->set_request_focus_on_press(false);
+ const gfx::FontList& font_list =
+ ui::ResourceBundle::GetSharedInstance().GetFontList(
+ ui::ResourceBundle::SmallFont);
+ button->SetFontList(font_list);
+ button->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+ button->set_tag(tag);
+ return button;
+}
+
+void TouchSelectionMenuRunnerViews::Menu::Close() {
+ // Closing the widget will self-destroy this object.
+ Widget* widget = GetWidget();
+ if (widget && !widget->IsClosed())
+ widget->Close();
+ owner_ = nullptr;
+}
+
+void TouchSelectionMenuRunnerViews::Menu::OnPaint(gfx::Canvas* canvas) {
+ BubbleDelegateView::OnPaint(canvas);
+
+ // Draw separator bars.
+ for (int i = 0; i < child_count() - 1; ++i) {
+ View* child = child_at(i);
+ int x = child->bounds().right() + kSpacingBetweenButtons / 2;
+ canvas->FillRect(gfx::Rect(x, 0, 1, child->height()),
+ kButtonSeparatorColor);
+ }
+}
+
+void TouchSelectionMenuRunnerViews::Menu::WindowClosing() {
+ DCHECK_IMPLIES(owner_, owner_->menu_ == this);
+ BubbleDelegateView::WindowClosing();
+ if (owner_)
+ owner_->menu_ = nullptr;
+}
+
+void TouchSelectionMenuRunnerViews::Menu::ButtonPressed(
+ Button* sender,
+ const ui::Event& event) {
+ Close();
+ if (sender->tag() != kEllipsesButtonTag)
+ client_->ExecuteCommand(sender->tag(), event.flags());
+ else
+ client_->RunContextMenu();
+}
+
+TouchSelectionMenuRunnerViews::TouchSelectionMenuRunnerViews()
+ : menu_(nullptr) {
+}
+
+TouchSelectionMenuRunnerViews::~TouchSelectionMenuRunnerViews() {
+ CloseMenu();
+}
+
+gfx::Rect TouchSelectionMenuRunnerViews::GetAnchorRectForTest() const {
+ return menu_ ? menu_->GetAnchorRect() : gfx::Rect();
+}
+
+void TouchSelectionMenuRunnerViews::OpenMenu(
+ ui::TouchSelectionMenuClient* client,
+ const gfx::Rect& anchor_rect,
+ const gfx::Size& handle_image_size,
+ aura::Window* context) {
+ CloseMenu();
+
+ menu_ = Menu::Create(this, client, anchor_rect, handle_image_size, context);
+}
+
+void TouchSelectionMenuRunnerViews::CloseMenu() {
+ if (!menu_)
+ return;
+
+ // Closing the menu will eventually delete the object.
+ menu_->Close();
+ menu_ = nullptr;
+}
+
+bool TouchSelectionMenuRunnerViews::IsRunning() const {
+ return menu_ != nullptr;
+}
+
+} // namespace views
diff --git a/ui/views/touchui/touch_selection_menu_runner_views.h b/ui/views/touchui/touch_selection_menu_runner_views.h
new file mode 100644
index 0000000..46032ee
--- /dev/null
+++ b/ui/views/touchui/touch_selection_menu_runner_views.h
@@ -0,0 +1,45 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_TOUCHUI_TOUCH_SELECTION_MENU_RUNNER_VIEWS_H_
+#define UI_VIEWS_TOUCHUI_TOUCH_SELECTION_MENU_RUNNER_VIEWS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/touch_selection/touch_selection_menu_runner.h"
+#include "ui/views/views_export.h"
+
+namespace views {
+
+// Views implementation for TouchSelectionMenuRunner.
+class VIEWS_EXPORT TouchSelectionMenuRunnerViews
+ : public ui::TouchSelectionMenuRunner {
+ public:
+ TouchSelectionMenuRunnerViews();
+ ~TouchSelectionMenuRunnerViews() override;
+
+ private:
+ friend class TouchSelectionMenuRunnerViewsTest;
+ class Menu;
+
+ // Helper for tests.
+ gfx::Rect GetAnchorRectForTest() const;
+
+ // ui::TouchSelectionMenuRunner:
+ void OpenMenu(ui::TouchSelectionMenuClient* client,
+ const gfx::Rect& anchor_rect,
+ const gfx::Size& handle_image_size,
+ aura::Window* context) override;
+ void CloseMenu() override;
+ bool IsRunning() const override;
+
+ // A pointer to the currently running menu, or |nullptr| if no menu is
+ // running. The menu manages its own lifetime and deletes itself when closed.
+ Menu* menu_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchSelectionMenuRunnerViews);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_TOUCHUI_TOUCH_SELECTION_MENU_RUNNER_VIEWS_H_
diff --git a/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc b/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
new file mode 100644
index 0000000..ab1a88f
--- /dev/null
+++ b/ui/views/touchui/touch_selection_menu_runner_views_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/strings/grit/ui_strings.h"
+#include "ui/touch_selection/touch_selection_menu_runner.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/touchui/touch_selection_menu_runner_views.h"
+#include "ui/views/views_delegate.h"
+
+namespace views {
+namespace {
+
+// Should match |kMenuButtonWidth| in touch_selection_menu_runner_views.cc.
+const int kMenuButtonWidth = 63;
+
+// Should match size of |kMenuCommands| array in
+// touch_selection_menu_runner_views.cc.
+const int kMenuCommandCount = 3;
+
+}
+
+class TouchSelectionMenuRunnerViewsTest : public ViewsTestBase,
+ public ui::TouchSelectionMenuClient {
+ public:
+ TouchSelectionMenuRunnerViewsTest() : no_command_available_(false) {}
+ ~TouchSelectionMenuRunnerViewsTest() override {}
+
+ protected:
+ void set_no_commmand_available(bool no_command) {
+ no_command_available_ = no_command;
+ }
+
+ gfx::Rect GetMenuAnchorRect() {
+ TouchSelectionMenuRunnerViews* menu_runner =
+ static_cast<TouchSelectionMenuRunnerViews*>(
+ ui::TouchSelectionMenuRunner::GetInstance());
+ return menu_runner->GetAnchorRectForTest();
+ }
+
+ private:
+ // ui::TouchSelectionMenuClient:
+ bool IsCommandIdEnabled(int command_id) const override {
+ return !no_command_available_;
+ }
+
+ void ExecuteCommand(int command_id, int event_flags) override {}
+
+ void RunContextMenu() override {}
+
+ // When set to true, no command would be availble and menu should not be
+ // shown.
+ bool no_command_available_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchSelectionMenuRunnerViewsTest);
+};
+
+TEST_F(TouchSelectionMenuRunnerViewsTest, InstalledAndWorksProperly) {
+ gfx::Rect menu_anchor(0, 0, 10, 10);
+ gfx::Size handle_size(10, 10);
+
+ // Menu runner instance should be installed, but no menu should be running.
+ EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance());
+ EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
+
+ // Run menu. Since commands are availble, this should bring up menus.
+ ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu(
+ this, menu_anchor, handle_size, GetContext());
+ EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
+
+ // Close menu.
+ ui::TouchSelectionMenuRunner::GetInstance()->CloseMenu();
+ RunPendingMessages();
+ EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
+
+ // Try running menu when no commands is available. Menu should not be shown.
+ set_no_commmand_available(true);
+ ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu(
+ this, menu_anchor, handle_size, GetContext());
+ EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
+}
+
+// Tests if anchor rect for the quick menu is adjusted correctly based on the
+// distance of handles.
+TEST_F(TouchSelectionMenuRunnerViewsTest, QuickMenuAdjustsAnchorRect) {
+ gfx::Size handle_size(10, 10);
+
+ // Calculate the width of quick menu. In addition to |kMenuCommandCount|
+ // commands, there is an item for ellipsis.
+ int quick_menu_width =
+ (kMenuCommandCount + 1) * kMenuButtonWidth + kMenuCommandCount;
+
+ // Set anchor rect's width a bit smaller than the quick menu width plus handle
+ // image width and check that anchor rect's height is adjusted.
+ gfx::Rect anchor_rect(0, 0, quick_menu_width + handle_size.width() - 10, 20);
+ ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu(
+ this, anchor_rect, handle_size, GetContext());
+ anchor_rect.Inset(0, 0, 0, -handle_size.height());
+ EXPECT_EQ(anchor_rect, GetMenuAnchorRect());
+
+ // Set anchor rect's width a bit greater than the quick menu width plus handle
+ // image width and check that anchor rect's height is not adjusted.
+ anchor_rect =
+ gfx::Rect(0, 0, quick_menu_width + handle_size.width() + 10, 20);
+ ui::TouchSelectionMenuRunner::GetInstance()->OpenMenu(
+ this, anchor_rect, handle_size, GetContext());
+ EXPECT_EQ(anchor_rect, GetMenuAnchorRect());
+}
+
+} // namespace views
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 100bad9..6da0fa7 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -388,10 +388,10 @@
'event_monitor_aura.h',
'metrics_aura.cc',
'native_cursor_aura.cc',
- 'touchui/touch_editing_menu.cc',
- 'touchui/touch_editing_menu.h',
'touchui/touch_selection_controller_impl.cc',
'touchui/touch_selection_controller_impl.h',
+ 'touchui/touch_selection_menu_runner_views.cc',
+ 'touchui/touch_selection_menu_runner_views.h',
'view_constants_aura.cc',
'view_constants_aura.h',
'views_touch_selection_controller_factory_aura.cc',
@@ -581,6 +581,7 @@
'controls/native/native_view_host_aura_unittest.cc',
'corewm/tooltip_controller_unittest.cc',
'touchui/touch_selection_controller_impl_unittest.cc',
+ 'touchui/touch_selection_menu_runner_views_unittest.cc',
'view_unittest_aura.cc',
'widget/native_widget_aura_unittest.cc',
],
@@ -706,6 +707,7 @@
],
'dependencies': [
'../aura/aura.gyp:aura',
+ '../touch_selection/ui_touch_selection.gyp:ui_touch_selection',
'../wm/wm.gyp:wm',
],
}],
@@ -864,6 +866,7 @@
'dependencies': [
'../aura/aura.gyp:aura',
'../aura/aura.gyp:aura_test_support',
+ '../touch_selection/ui_touch_selection.gyp:ui_touch_selection',
'../wm/wm.gyp:wm',
],
'conditions': [
diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc
index 7c34470..507b038 100644
--- a/ui/views/views_delegate.cc
+++ b/ui/views/views_delegate.cc
@@ -7,11 +7,19 @@
#include "base/command_line.h"
#include "ui/views/views_touch_selection_controller_factory.h"
+#if defined(USE_AURA)
+#include "ui/views/touchui/touch_selection_menu_runner_views.h"
+#endif
+
namespace views {
ViewsDelegate::ViewsDelegate()
: views_tsc_factory_(new ViewsTouchEditingControllerFactory) {
ui::TouchEditingControllerFactory::SetInstance(views_tsc_factory_.get());
+
+#if defined(USE_AURA)
+ touch_selection_menu_runner_.reset(new TouchSelectionMenuRunnerViews());
+#endif
}
ViewsDelegate::~ViewsDelegate() {
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index 5013715..39c7f5a 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -47,6 +47,11 @@ class NonClientFrameView;
class ViewsTouchEditingControllerFactory;
class View;
class Widget;
+
+#if defined(USE_AURA)
+class TouchSelectionMenuRunnerViews;
+#endif
+
namespace internal {
class NativeWidgetDelegate;
}
@@ -161,6 +166,10 @@ class VIEWS_EXPORT ViewsDelegate {
private:
scoped_ptr<ViewsTouchEditingControllerFactory> views_tsc_factory_;
+#if defined(USE_AURA)
+ scoped_ptr<TouchSelectionMenuRunnerViews> touch_selection_menu_runner_;
+#endif
+
DISALLOW_COPY_AND_ASSIGN(ViewsDelegate);
};
diff --git a/ui/views/widget/widget_interactive_uitest.cc b/ui/views/widget/widget_interactive_uitest.cc
index 5c27773..b8f138d 100644
--- a/ui/views/widget/widget_interactive_uitest.cc
+++ b/ui/views/widget/widget_interactive_uitest.cc
@@ -284,22 +284,16 @@ class WidgetTestInteractive : public WidgetTest {
}
protected:
+#if defined(USE_AURA)
static void ShowQuickMenuImmediately(
TouchSelectionControllerImpl* controller) {
DCHECK(controller);
- if (controller->context_menu_timer_.IsRunning()) {
- controller->context_menu_timer_.Stop();
-// TODO(tapted): Enable this when porting ui/views/touchui to Mac.
-#if !defined(OS_MACOSX)
- controller->ContextMenuTimerFired();
-#endif
+ if (controller->quick_menu_timer_.IsRunning()) {
+ controller->quick_menu_timer_.Stop();
+ controller->QuickMenuTimerFired();
}
}
-
- static bool IsQuickMenuVisible(TouchSelectionControllerImpl* controller) {
- DCHECK(controller);
- return controller->context_menu_ && controller->context_menu_->visible();
- }
+#endif // defined (USE_AURA)
Widget* CreateWidget() {
Widget* widget = CreateNativeDesktopWidget();
@@ -894,17 +888,9 @@ TEST_F(WidgetTestInteractive, CanActivateFlagIsHonored) {
EXPECT_FALSE(widget.IsActive());
}
-// No touch on desktop Mac. Tracked in http://crbug.com/445520.
-#if defined(OS_MACOSX) && !defined(USE_AURA)
-#define MAYBE_TouchSelectionQuickMenuIsNotActivated \
- DISABLED_TouchSelectionQuickMenuIsNotActivated
-#else
-#define MAYBE_TouchSelectionQuickMenuIsNotActivated \
- TouchSelectionQuickMenuIsNotActivated
-#endif
-
+#if defined(USE_AURA)
// Test that touch selection quick menu is not activated when opened.
-TEST_F(WidgetTestInteractive, MAYBE_TouchSelectionQuickMenuIsNotActivated) {
+TEST_F(WidgetTestInteractive, TouchSelectionQuickMenuIsNotActivated) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableTouchEditing);
#if defined(OS_WIN)
@@ -932,10 +918,10 @@ TEST_F(WidgetTestInteractive, MAYBE_TouchSelectionQuickMenuIsNotActivated) {
EXPECT_TRUE(textfield->HasFocus());
EXPECT_TRUE(widget->IsActive());
- EXPECT_TRUE(IsQuickMenuVisible(static_cast<TouchSelectionControllerImpl*>(
- textfield_test_api.touch_selection_controller())));
+ EXPECT_TRUE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
widget->CloseNow();
}
+#endif // defined(USE_AURA)
TEST_F(WidgetTestInteractive, DisableViewDoesNotActivateWidget) {
#if defined(OS_WIN)