blob: 8b6b35116c8cb117edee8b9d82fa3acf8a5cbb79 [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// Copyright (c) 2013 The Chromium Authors. All rights reserved.
Torne (Richard Coles)58218062012-11-14 11:43:16 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/touchui/touch_selection_controller_impl.h"
6
Ben Murdocheb525c52013-07-10 11:40:50 +01007#include "base/time/time.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +01008#include "grit/ui_resources.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00009#include "grit/ui_strings.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010010#include "ui/base/resource/resource_bundle.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010011#include "ui/base/ui_base_switches_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000012#include "ui/gfx/canvas.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010013#include "ui/gfx/image/image.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010014#include "ui/gfx/path.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015#include "ui/gfx/rect.h"
16#include "ui/gfx/screen.h"
17#include "ui/gfx/size.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010018#include "ui/views/corewm/shadow_types.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019#include "ui/views/widget/widget.h"
20
21namespace {
22
23// Constants defining the visual attributes of selection handles
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010024const int kSelectionHandleLineWidth = 1;
25const SkColor kSelectionHandleLineColor =
26 SkColorSetRGB(0x42, 0x81, 0xf4);
27
Ben Murdochbb1529c2013-08-08 10:24:53 +010028// When a handle is dragged, the drag position reported to the client view is
29// offset vertically to represent the cursor position. This constant specifies
30// the offset in pixels above the "O" (see pic below). This is required because
31// say if this is zero, that means the drag position we report is the point
32// right above the "O" or the bottom most point of the cursor "|". In that case,
33// a vertical movement of even one pixel will make the handle jump to the line
34// below it. So when the user just starts dragging, the handle will jump to the
35// next line if the user makes any vertical movement. It is correct but
36// looks/feels weird. So we have this non-zero offset to prevent this jumping.
37//
38// Editing handle widget showing the difference between the position of the
39// ET_GESTURE_SCROLL_UPDATE event and the drag position reported to the client:
40// _____
41// | |<-|---- Drag position reported to client
42// _ | O |
43// Vertical Padding __| | <-|---- ET_GESTURE_SCROLL_UPDATE position
44// |_ |_____|<--- Editing handle widget
45//
46// | |
47// T
48// Horizontal Padding
49//
50const int kSelectionHandleVerticalDragOffset = 5;
51
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010052// Padding around the selection handle defining the area that will be included
Ben Murdochbb1529c2013-08-08 10:24:53 +010053// in the touch target to make dragging the handle easier (see pic above).
54const int kSelectionHandleHorizPadding = 10;
55const int kSelectionHandleVertPadding = 20;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000056
57// The minimum selection size to trigger selection controller.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010058// TODO(varunjain): Figure out if this is really required and get rid of it if
59// it isnt.
Ben Murdochbb1529c2013-08-08 10:24:53 +010060const int kMinSelectionSize = 1;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000061
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010062const int kContextMenuTimoutMs = 200;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000063
Torne (Richard Coles)58218062012-11-14 11:43:16 +000064// Creates a widget to host SelectionHandleView.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010065views::Widget* CreateTouchSelectionPopupWidget(
66 gfx::NativeView context,
67 views::WidgetDelegate* widget_delegate) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000068 views::Widget* widget = new views::Widget;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010069 views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000070 params.can_activate = false;
Ben Murdocheb525c52013-07-10 11:40:50 +010071 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000072 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000073 params.context = context;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010074 params.delegate = widget_delegate;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000075 widget->Init(params);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010076#if defined(USE_AURA)
77 SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE);
78#endif
Torne (Richard Coles)58218062012-11-14 11:43:16 +000079 return widget;
80}
81
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010082gfx::Image* GetHandleImage() {
83 static gfx::Image* handle_image = NULL;
84 if (!handle_image) {
85 handle_image = &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
86 IDR_TEXT_SELECTION_HANDLE);
87 }
88 return handle_image;
89}
90
91gfx::Size GetHandleImageSize() {
92 return GetHandleImage()->Size();
Torne (Richard Coles)58218062012-11-14 11:43:16 +000093}
94
95// The points may not match exactly, since the selection range computation may
96// introduce some floating point errors. So check for a minimum size to decide
97// whether or not there is any selection.
98bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) {
99 int delta_x = p2.x() - p1.x();
100 int delta_y = p2.y() - p1.y();
101 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize);
102}
103
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100104// Cannot use gfx::UnionRect since it does not work for empty rects.
105gfx::Rect Union(const gfx::Rect& r1, const gfx::Rect& r2) {
106 int rx = std::min(r1.x(), r2.x());
107 int ry = std::min(r1.y(), r2.y());
108 int rr = std::max(r1.right(), r2.right());
109 int rb = std::max(r1.bottom(), r2.bottom());
110
111 return gfx::Rect(rx, ry, rr - rx, rb - ry);
112}
113
114// Convenience method to convert a |rect| from screen to the |client|'s
115// coordinate system.
116// Note that this is not quite correct because it does not take into account
117// transforms such as rotation and scaling. This should be in TouchEditable.
118// TODO(varunjain): Fix this.
119gfx::Rect ConvertFromScreen(ui::TouchEditable* client, const gfx::Rect& rect) {
120 gfx::Point origin = rect.origin();
121 client->ConvertPointFromScreen(&origin);
122 return gfx::Rect(origin, rect.size());
123}
124
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000125} // namespace
126
127namespace views {
128
129// A View that displays the text selection handle.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100130class TouchSelectionControllerImpl::EditingHandleView
131 : public views::WidgetDelegateView {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000132 public:
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000133 explicit EditingHandleView(TouchSelectionControllerImpl* controller,
134 gfx::NativeView context)
Ben Murdochbb1529c2013-08-08 10:24:53 +0100135 : controller_(controller),
136 drag_offset_(0),
137 draw_invisible_(false) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100138 widget_.reset(CreateTouchSelectionPopupWidget(context, this));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000139 widget_->SetContentsView(this);
140 widget_->SetAlwaysOnTop(true);
141
142 // We are owned by the TouchSelectionController.
143 set_owned_by_client();
144 }
145
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000146 virtual ~EditingHandleView() {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000147 }
148
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100149 int cursor_height() const { return selection_rect_.height(); }
150
151 // Current selection end point that this handle marks in screen coordinates.
152 const gfx::Rect selection_rect() const { return selection_rect_; }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000153
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100154 // Overridden from views::WidgetDelegateView:
155 virtual bool WidgetHasHitTestMask() const OVERRIDE {
156 return true;
157 }
158
159 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100160 gfx::Size image_size = GetHandleImageSize();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100161 mask->addRect(SkIntToScalar(0), SkIntToScalar(cursor_height()),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100162 SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100163 SkIntToScalar(cursor_height() + image_size.height() +
Ben Murdochbb1529c2013-08-08 10:24:53 +0100164 kSelectionHandleVertPadding));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100165 }
166
167 virtual void DeleteDelegate() OVERRIDE {
168 // We are owned and deleted by TouchSelectionController.
169 }
170
171 // Overridden from views::View:
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000172 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100173 if (draw_invisible_)
174 return;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100175 gfx::Size image_size = GetHandleImageSize();
176 int cursor_pos_x = image_size.width() / 2 - kSelectionHandleLineWidth +
Ben Murdochbb1529c2013-08-08 10:24:53 +0100177 kSelectionHandleHorizPadding;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100178
179 // Draw the cursor line.
180 canvas->FillRect(
181 gfx::Rect(cursor_pos_x, 0,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100182 2 * kSelectionHandleLineWidth + 1, cursor_height()),
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100183 kSelectionHandleLineColor);
184
185 // Draw the handle image.
186 canvas->DrawImageInt(*GetHandleImage()->ToImageSkia(),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100187 kSelectionHandleHorizPadding, cursor_height());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000188 }
189
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000190 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
191 event->SetHandled();
192 switch (event->type()) {
193 case ui::ET_GESTURE_SCROLL_BEGIN:
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100194 widget_->SetCapture(this);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000195 controller_->SetDraggingHandle(this);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100196 drag_offset_ = event->y() - cursor_height() -
197 kSelectionHandleVerticalDragOffset;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000198 break;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100199 case ui::ET_GESTURE_SCROLL_UPDATE: {
200 gfx::Point drag_pos(event->location().x(),
201 event->location().y() - drag_offset_);
202 controller_->SelectionHandleDragged(drag_pos);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000203 break;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100204 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000205 case ui::ET_GESTURE_SCROLL_END:
Ben Murdochca12bfa2013-07-23 11:17:05 +0100206 case ui::ET_SCROLL_FLING_START:
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100207 widget_->ReleaseCapture();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000208 controller_->SetDraggingHandle(NULL);
209 break;
210 default:
211 break;
212 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000213 }
214
215 virtual void SetVisible(bool visible) OVERRIDE {
216 // We simply show/hide the container widget.
217 if (visible != widget_->IsVisible()) {
218 if (visible)
219 widget_->Show();
220 else
221 widget_->Hide();
222 }
223 View::SetVisible(visible);
224 }
225
226 virtual gfx::Size GetPreferredSize() OVERRIDE {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100227 gfx::Size image_size = GetHandleImageSize();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100228 return gfx::Size(image_size.width() + 2 * kSelectionHandleHorizPadding,
229 image_size.height() + cursor_height() + kSelectionHandleVertPadding);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000230 }
231
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000232 bool IsWidgetVisible() const {
233 return widget_->IsVisible();
234 }
235
236 void SetSelectionRectInScreen(const gfx::Rect& rect) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100237 gfx::Size image_size = GetHandleImageSize();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100238 selection_rect_ = rect;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100239 gfx::Rect widget_bounds(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100240 rect.x() - image_size.width() / 2 - kSelectionHandleHorizPadding,
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100241 rect.y(),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100242 image_size.width() + 2 * kSelectionHandleHorizPadding,
243 rect.height() + image_size.height() + kSelectionHandleVertPadding);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000244 widget_->SetBounds(widget_bounds);
245 }
246
247 gfx::Point GetScreenPosition() {
248 return widget_->GetClientAreaBoundsInScreen().origin();
249 }
250
Ben Murdochbb1529c2013-08-08 10:24:53 +0100251 void SetDrawInvisible(bool draw_invisible) {
252 if (draw_invisible_ == draw_invisible)
253 return;
254 draw_invisible_ = draw_invisible;
255 SchedulePaint();
256 }
257
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000258 private:
259 scoped_ptr<Widget> widget_;
260 TouchSelectionControllerImpl* controller_;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100261 gfx::Rect selection_rect_;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100262 int drag_offset_;
263
264 // If set to true, the handle will not draw anything, hence providing an empty
265 // widget. We need this because we may want to stop showing the handle while
266 // it is being dragged. Since it is being dragged, we cannot destroy the
267 // handle.
268 bool draw_invisible_;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000269
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000270 DISALLOW_COPY_AND_ASSIGN(EditingHandleView);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000271};
272
273TouchSelectionControllerImpl::TouchSelectionControllerImpl(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000274 ui::TouchEditable* client_view)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000275 : client_view_(client_view),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000276 client_widget_(NULL),
277 selection_handle_1_(new EditingHandleView(this,
278 client_view->GetNativeView())),
279 selection_handle_2_(new EditingHandleView(this,
280 client_view->GetNativeView())),
281 cursor_handle_(new EditingHandleView(this,
282 client_view->GetNativeView())),
283 context_menu_(NULL),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000284 dragging_handle_(NULL) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000285 client_widget_ = Widget::GetTopLevelWidgetForNativeView(
286 client_view_->GetNativeView());
287 if (client_widget_)
288 client_widget_->AddObserver(this);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000289}
290
291TouchSelectionControllerImpl::~TouchSelectionControllerImpl() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000292 HideContextMenu();
293 if (client_widget_)
294 client_widget_->RemoveObserver(this);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000295}
296
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000297void TouchSelectionControllerImpl::SelectionChanged() {
298 gfx::Rect r1, r2;
299 client_view_->GetSelectionEndPoints(&r1, &r2);
300 gfx::Point screen_pos_1(r1.origin());
301 client_view_->ConvertPointToScreen(&screen_pos_1);
302 gfx::Point screen_pos_2(r2.origin());
303 client_view_->ConvertPointToScreen(&screen_pos_2);
304 gfx::Rect screen_rect_1(screen_pos_1, r1.size());
305 gfx::Rect screen_rect_2(screen_pos_2, r2.size());
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100306 if (screen_rect_1 == selection_end_point_1 &&
307 screen_rect_2 == selection_end_point_2)
308 return;
309
310 selection_end_point_1 = screen_rect_1;
311 selection_end_point_2 = screen_rect_2;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000312
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000313 if (client_view_->DrawsHandles()) {
314 UpdateContextMenu(r1.origin(), r2.origin());
315 return;
316 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000317 if (dragging_handle_) {
318 // We need to reposition only the selection handle that is being dragged.
319 // The other handle stays the same. Also, the selection handle being dragged
320 // will always be at the end of selection, while the other handle will be at
321 // the start.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000322 dragging_handle_->SetSelectionRectInScreen(screen_rect_2);
323
Ben Murdochbb1529c2013-08-08 10:24:53 +0100324 // Temporary fix for selection handle going outside a window. On a webpage,
325 // the page should scroll if the selection handle is dragged outside the
326 // window. That does not happen currently. So we just hide the handle for
327 // now.
328 // TODO(varunjain): Fix this: crbug.com/269003
329 dragging_handle_->SetDrawInvisible(!client_view_->GetBounds().Contains(r2));
330
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000331 if (dragging_handle_ != cursor_handle_.get()) {
332 // The non-dragging-handle might have recently become visible.
333 EditingHandleView* non_dragging_handle =
334 dragging_handle_ == selection_handle_1_.get()?
335 selection_handle_2_.get() : selection_handle_1_.get();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100336 non_dragging_handle->SetSelectionRectInScreen(screen_rect_1);
337 non_dragging_handle->SetVisible(client_view_->GetBounds().Contains(r1));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000338 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000339 } else {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000340 UpdateContextMenu(r1.origin(), r2.origin());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000341
342 // Check if there is any selection at all.
343 if (IsEmptySelection(screen_pos_2, screen_pos_1)) {
344 selection_handle_1_->SetVisible(false);
345 selection_handle_2_->SetVisible(false);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000346 cursor_handle_->SetSelectionRectInScreen(screen_rect_1);
347 cursor_handle_->SetVisible(true);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000348 return;
349 }
350
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000351 cursor_handle_->SetVisible(false);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100352 selection_handle_1_->SetSelectionRectInScreen(screen_rect_1);
353 selection_handle_1_->SetVisible(client_view_->GetBounds().Contains(r1));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000354
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100355 selection_handle_2_->SetSelectionRectInScreen(screen_rect_2);
356 selection_handle_2_->SetVisible(client_view_->GetBounds().Contains(r2));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000357 }
358}
359
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100360bool TouchSelectionControllerImpl::IsHandleDragInProgress() {
361 return !!dragging_handle_;
362}
363
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000364void TouchSelectionControllerImpl::SetDraggingHandle(
365 EditingHandleView* handle) {
366 dragging_handle_ = handle;
367 if (dragging_handle_)
368 HideContextMenu();
369 else
370 StartContextMenuTimer();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000371}
372
373void TouchSelectionControllerImpl::SelectionHandleDragged(
374 const gfx::Point& drag_pos) {
375 // We do not want to show the context menu while dragging.
376 HideContextMenu();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000377
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000378 DCHECK(dragging_handle_);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100379 gfx::Point drag_pos_in_client = drag_pos;
380 ConvertPointToClientView(dragging_handle_, &drag_pos_in_client);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000381
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000382 if (dragging_handle_ == cursor_handle_.get()) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100383 client_view_->MoveCaretTo(drag_pos_in_client);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000384 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000385 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000386
387 // Find the stationary selection handle.
388 EditingHandleView* fixed_handle = selection_handle_1_.get();
389 if (fixed_handle == dragging_handle_)
390 fixed_handle = selection_handle_2_.get();
391
392 // Find selection end points in client_view's coordinate system.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100393 gfx::Point p2 = fixed_handle->selection_rect().origin();
394 p2.Offset(0, fixed_handle->cursor_height() / 2);
395 client_view_->ConvertPointFromScreen(&p2);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000396
397 // Instruct client_view to select the region between p1 and p2. The position
398 // of |fixed_handle| is the start and that of |dragging_handle| is the end
399 // of selection.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100400 client_view_->SelectRect(p2, drag_pos_in_client);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000401}
402
403void TouchSelectionControllerImpl::ConvertPointToClientView(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000404 EditingHandleView* source, gfx::Point* point) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000405 View::ConvertPointToScreen(source, point);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000406 client_view_->ConvertPointFromScreen(point);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000407}
408
409bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
410 return client_view_->IsCommandIdEnabled(command_id);
411}
412
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000413void TouchSelectionControllerImpl::ExecuteCommand(int command_id,
414 int event_flags) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000415 HideContextMenu();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000416 client_view_->ExecuteCommand(command_id, event_flags);
417}
418
419void TouchSelectionControllerImpl::OpenContextMenu() {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100420 // Context menu should appear centered on top of the selected region.
421 gfx::Point anchor(context_menu_->anchor_rect().CenterPoint().x(),
422 context_menu_->anchor_rect().y());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000423 HideContextMenu();
424 client_view_->OpenContextMenu(anchor);
425}
426
427void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
428 if (menu == context_menu_)
429 context_menu_ = NULL;
430}
431
432void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
433 DCHECK_EQ(client_widget_, widget);
434 client_widget_ = NULL;
435}
436
437void TouchSelectionControllerImpl::OnWidgetBoundsChanged(
438 Widget* widget,
439 const gfx::Rect& new_bounds) {
440 DCHECK_EQ(client_widget_, widget);
441 HideContextMenu();
442 SelectionChanged();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000443}
444
445void TouchSelectionControllerImpl::ContextMenuTimerFired() {
446 // Get selection end points in client_view's space.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100447 gfx::Rect end_rect_1_in_screen;
448 gfx::Rect end_rect_2_in_screen;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100449 if (cursor_handle_->IsWidgetVisible()) {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100450 end_rect_1_in_screen = cursor_handle_->selection_rect();
451 end_rect_2_in_screen = end_rect_1_in_screen;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100452 } else {
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100453 end_rect_1_in_screen = selection_handle_1_->selection_rect();
454 end_rect_2_in_screen = selection_handle_2_->selection_rect();
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100455 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000456
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100457 // Convert from screen to client.
458 gfx::Rect end_rect_1(ConvertFromScreen(client_view_, end_rect_1_in_screen));
459 gfx::Rect end_rect_2(ConvertFromScreen(client_view_, end_rect_2_in_screen));
460
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000461 // if selection is completely inside the view, we display the context menu
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000462 // in the middle of the end points on the top. Else, we show it above the
463 // visible handle. If no handle is visible, we do not show the menu.
464 gfx::Rect menu_anchor;
465 gfx::Rect client_bounds = client_view_->GetBounds();
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100466 if (client_bounds.Contains(end_rect_1) &&
467 client_bounds.Contains(end_rect_2))
468 menu_anchor = Union(end_rect_1_in_screen,end_rect_2_in_screen);
469 else if (client_bounds.Contains(end_rect_1))
470 menu_anchor = end_rect_1_in_screen;
471 else if (client_bounds.Contains(end_rect_2))
472 menu_anchor = end_rect_2_in_screen;
473 else
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000474 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000475
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000476 DCHECK(!context_menu_);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100477 context_menu_ = TouchEditingMenuView::Create(this, menu_anchor,
478 client_view_->GetNativeView());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000479}
480
481void TouchSelectionControllerImpl::StartContextMenuTimer() {
482 if (context_menu_timer_.IsRunning())
483 return;
484 context_menu_timer_.Start(
485 FROM_HERE,
486 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
487 this,
488 &TouchSelectionControllerImpl::ContextMenuTimerFired);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000489}
490
491void TouchSelectionControllerImpl::UpdateContextMenu(const gfx::Point& p1,
492 const gfx::Point& p2) {
493 // Hide context menu to be shown when the timer fires.
494 HideContextMenu();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000495 StartContextMenuTimer();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000496}
497
498void TouchSelectionControllerImpl::HideContextMenu() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000499 if (context_menu_)
500 context_menu_->Close();
501 context_menu_ = NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000502 context_menu_timer_.Stop();
503}
504
505gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
506 return selection_handle_1_->GetScreenPosition();
507}
508
509gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() {
510 return selection_handle_2_->GetScreenPosition();
511}
512
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000513gfx::Point TouchSelectionControllerImpl::GetCursorHandlePosition() {
514 return cursor_handle_->GetScreenPosition();
515}
516
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000517bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() {
518 return selection_handle_1_->visible();
519}
520
521bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() {
522 return selection_handle_2_->visible();
523}
524
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000525bool TouchSelectionControllerImpl::IsCursorHandleVisible() {
526 return cursor_handle_->visible();
527}
528
529ViewsTouchSelectionControllerFactory::ViewsTouchSelectionControllerFactory() {
530}
531
532ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create(
533 ui::TouchEditable* client_view) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100534 if (switches::IsTouchEditingEnabled())
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000535 return new views::TouchSelectionControllerImpl(client_view);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000536 return NULL;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000537}
538
539} // namespace views