Enable touch editing controller for views textfields behind a flag.
BUG=115237
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=182542
Review URL: https://chromiumcodereview.appspot.com/12210154
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182841 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/ui/base/touch/touch_editing_controller.cc b/ui/base/touch/touch_editing_controller.cc
index 8afb740..1b84ed9 100644
--- a/ui/base/touch/touch_editing_controller.cc
+++ b/ui/base/touch/touch_editing_controller.cc
@@ -6,9 +6,21 @@
namespace ui {
+namespace {
+TouchSelectionControllerFactory* g_shared_instance = NULL;
+} // namespace
+
TouchSelectionController* TouchSelectionController::create(
TouchEditable* client_view) {
+ if (g_shared_instance)
+ return g_shared_instance->create(client_view);
return NULL;
}
+// static
+void TouchSelectionControllerFactory::SetInstance(
+ TouchSelectionControllerFactory* instance) {
+ g_shared_instance = instance;
+}
+
} // namespace ui
diff --git a/ui/base/touch/touch_editing_controller.h b/ui/base/touch/touch_editing_controller.h
index 999c2ad3..e7985ce3 100644
--- a/ui/base/touch/touch_editing_controller.h
+++ b/ui/base/touch/touch_editing_controller.h
@@ -20,6 +20,17 @@
// end of selection. Visually, |start| may lie after |end|.
virtual void SelectRect(const gfx::Point& start, const gfx::Point& end) = 0;
+ // Gets the end points of the current selection. The end points p1 and p2 must
+ // be the cursor rect for the start and end of selection:
+ // ____________________________________
+ // | textfield with |selected text| |
+ // ------------------------------------
+ // ^p1 ^p2
+ //
+ // p1 should be the logical start and p2 the logical end of selection. Hence,
+ // visually, p1 could be to the right of p2 in the figure above.
+ virtual void GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) = 0;
+
// Gets the bounds of the client view in parent's coordinates.
virtual const gfx::Rect& GetBounds() = 0;
@@ -30,6 +41,13 @@
virtual void ConvertPointToScreen(gfx::Point* point) = 0;
virtual void ConvertPointFromScreen(gfx::Point* point) = 0;
+ // Returns true if the editable draws its own handles (hence, the
+ // TouchSelectionController need not draw handles).
+ virtual bool DrawsHandles() = 0;
+
+ // Tells the editable to open context menu.
+ virtual void OpenContextMenu(const gfx::Point anchor) = 0;
+
protected:
virtual ~TouchEditable() {}
};
@@ -44,19 +62,18 @@
static TouchSelectionController* create(
TouchEditable* client_view);
- // Notification that the text selection in TouchEditable has
- // changed. p1 and p2 are lower corners of the start and end of selection:
- // ____________________________________
- // | textfield with |selected text| |
- // ------------------------------------
- // ^p1 ^p2
- //
- // p1 is always the start and p2 is always the end of selection. Hence,
- // p1 could be to the right of p2 in the figure above.
- virtual void SelectionChanged(const gfx::Point& p1, const gfx::Point& p2) = 0;
+ // Notifies the controller that the selection has changed.
+ virtual void SelectionChanged() = 0;
+};
- // Notification that the TouchEditable has lost focus.
- virtual void ClientViewLostFocus() = 0;
+class UI_EXPORT TouchSelectionControllerFactory {
+ public:
+ static void SetInstance(TouchSelectionControllerFactory* instance);
+
+ virtual TouchSelectionController* create(TouchEditable* client_view) = 0;
+
+ protected:
+ virtual ~TouchSelectionControllerFactory() {}
};
} // namespace views
diff --git a/ui/base/ui_base_switches.cc b/ui/base/ui_base_switches.cc
index e647ac3..b4fdabf 100644
--- a/ui/base/ui_base_switches.cc
+++ b/ui/base/ui_base_switches.cc
@@ -20,6 +20,9 @@
const char kEnableTouchDragDrop[] = "enable-touch-drag-drop";
+// Enables controls that support touch base text editing.
+const char kEnableTouchEditing[] = "enable-touch-editing";
+
// Enables the Views textfield on Windows.
const char kEnableViewsTextfield[] = "enable-views-textfield";
diff --git a/ui/base/ui_base_switches.h b/ui/base/ui_base_switches.h
index 14bdebc44..b21e2b1 100644
--- a/ui/base/ui_base_switches.h
+++ b/ui/base/ui_base_switches.h
@@ -17,6 +17,7 @@
UI_EXPORT extern const char kEnableBezelTouch[];
UI_EXPORT extern const char kEnableNewDialogStyle[];
UI_EXPORT extern const char kEnableTouchDragDrop[];
+UI_EXPORT extern const char kEnableTouchEditing[];
UI_EXPORT extern const char kEnableViewsTextfield[];
UI_EXPORT extern const char kForceDeviceScaleFactor[];
UI_EXPORT extern const char kHighlightMissingScaledResources[];
diff --git a/ui/views/controls/textfield/native_textfield_views.cc b/ui/views/controls/textfield/native_textfield_views.cc
index 08ba3fff..da41dc6 100644
--- a/ui/views/controls/textfield/native_textfield_views.cc
+++ b/ui/views/controls/textfield/native_textfield_views.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
@@ -73,8 +73,7 @@
initiating_drag_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)),
aggregated_clicks_(0),
- ALLOW_THIS_IN_INITIALIZER_LIST(touch_selection_controller_(
- ui::TouchSelectionController::create(this))) {
+ touch_selection_controller_(NULL) {
set_border(text_border_);
#if defined(OS_CHROMEOS)
@@ -108,6 +107,7 @@
}
OnAfterUserAction();
+ touch_selection_controller_.reset();
return true;
}
@@ -173,6 +173,12 @@
OnAfterUserAction();
event->SetHandled();
break;
+ case ui::ET_GESTURE_TAP:
+ touch_selection_controller_.reset(
+ ui::TouchSelectionController::create(this));
+ if (touch_selection_controller_.get())
+ touch_selection_controller_->SelectionChanged();
+ break;
default:
View::OnGestureEvent(event);
return;
@@ -316,6 +322,16 @@
OnAfterUserAction();
}
+void NativeTextfieldViews::GetSelectionEndPoints(gfx::Rect* p1,
+ gfx::Rect* p2) {
+ gfx::RenderText* render_text = GetRenderText();
+ const gfx::SelectionModel& sel = render_text->selection_model();
+ gfx::SelectionModel start_sel =
+ render_text->GetSelectionModelForSelectionStart();
+ *p1 = render_text->GetCursorBounds(start_sel, true);
+ *p2 = render_text->GetCursorBounds(sel, true);
+}
+
const gfx::Rect& NativeTextfieldViews::GetBounds() {
return bounds();
}
@@ -332,6 +348,15 @@
View::ConvertPointFromScreen(this, point);
}
+bool NativeTextfieldViews::DrawsHandles() {
+ return false;
+}
+
+void NativeTextfieldViews::OpenContextMenu(const gfx::Point anchor) {
+ touch_selection_controller_.reset();
+ ShowContextMenu(anchor, false);
+}
+
gfx::NativeCursor NativeTextfieldViews::GetCursor(const ui::MouseEvent& event) {
bool in_selection = GetRenderText()->IsPointInSelection(event.location());
bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED;
@@ -580,6 +605,7 @@
bool handled = false;
if (controller)
handled = controller->HandleKeyEvent(textfield_, e);
+ touch_selection_controller_.reset();
return handled || HandleKeyEvent(e);
}
@@ -609,8 +635,7 @@
RepaintCursor();
}
- if (touch_selection_controller_.get())
- touch_selection_controller_->ClientViewLostFocus();
+ touch_selection_controller_.reset();
ClearSelection();
}
@@ -679,6 +704,7 @@
}
void NativeTextfieldViews::ExecuteCommand(int command_id) {
+ touch_selection_controller_.reset();
if (!IsCommandIdEnabled(command_id))
return;
@@ -1194,17 +1220,8 @@
textfield_->GetInputMethod()->OnCaretBoundsChanged(textfield_);
// Notify selection controller
- if (!touch_selection_controller_.get())
- return;
- gfx::RenderText* render_text = GetRenderText();
- const gfx::SelectionModel& sel = render_text->selection_model();
- gfx::SelectionModel start_sel =
- render_text->GetSelectionModelForSelectionStart();
- gfx::Rect start_cursor = render_text->GetCursorBounds(start_sel, true);
- gfx::Rect end_cursor = render_text->GetCursorBounds(sel, true);
- gfx::Point start(start_cursor.x(), start_cursor.bottom() - 1);
- gfx::Point end(end_cursor.x(), end_cursor.bottom() - 1);
- touch_selection_controller_->SelectionChanged(start, end);
+ if (touch_selection_controller_.get())
+ touch_selection_controller_->SelectionChanged();
}
void NativeTextfieldViews::OnBeforeUserAction() {
diff --git a/ui/views/controls/textfield/native_textfield_views.h b/ui/views/controls/textfield/native_textfield_views.h
index b9a0a73..112d901 100644
--- a/ui/views/controls/textfield/native_textfield_views.h
+++ b/ui/views/controls/textfield/native_textfield_views.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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.
@@ -80,10 +80,13 @@
// ui::TouchEditable overrides:
virtual void SelectRect(const gfx::Point& start,
const gfx::Point& end) OVERRIDE;
+ virtual void GetSelectionEndPoints(gfx::Rect* p1, gfx::Rect* p2) OVERRIDE;
virtual const gfx::Rect& GetBounds() OVERRIDE;
virtual gfx::NativeView GetNativeView() OVERRIDE;
virtual void ConvertPointToScreen(gfx::Point* point) OVERRIDE;
virtual void ConvertPointFromScreen(gfx::Point* point) OVERRIDE;
+ virtual bool DrawsHandles() OVERRIDE;
+ virtual void OpenContextMenu(const gfx::Point anchor) OVERRIDE;
// ContextMenuController overrides:
virtual void ShowContextMenuForView(View* source,
diff --git a/ui/views/touchui/touch_editing_menu.cc b/ui/views/touchui/touch_editing_menu.cc
new file mode 100644
index 0000000..ed1dae9
--- /dev/null
+++ b/ui/views/touchui/touch_editing_menu.cc
@@ -0,0 +1,150 @@
+// 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/utf_string_conversions.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/insets.h"
+#include "ui/gfx/text_utils.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,
+ IDS_APP_DELETE,
+ IDS_APP_SELECT_ALL};
+const int kSpacingBetweenButtons = 0;
+const int kButtonSeparatorColor = SkColorSetARGB(13, 0, 0, 0);
+const int kMenuButtonBorderThickness = 5;
+const SkColor kMenuButtonColorNormal = SkColorSetARGB(102, 255, 255, 255);
+const SkColor kMenuButtonColorHover = SkColorSetARGB(13, 0, 0, 0);
+
+const char* kEllipsesButtonText = "...";
+const int kEllipsesButtonTag = -1;
+} // namespace
+
+namespace views {
+
+class TouchEditingMenuButtonBackground : public Background {
+ public:
+ TouchEditingMenuButtonBackground() {}
+
+ virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
+ CustomButton::ButtonState state = static_cast<CustomButton*>(view)->state();
+ SkColor background_color = (state == CustomButton::STATE_NORMAL)?
+ kMenuButtonColorNormal : kMenuButtonColorHover;
+ int w = view->width();
+ int h = view->height();
+ canvas->FillRect(gfx::Rect(1, 0, w, h), background_color);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TouchEditingMenuButtonBackground);
+};
+
+TouchEditingMenuView::TouchEditingMenuView(
+ TouchEditingMenuController* controller,
+ gfx::Point anchor_point,
+ gfx::NativeView context)
+ : BubbleDelegateView(NULL, views::BubbleBorder::BOTTOM_CENTER),
+ controller_(controller) {
+ set_anchor_point(anchor_point);
+ set_shadow(views::BubbleBorder::SMALL_SHADOW);
+ set_parent_window(context);
+ set_margins(gfx::Insets());
+ set_use_focusless(true);
+ set_adjust_if_offscreen(true);
+
+ SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, 0, 0,
+ kSpacingBetweenButtons));
+ CreateButtons();
+ views::BubbleDelegateView::CreateBubble(this);
+ GetBubbleFrameView()->set_background(NULL);
+ Show();
+}
+
+TouchEditingMenuView::~TouchEditingMenuView() {
+}
+
+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());
+ else
+ controller_->OpenContextMenu();
+ }
+}
+
+void TouchEditingMenuView::OnPaint(gfx::Canvas* canvas) {
+ BubbleDelegateView::OnPaint(canvas);
+
+ // Draw separator bars.
+ int x = 0;
+ for (int i = 0; i < child_count() - 1; ++i) {
+ View* child = child_at(i);
+ x += child->width();
+ 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(
+ UTF8ToUTF16(kEllipsesButtonText), kEllipsesButtonTag));
+ Layout();
+}
+
+Button* TouchEditingMenuView::CreateButton(const string16& title, int tag) {
+ LabelButton* button = new LabelButton(this, gfx::RemoveAcceleratorChar(
+ title, '&', NULL, NULL));
+ button->set_focusable(true);
+ button->set_request_focus_on_press(false);
+ button->set_background(new TouchEditingMenuButtonBackground);
+ button->set_border(Border::CreateEmptyBorder(kMenuButtonBorderThickness,
+ kMenuButtonBorderThickness,
+ kMenuButtonBorderThickness,
+ kMenuButtonBorderThickness));
+ button->SetFont(ui::ResourceBundle::GetSharedInstance().GetFont(
+ ui::ResourceBundle::SmallFont));
+ 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
new file mode 100644
index 0000000..a667749
--- /dev/null
+++ b/ui/views/touchui/touch_editing_menu.h
@@ -0,0 +1,74 @@
+// 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/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) = 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(TouchEditingMenuController* controller,
+ gfx::Point anchor_point,
+ gfx::NativeView context);
+ virtual ~TouchEditingMenuView();
+
+ void Close();
+
+ private:
+ // views::WidgetDelegate overrides:
+ virtual void WindowClosing() OVERRIDE;
+
+ // Overridden from ButtonListener.
+ virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE;
+
+ // Overridden from BubbleDelegateView.
+ virtual 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 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 5956373..a0ca742 100644
--- a/ui/views/touchui/touch_selection_controller_impl.cc
+++ b/ui/views/touchui/touch_selection_controller_impl.cc
@@ -1,52 +1,32 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// 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_selection_controller_impl.h"
+#include "base/command_line.h"
#include "base/time.h"
-#include "base/utf_string_conversions.h"
#include "grit/ui_strings.h"
-#include "third_party/skia/include/effects/SkGradientShader.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_switches.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size.h"
-#include "ui/gfx/text_utils.h"
-#include "ui/gfx/transform.h"
-#include "ui/views/background.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/button/custom_button.h"
-#include "ui/views/controls/button/text_button.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/controls/menu/menu_config.h"
-#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/widget.h"
namespace {
// Constants defining the visual attributes of selection handles
const int kSelectionHandleRadius = 10;
-const int kSelectionHandleCursorHeight = 10;
const int kSelectionHandleAlpha = 0x7F;
const SkColor kSelectionHandleColor =
- SkColorSetA(SK_ColorBLUE, kSelectionHandleAlpha);
+ SkColorSetA(SK_ColorBLACK, kSelectionHandleAlpha);
// The minimum selection size to trigger selection controller.
const int kMinSelectionSize = 4;
-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 = 25;
+const int kContextMenuVerticalOffset = 5;
// Convenience struct to represent a circle shape.
struct Circle {
@@ -56,12 +36,13 @@
};
// Creates a widget to host SelectionHandleView.
-views::Widget* CreateTouchSelectionPopupWidget() {
+views::Widget* CreateTouchSelectionPopupWidget(gfx::NativeView context) {
views::Widget* widget = new views::Widget;
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
params.can_activate = false;
params.transparent = true;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.context = context;
widget->Init(params);
return widget;
}
@@ -90,9 +71,11 @@
// A View that displays the text selection handle.
class TouchSelectionControllerImpl::SelectionHandleView : public View {
public:
- explicit SelectionHandleView(TouchSelectionControllerImpl* controller)
- : controller_(controller) {
- widget_.reset(CreateTouchSelectionPopupWidget());
+ explicit SelectionHandleView(TouchSelectionControllerImpl* controller,
+ gfx::NativeView context)
+ : controller_(controller),
+ cursor_height_(0) {
+ widget_.reset(CreateTouchSelectionPopupWidget(context));
widget_->SetContentsView(this);
widget_->SetAlwaysOnTop(true);
@@ -105,30 +88,30 @@
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
Circle circle = {kSelectionHandleRadius, gfx::Point(kSelectionHandleRadius,
- kSelectionHandleRadius + kSelectionHandleCursorHeight),
+ kSelectionHandleRadius + cursor_height_),
kSelectionHandleColor};
PaintCircle(circle, canvas);
canvas->DrawLine(gfx::Point(kSelectionHandleRadius, 0),
- gfx::Point(kSelectionHandleRadius, kSelectionHandleCursorHeight),
+ gfx::Point(kSelectionHandleRadius, cursor_height_),
kSelectionHandleColor);
}
- virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
- controller_->dragging_handle_ = this;
- return true;
- }
-
- virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE {
- controller_->SelectionHandleDragged(event.location());
- return true;
- }
-
- virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE {
- controller_->dragging_handle_ = NULL;
- }
-
- virtual void OnMouseCaptureLost() OVERRIDE {
- controller_->dragging_handle_ = NULL;
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
+ event->SetHandled();
+ switch (event->type()) {
+ case ui::ET_GESTURE_SCROLL_BEGIN:
+ controller_->dragging_handle_ = this;
+ break;
+ case ui::ET_GESTURE_SCROLL_UPDATE:
+ controller_->SelectionHandleDragged(event->location());
+ break;
+ case ui::ET_GESTURE_SCROLL_END:
+ case ui::ET_GESTURE_END:
+ controller_->dragging_handle_ = NULL;
+ break;
+ default:
+ break;
+ }
}
virtual void SetVisible(bool visible) OVERRIDE {
@@ -144,13 +127,18 @@
virtual gfx::Size GetPreferredSize() OVERRIDE {
return gfx::Size(2 * kSelectionHandleRadius,
- 2 * kSelectionHandleRadius + kSelectionHandleCursorHeight);
+ 2 * kSelectionHandleRadius + cursor_height_);
}
- void SetScreenPosition(const gfx::Point& position) {
- gfx::Rect widget_bounds(position.x() - kSelectionHandleRadius, position.y(),
+ bool IsWidgetVisible() const {
+ return widget_->IsVisible();
+ }
+
+ void SetSelectionRectInScreen(const gfx::Rect& rect) {
+ cursor_height_ = rect.height();
+ gfx::Rect widget_bounds(rect.x() - kSelectionHandleRadius, rect.y(),
2 * kSelectionHandleRadius,
- 2 * kSelectionHandleRadius + kSelectionHandleCursorHeight);
+ 2 * kSelectionHandleRadius + cursor_height_);
widget_->SetBounds(widget_bounds);
}
@@ -161,194 +149,65 @@
private:
scoped_ptr<Widget> widget_;
TouchSelectionControllerImpl* controller_;
+ int cursor_height_;
DISALLOW_COPY_AND_ASSIGN(SelectionHandleView);
};
-class ContextMenuButtonBackground : public Background {
- public:
- ContextMenuButtonBackground() {}
-
- virtual void Paint(gfx::Canvas* canvas, View* view) const OVERRIDE {
- CustomButton::ButtonState state = static_cast<CustomButton*>(view)->state();
- SkColor background_color, border_color;
- if (state == CustomButton::STATE_NORMAL) {
- background_color = SkColorSetARGB(102, 255, 255, 255);
- border_color = SkColorSetARGB(36, 0, 0, 0);
- } else {
- background_color = SkColorSetARGB(13, 0, 0, 0);
- border_color = SkColorSetARGB(72, 0, 0, 0);
- }
- int w = view->width();
- int h = view->height();
- canvas->FillRect(gfx::Rect(1, 1, w - 2, h - 2), background_color);
- canvas->FillRect(gfx::Rect(2, 0, w - 4, 1), border_color);
- canvas->FillRect(gfx::Rect(1, 1, 1, 1), border_color);
- canvas->FillRect(gfx::Rect(0, 2, 1, h - 4), border_color);
- canvas->FillRect(gfx::Rect(1, h - 2, 1, 1), border_color);
- canvas->FillRect(gfx::Rect(2, h - 1, w - 4, 1), border_color);
- canvas->FillRect(gfx::Rect(w - 2, 1, 1, 1), border_color);
- canvas->FillRect(gfx::Rect(w - 1, 2, 1, h - 4), border_color);
- canvas->FillRect(gfx::Rect(w - 2, h - 2, 1, 1), border_color);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ContextMenuButtonBackground);
-};
-
-// A View that displays the touch context menu.
-class TouchSelectionControllerImpl::TouchContextMenuView
- : public ButtonListener,
- public View {
- public:
- explicit TouchContextMenuView(TouchSelectionControllerImpl* controller)
- : controller_(controller) {
- widget_.reset(CreateTouchSelectionPopupWidget());
- widget_->SetContentsView(this);
- widget_->SetAlwaysOnTop(true);
-
- // We are owned by the TouchSelectionController.
- set_owned_by_client();
- SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, kContextMenuPadding,
- kContextMenuPadding, kContextMenuPadding));
- }
-
- virtual ~TouchContextMenuView() {
- }
-
- 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_->GetClientAreaBoundsInScreen().origin();
- }
-
- void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE {
- // TODO(varunjain): the following color scheme is copied from
- // menu_scroll_view_container.cc. Figure out how to consolidate the two
- // pieces of code.
-#if defined(OS_CHROMEOS)
- static const SkColor kGradientColors[2] = {
- SK_ColorWHITE,
- SkColorSetRGB(0xF0, 0xF0, 0xF0)
- };
-
- static const SkScalar kGradientPoints[2] = {
- SkIntToScalar(0),
- SkIntToScalar(1)
- };
-
- SkPoint points[2];
- points[0].iset(0, 0);
- points[1].iset(0, height());
-
- skia::RefPtr<SkShader> shader = skia::AdoptRef(
- SkGradientShader::CreateLinear(
- points, kGradientColors, kGradientPoints,
- arraysize(kGradientPoints),
- SkShader::kRepeat_TileMode));
- DCHECK(shader);
-
- SkPaint paint;
- paint.setShader(shader.get());
-
- paint.setStyle(SkPaint::kFill_Style);
- paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-
- canvas->DrawRect(GetLocalBounds(), paint);
-#else
- canvas->DrawColor(SkColorSetRGB(210, 225, 246),
- SkXfermode::kSrc_Mode);
-#endif
- }
-
- // Overridden from ButtonListener:
- virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE {
- 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, gfx::RemoveAcceleratorChar(
- l10n_util::GetStringUTF16(command_id), '&', NULL, NULL));
- button->set_focusable(true);
- button->set_request_focus_on_press(false);
- button->SetEnabledColor(MenuConfig::instance().text_color);
- button->set_background(new ContextMenuButtonBackground());
- 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::GetNativeScreen()->
- GetDisplayNearestPoint(position).bounds();
- widget_->SetBounds(widget_bounds.AdjustToFit(monitor_bounds));
- Layout();
- }
-
- scoped_ptr<Widget> widget_;
- TouchSelectionControllerImpl* controller_;
-
- DISALLOW_COPY_AND_ASSIGN(TouchContextMenuView);
-};
-
TouchSelectionControllerImpl::TouchSelectionControllerImpl(
ui::TouchEditable* client_view)
: client_view_(client_view),
- selection_handle_1_(new SelectionHandleView(this)),
- selection_handle_2_(new SelectionHandleView(this)),
- context_menu_(new TouchContextMenuView(this)),
+ client_widget_(NULL),
+ selection_handle_1_(new SelectionHandleView(this,
+ client_view->GetNativeView())),
+ selection_handle_2_(new SelectionHandleView(this,
+ client_view->GetNativeView())),
+ context_menu_(NULL),
dragging_handle_(NULL) {
+ client_widget_ = Widget::GetWidgetForNativeView(
+ client_view_->GetNativeView());
+ client_widget_->AddObserver(this);
}
TouchSelectionControllerImpl::~TouchSelectionControllerImpl() {
+ HideContextMenu();
+ if (client_widget_)
+ client_widget_->RemoveObserver(this);
}
-void TouchSelectionControllerImpl::SelectionChanged(const gfx::Point& p1,
- const gfx::Point& p2) {
- gfx::Point screen_pos_1(p1);
+void TouchSelectionControllerImpl::SelectionChanged() {
+ gfx::Rect r1, r2;
+ client_view_->GetSelectionEndPoints(&r1, &r2);
+ gfx::Point screen_pos_1(r1.origin());
client_view_->ConvertPointToScreen(&screen_pos_1);
- gfx::Point screen_pos_2(p2);
+ gfx::Point screen_pos_2(r2.origin());
client_view_->ConvertPointToScreen(&screen_pos_2);
+ gfx::Rect screen_rect_1(screen_pos_1, r1.size());
+ gfx::Rect screen_rect_2(screen_pos_2, r2.size());
+ if (client_view_->DrawsHandles()) {
+ UpdateContextMenu(r1.origin(), r2.origin());
+ return;
+ }
if (dragging_handle_) {
// We need to reposition only the selection handle that is being dragged.
// The other handle stays the same. Also, the selection handle being dragged
// will always be at the end of selection, while the other handle will be at
// the start.
- dragging_handle_->SetScreenPosition(screen_pos_2);
+ dragging_handle_->SetSelectionRectInScreen(screen_rect_2);
+
+ // The non-dragging-handle might have recently become visible.
+ SelectionHandleView* non_dragging_handle =
+ dragging_handle_ == selection_handle_1_.get()?
+ selection_handle_2_.get() : selection_handle_1_.get();
+ if (client_view_->GetBounds().Contains(r1.origin())) {
+ non_dragging_handle->SetSelectionRectInScreen(screen_rect_1);
+ non_dragging_handle->SetVisible(true);
+ } else {
+ non_dragging_handle->SetVisible(false);
+ }
} else {
- UpdateContextMenu(p1, p2);
+ UpdateContextMenu(r1.origin(), r2.origin());
// Check if there is any selection at all.
if (IsEmptySelection(screen_pos_2, screen_pos_1)) {
@@ -357,15 +216,15 @@
return;
}
- if (client_view_->GetBounds().Contains(p1)) {
- selection_handle_1_->SetScreenPosition(screen_pos_1);
+ if (client_view_->GetBounds().Contains(r1.origin())) {
+ selection_handle_1_->SetSelectionRectInScreen(screen_rect_1);
selection_handle_1_->SetVisible(true);
} else {
selection_handle_1_->SetVisible(false);
}
- if (client_view_->GetBounds().Contains(p2)) {
- selection_handle_2_->SetScreenPosition(screen_pos_2);
+ if (client_view_->GetBounds().Contains(r2.origin())) {
+ selection_handle_2_->SetSelectionRectInScreen(screen_rect_2);
selection_handle_2_->SetVisible(true);
} else {
selection_handle_2_->SetVisible(false);
@@ -373,12 +232,6 @@
}
}
-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.
@@ -423,21 +276,44 @@
client_view_->ExecuteCommand(command_id);
}
+void TouchSelectionControllerImpl::OpenContextMenu() {
+ gfx::Point anchor = context_menu_->anchor_point();
+ HideContextMenu();
+ client_view_->OpenContextMenu(anchor);
+}
+
+void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
+ if (menu == context_menu_)
+ context_menu_ = NULL;
+}
+
+void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
+ DCHECK_EQ(client_widget_, widget);
+ client_widget_ = NULL;
+}
+
+void TouchSelectionControllerImpl::OnWidgetBoundsChanged(
+ Widget* widget,
+ const gfx::Rect& new_bounds) {
+ DCHECK_EQ(client_widget_, widget);
+ HideContextMenu();
+ SelectionChanged();
+}
+
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);
+ gfx::Rect r1, r2;
+ client_view_->GetSelectionEndPoints(&r1, &r2);
// 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;
gfx::Rect client_bounds = client_view_->GetBounds();
- if (client_bounds.Contains(p1) && client_bounds.Contains(p2)) {
- menu_pos.set_x((p1.x() + p2.x()) / 2);
- menu_pos.set_y(std::min(p1.y(), p2.y()) - kContextMenuVerticalOffset);
+ if (client_bounds.Contains(r1.origin()) &&
+ client_bounds.Contains(r2.origin())) {
+ menu_pos.set_x((r1.origin().x() + r2.origin().x()) / 2);
+ menu_pos.set_y(std::min(r1.y(), r2.y()) - kContextMenuVerticalOffset);
} else {
menu_pos.set_x(client_bounds.x() + client_bounds.width() / 2);
menu_pos.set_y(client_bounds.y());
@@ -445,8 +321,9 @@
client_view_->ConvertPointToScreen(&menu_pos);
- context_menu_->SetScreenPosition(menu_pos);
- context_menu_->SetVisible(true);
+ DCHECK(!context_menu_);
+ context_menu_ = new TouchEditingMenuView(this, menu_pos,
+ client_view_->GetNativeView());
}
void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1,
@@ -465,7 +342,9 @@
}
void TouchSelectionControllerImpl::HideContextMenu() {
- context_menu_->SetVisible(false);
+ if (context_menu_)
+ context_menu_->Close();
+ context_menu_ = NULL;
context_menu_timer_.Stop();
}
@@ -485,13 +364,17 @@
return selection_handle_2_->visible();
}
-} // namespace views
-
-namespace ui {
-
-TouchSelectionController* TouchSelectionController::create(
- TouchEditable* client_view) {
- return new views::TouchSelectionControllerImpl(client_view);
+ViewsTouchSelectionControllerFactory::ViewsTouchSelectionControllerFactory() {
}
-} // namespace ui
+ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create(
+ ui::TouchEditable* client_view) {
+#if defined(OS_CHROMEOS)
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableTouchEditing))
+ return new views::TouchSelectionControllerImpl(client_view);
+#endif
+ return NULL;
+}
+
+} // namespace views
diff --git a/ui/views/touchui/touch_selection_controller_impl.h b/ui/views/touchui/touch_selection_controller_impl.h
index 5d042e7..9e60a997 100644
--- a/ui/views/touchui/touch_selection_controller_impl.h
+++ b/ui/views/touchui/touch_selection_controller_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -8,6 +8,7 @@
#include "base/timer.h"
#include "ui/base/touch/touch_editing_controller.h"
#include "ui/gfx/point.h"
+#include "ui/views/touchui/touch_editing_menu.h"
#include "ui/views/view.h"
#include "ui/views/views_export.h"
@@ -16,7 +17,9 @@
// Touch specific implementation of TouchSelectionController. Responsible for
// displaying selection handles and menu elements relevant in a touch interface.
class VIEWS_EXPORT TouchSelectionControllerImpl
- : public ui::TouchSelectionController {
+ : public ui::TouchSelectionController,
+ public TouchEditingMenuController,
+ public WidgetObserver {
public:
// Use TextSelectionController::create().
explicit TouchSelectionControllerImpl(
@@ -25,15 +28,11 @@
virtual ~TouchSelectionControllerImpl();
// TextSelectionController.
- virtual void SelectionChanged(const gfx::Point& p1,
- const gfx::Point& p2) OVERRIDE;
-
- virtual void ClientViewLostFocus() OVERRIDE;
+ virtual void SelectionChanged() OVERRIDE;
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.
@@ -43,11 +42,18 @@
// 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;
+ // Overridden from TouchEditingMenuController.
+ virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE;
+ virtual void ExecuteCommand(int command_id) OVERRIDE;
+ virtual void OpenContextMenu() OVERRIDE;
+ virtual void OnMenuClosed(TouchEditingMenuView* menu) OVERRIDE;
- // Sends a context menu command to the client view.
- void ExecuteCommand(int command_id);
+ // Overridden from WidgetObserver. We will observe the widget backing the
+ // |client_view_| so that when its moved/resized, we can update the selection
+ // handles appropriately.
+ virtual void OnWidgetClosing(Widget* widget) OVERRIDE;
+ virtual void OnWidgetBoundsChanged(Widget* widget,
+ const gfx::Rect& new_bounds) OVERRIDE;
// Time to show context menu.
void ContextMenuTimerFired();
@@ -65,9 +71,10 @@
bool IsSelectionHandle2Visible();
ui::TouchEditable* client_view_;
+ Widget* client_widget_;
scoped_ptr<SelectionHandleView> selection_handle_1_;
scoped_ptr<SelectionHandleView> selection_handle_2_;
- scoped_ptr<TouchContextMenuView> context_menu_;
+ TouchEditingMenuView* 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
@@ -80,6 +87,16 @@
DISALLOW_COPY_AND_ASSIGN(TouchSelectionControllerImpl);
};
+class VIEWS_EXPORT ViewsTouchSelectionControllerFactory
+ : public ui::TouchSelectionControllerFactory {
+ public:
+ ViewsTouchSelectionControllerFactory();
+
+ // Overridden from ui::TouchSelectionControllerFactory.
+ virtual ui::TouchSelectionController* create(
+ ui::TouchEditable* client_view) OVERRIDE;
+};
+
} // namespace views
#endif // UI_UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 1a154558..81024839 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -309,6 +309,10 @@
'repeat_controller.h',
'round_rect_painter.cc',
'round_rect_painter.h',
+ 'touchui/touch_editing_menu.cc',
+ 'touchui/touch_editing_menu.h',
+ 'touchui/touch_selection_controller_impl.cc',
+ 'touchui/touch_selection_controller_impl.h',
'view.cc',
'view.h',
'view_constants.cc',
@@ -321,6 +325,7 @@
'view_text_utils.cc',
'view_text_utils.h',
'view_win.cc',
+ 'views_delegate.cc',
'views_delegate.h',
'widget/aero_tooltip_manager.cc',
'widget/aero_tooltip_manager.h',
diff --git a/ui/views/views_delegate.cc b/ui/views/views_delegate.cc
new file mode 100644
index 0000000..20cb88f9
--- /dev/null
+++ b/ui/views/views_delegate.cc
@@ -0,0 +1,20 @@
+// 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/views_delegate.h"
+
+#include "ui/views/touchui/touch_selection_controller_impl.h"
+
+namespace views {
+
+ViewsDelegate::ViewsDelegate()
+ : views_tsc_factory_(new views::ViewsTouchSelectionControllerFactory) {
+ ui::TouchSelectionControllerFactory::SetInstance(views_tsc_factory_.get());
+}
+
+ViewsDelegate::~ViewsDelegate() {
+ ui::TouchSelectionControllerFactory::SetInstance(NULL);
+}
+
+} // namespace views
diff --git a/ui/views/views_delegate.h b/ui/views/views_delegate.h
index 105bab04..72aaeeaa 100644
--- a/ui/views/views_delegate.h
+++ b/ui/views/views_delegate.h
@@ -32,6 +32,7 @@
class NativeWidget;
class NonClientFrameView;
+class ViewsTouchSelectionControllerFactory;
class View;
class Widget;
namespace internal {
@@ -49,7 +50,9 @@
// The active ViewsDelegate used by the views system.
static ViewsDelegate* views_delegate;
- virtual ~ViewsDelegate() {}
+ ViewsDelegate();
+
+ virtual ~ViewsDelegate();
// Saves the position, size and "show" state for the window with the
// specified name.
@@ -106,6 +109,9 @@
// Gives the platform a chance to modify the properties of a Widget.
virtual void OnBeforeWidgetInit(Widget::InitParams* params,
internal::NativeWidgetDelegate* delegate) = 0;
+
+ private:
+ scoped_ptr<ViewsTouchSelectionControllerFactory> views_tsc_factory_;
};
} // namespace views