1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // 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
7 #include "base/time/time.h"
8 #include "grit/ui_resources.h"
9 #include "grit/ui_strings.h"
10 #include "ui/aura/client/cursor_client.h"
11 #include "ui/aura/env.h"
12 #include "ui/aura/window.h"
13 #include "ui/base/resource/resource_bundle.h"
14 #include "ui/gfx/canvas.h"
15 #include "ui/gfx/image/image.h"
16 #include "ui/gfx/path.h"
17 #include "ui/gfx/rect.h"
18 #include "ui/gfx/screen.h"
19 #include "ui/gfx/size.h"
20 #include "ui/views/widget/widget.h"
21 #include "ui/wm/core/masked_window_targeter.h"
22 #include "ui/wm/core/window_animations.h"
23
24 namespace {
25
26 // Constants defining the visual attributes of selection handles
27 const int kSelectionHandleLineWidth = 1;
28 const SkColor kSelectionHandleLineColor =
29 SkColorSetRGB(0x42, 0x81, 0xf4);
30
31 // When a handle is dragged, the drag position reported to the client view is
32 // offset vertically to represent the cursor position. This constant specifies
33 // the offset in pixels above the "O" (see pic below). This is required because
34 // say if this is zero, that means the drag position we report is the point
35 // right above the "O" or the bottom most point of the cursor "|". In that case,
36 // a vertical movement of even one pixel will make the handle jump to the line
37 // below it. So when the user just starts dragging, the handle will jump to the
38 // next line if the user makes any vertical movement. It is correct but
39 // looks/feels weird. So we have this non-zero offset to prevent this jumping.
40 //
41 // Editing handle widget showing the difference between the position of the
42 // ET_GESTURE_SCROLL_UPDATE event and the drag position reported to the client:
43 // _____
44 // | |<-|---- Drag position reported to client
45 // _ | O |
46 // Vertical Padding __| | <-|---- ET_GESTURE_SCROLL_UPDATE position
47 // |_ |_____|<--- Editing handle widget
48 //
49 // | |
50 // T
51 // Horizontal Padding
52 //
53 const int kSelectionHandleVerticalDragOffset = 5;
54
55 // Padding around the selection handle defining the area that will be included
56 // in the touch target to make dragging the handle easier (see pic above).
57 const int kSelectionHandleHorizPadding = 10;
58 const int kSelectionHandleVertPadding = 20;
59
60 const int kContextMenuTimoutMs = 200;
61
62 const int kSelectionHandleQuickFadeDurationMs = 50;
63
64 // Minimum height for selection handle bar. If the bar height is going to be
65 // less than this value, handle will not be shown.
66 const int kSelectionHandleBarMinHeight = 5;
67 // Maximum amount that selection handle bar can stick out of client view's
68 // boundaries.
69 const int kSelectionHandleBarBottomAllowance = 3;
70
71 // Creates a widget to host SelectionHandleView.
CreateTouchSelectionPopupWidget(gfx::NativeView context,views::WidgetDelegate * widget_delegate)72 views::Widget* CreateTouchSelectionPopupWidget(
73 gfx::NativeView context,
74 views::WidgetDelegate* widget_delegate) {
75 views::Widget* widget = new views::Widget;
76 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
77 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
78 params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE;
79 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
80 params.parent = context;
81 params.delegate = widget_delegate;
82 widget->Init(params);
83 return widget;
84 }
85
GetHandleImage()86 gfx::Image* GetHandleImage() {
87 static gfx::Image* handle_image = NULL;
88 if (!handle_image) {
89 handle_image = &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
90 IDR_TEXT_SELECTION_HANDLE);
91 }
92 return handle_image;
93 }
94
GetHandleImageSize()95 gfx::Size GetHandleImageSize() {
96 return GetHandleImage()->Size();
97 }
98
99 // Cannot use gfx::UnionRect since it does not work for empty rects.
Union(const gfx::Rect & r1,const gfx::Rect & r2)100 gfx::Rect Union(const gfx::Rect& r1, const gfx::Rect& r2) {
101 int rx = std::min(r1.x(), r2.x());
102 int ry = std::min(r1.y(), r2.y());
103 int rr = std::max(r1.right(), r2.right());
104 int rb = std::max(r1.bottom(), r2.bottom());
105
106 return gfx::Rect(rx, ry, rr - rx, rb - ry);
107 }
108
109 // Convenience methods to convert a |rect| from screen to the |client|'s
110 // coordinate system and vice versa.
111 // Note that this is not quite correct because it does not take into account
112 // transforms such as rotation and scaling. This should be in TouchEditable.
113 // TODO(varunjain): Fix this.
ConvertFromScreen(ui::TouchEditable * client,const gfx::Rect & rect)114 gfx::Rect ConvertFromScreen(ui::TouchEditable* client, const gfx::Rect& rect) {
115 gfx::Point origin = rect.origin();
116 client->ConvertPointFromScreen(&origin);
117 return gfx::Rect(origin, rect.size());
118 }
ConvertToScreen(ui::TouchEditable * client,const gfx::Rect & rect)119 gfx::Rect ConvertToScreen(ui::TouchEditable* client, const gfx::Rect& rect) {
120 gfx::Point origin = rect.origin();
121 client->ConvertPointToScreen(&origin);
122 return gfx::Rect(origin, rect.size());
123 }
124
125 } // namespace
126
127 namespace views {
128
129 typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView;
130
131 class TouchHandleWindowTargeter : public wm::MaskedWindowTargeter {
132 public:
133 TouchHandleWindowTargeter(aura::Window* window,
134 EditingHandleView* handle_view);
135
~TouchHandleWindowTargeter()136 virtual ~TouchHandleWindowTargeter() {}
137
138 private:
139 // wm::MaskedWindowTargeter:
140 virtual bool GetHitTestMask(aura::Window* window,
141 gfx::Path* mask) const OVERRIDE;
142
143 EditingHandleView* handle_view_;
144
145 DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter);
146 };
147
148 // A View that displays the text selection handle.
149 class TouchSelectionControllerImpl::EditingHandleView
150 : public views::WidgetDelegateView {
151 public:
EditingHandleView(TouchSelectionControllerImpl * controller,gfx::NativeView context)152 EditingHandleView(TouchSelectionControllerImpl* controller,
153 gfx::NativeView context)
154 : controller_(controller),
155 drag_offset_(0),
156 draw_invisible_(false) {
157 widget_.reset(CreateTouchSelectionPopupWidget(context, this));
158 widget_->SetContentsView(this);
159
160 aura::Window* window = widget_->GetNativeWindow();
161 window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
162 new TouchHandleWindowTargeter(window, this)));
163
164 // We are owned by the TouchSelectionController.
165 set_owned_by_client();
166 }
167
~EditingHandleView()168 virtual ~EditingHandleView() {
169 SetWidgetVisible(false, false);
170 }
171
172 // Overridden from views::WidgetDelegateView:
WidgetHasHitTestMask() const173 virtual bool WidgetHasHitTestMask() const OVERRIDE {
174 return true;
175 }
176
GetWidgetHitTestMask(gfx::Path * mask) const177 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE {
178 gfx::Size image_size = GetHandleImageSize();
179 mask->addRect(SkIntToScalar(0), SkIntToScalar(selection_rect_.height()),
180 SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding,
181 SkIntToScalar(selection_rect_.height() + image_size.height() +
182 kSelectionHandleVertPadding));
183 }
184
DeleteDelegate()185 virtual void DeleteDelegate() OVERRIDE {
186 // We are owned and deleted by TouchSelectionController.
187 }
188
189 // Overridden from views::View:
OnPaint(gfx::Canvas * canvas)190 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
191 if (draw_invisible_)
192 return;
193 gfx::Size image_size = GetHandleImageSize();
194 int cursor_pos_x = image_size.width() / 2 - kSelectionHandleLineWidth +
195 kSelectionHandleHorizPadding;
196
197 // Draw the cursor line.
198 canvas->FillRect(
199 gfx::Rect(cursor_pos_x, 0,
200 2 * kSelectionHandleLineWidth + 1, selection_rect_.height()),
201 kSelectionHandleLineColor);
202
203 // Draw the handle image.
204 canvas->DrawImageInt(*GetHandleImage()->ToImageSkia(),
205 kSelectionHandleHorizPadding, selection_rect_.height());
206 }
207
OnGestureEvent(ui::GestureEvent * event)208 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
209 event->SetHandled();
210 switch (event->type()) {
211 case ui::ET_GESTURE_SCROLL_BEGIN:
212 widget_->SetCapture(this);
213 controller_->SetDraggingHandle(this);
214 drag_offset_ = event->y() - selection_rect_.height() +
215 kSelectionHandleVerticalDragOffset;
216 break;
217 case ui::ET_GESTURE_SCROLL_UPDATE: {
218 gfx::Point drag_pos(event->location().x(),
219 event->location().y() - drag_offset_);
220 controller_->SelectionHandleDragged(drag_pos);
221 break;
222 }
223 case ui::ET_GESTURE_SCROLL_END:
224 case ui::ET_SCROLL_FLING_START:
225 widget_->ReleaseCapture();
226 controller_->SetDraggingHandle(NULL);
227 break;
228 default:
229 break;
230 }
231 }
232
GetPreferredSize() const233 virtual gfx::Size GetPreferredSize() const OVERRIDE {
234 gfx::Size image_size = GetHandleImageSize();
235 return gfx::Size(image_size.width() + 2 * kSelectionHandleHorizPadding,
236 image_size.height() + selection_rect_.height() +
237 kSelectionHandleVertPadding);
238 }
239
IsWidgetVisible() const240 bool IsWidgetVisible() const {
241 return widget_->IsVisible();
242 }
243
SetWidgetVisible(bool visible,bool quick)244 void SetWidgetVisible(bool visible, bool quick) {
245 if (widget_->IsVisible() == visible)
246 return;
247 wm::SetWindowVisibilityAnimationDuration(
248 widget_->GetNativeView(),
249 base::TimeDelta::FromMilliseconds(
250 quick ? kSelectionHandleQuickFadeDurationMs : 0));
251 if (visible)
252 widget_->Show();
253 else
254 widget_->Hide();
255 }
256
SetSelectionRectInScreen(const gfx::Rect & rect)257 void SetSelectionRectInScreen(const gfx::Rect& rect) {
258 gfx::Size image_size = GetHandleImageSize();
259 selection_rect_ = rect;
260 gfx::Rect widget_bounds(
261 rect.x() - image_size.width() / 2 - kSelectionHandleHorizPadding,
262 rect.y(),
263 image_size.width() + 2 * kSelectionHandleHorizPadding,
264 rect.height() + image_size.height() + kSelectionHandleVertPadding);
265 widget_->SetBounds(widget_bounds);
266 }
267
GetScreenPosition()268 gfx::Point GetScreenPosition() {
269 return widget_->GetClientAreaBoundsInScreen().origin();
270 }
271
SetDrawInvisible(bool draw_invisible)272 void SetDrawInvisible(bool draw_invisible) {
273 if (draw_invisible_ == draw_invisible)
274 return;
275 draw_invisible_ = draw_invisible;
276 SchedulePaint();
277 }
278
selection_rect() const279 const gfx::Rect& selection_rect() const { return selection_rect_; }
280
281 private:
282 scoped_ptr<Widget> widget_;
283 TouchSelectionControllerImpl* controller_;
284 gfx::Rect selection_rect_;
285
286 // Vertical offset between the scroll event position and the drag position
287 // reported to the client view (see the ASCII figure at the top of the file
288 // and its description for more details).
289 int drag_offset_;
290
291 // If set to true, the handle will not draw anything, hence providing an empty
292 // widget. We need this because we may want to stop showing the handle while
293 // it is being dragged. Since it is being dragged, we cannot destroy the
294 // handle.
295 bool draw_invisible_;
296
297 DISALLOW_COPY_AND_ASSIGN(EditingHandleView);
298 };
299
TouchHandleWindowTargeter(aura::Window * window,EditingHandleView * handle_view)300 TouchHandleWindowTargeter::TouchHandleWindowTargeter(
301 aura::Window* window,
302 EditingHandleView* handle_view)
303 : wm::MaskedWindowTargeter(window),
304 handle_view_(handle_view) {
305 }
306
GetHitTestMask(aura::Window * window,gfx::Path * mask) const307 bool TouchHandleWindowTargeter::GetHitTestMask(aura::Window* window,
308 gfx::Path* mask) const {
309 const gfx::Rect& selection_rect = handle_view_->selection_rect();
310 gfx::Size image_size = GetHandleImageSize();
311 mask->addRect(SkIntToScalar(0), SkIntToScalar(selection_rect.height()),
312 SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding,
313 SkIntToScalar(selection_rect.height() + image_size.height() +
314 kSelectionHandleVertPadding));
315 return true;
316 }
317
TouchSelectionControllerImpl(ui::TouchEditable * client_view)318 TouchSelectionControllerImpl::TouchSelectionControllerImpl(
319 ui::TouchEditable* client_view)
320 : client_view_(client_view),
321 client_widget_(NULL),
322 selection_handle_1_(new EditingHandleView(this,
323 client_view->GetNativeView())),
324 selection_handle_2_(new EditingHandleView(this,
325 client_view->GetNativeView())),
326 cursor_handle_(new EditingHandleView(this,
327 client_view->GetNativeView())),
328 context_menu_(NULL),
329 dragging_handle_(NULL) {
330 client_widget_ = Widget::GetTopLevelWidgetForNativeView(
331 client_view_->GetNativeView());
332 if (client_widget_)
333 client_widget_->AddObserver(this);
334 aura::Env::GetInstance()->AddPreTargetHandler(this);
335 }
336
~TouchSelectionControllerImpl()337 TouchSelectionControllerImpl::~TouchSelectionControllerImpl() {
338 HideContextMenu();
339 aura::Env::GetInstance()->RemovePreTargetHandler(this);
340 if (client_widget_)
341 client_widget_->RemoveObserver(this);
342 }
343
SelectionChanged()344 void TouchSelectionControllerImpl::SelectionChanged() {
345 gfx::Rect r1, r2;
346 client_view_->GetSelectionEndPoints(&r1, &r2);
347 gfx::Rect screen_rect_1 = ConvertToScreen(client_view_, r1);
348 gfx::Rect screen_rect_2 = ConvertToScreen(client_view_, r2);
349 gfx::Rect client_bounds = client_view_->GetBounds();
350 if (r1.y() < client_bounds.y())
351 r1.Inset(0, client_bounds.y() - r1.y(), 0, 0);
352 if (r2.y() < client_bounds.y())
353 r2.Inset(0, client_bounds.y() - r2.y(), 0, 0);
354 gfx::Rect screen_rect_1_clipped = ConvertToScreen(client_view_, r1);
355 gfx::Rect screen_rect_2_clipped = ConvertToScreen(client_view_, r2);
356 if (screen_rect_1_clipped == selection_end_point_1_clipped_ &&
357 screen_rect_2_clipped == selection_end_point_2_clipped_)
358 return;
359
360 selection_end_point_1_ = screen_rect_1;
361 selection_end_point_2_ = screen_rect_2;
362 selection_end_point_1_clipped_ = screen_rect_1_clipped;
363 selection_end_point_2_clipped_ = screen_rect_2_clipped;
364
365 if (client_view_->DrawsHandles()) {
366 UpdateContextMenu();
367 return;
368 }
369 if (dragging_handle_) {
370 // We need to reposition only the selection handle that is being dragged.
371 // The other handle stays the same. Also, the selection handle being dragged
372 // will always be at the end of selection, while the other handle will be at
373 // the start.
374 // If the new location of this handle is out of client view, its widget
375 // should not get hidden, since it should still receive touch events.
376 // Hence, we are not using |SetHandleSelectionRect()| method here.
377 dragging_handle_->SetSelectionRectInScreen(screen_rect_2_clipped);
378
379 // Temporary fix for selection handle going outside a window. On a webpage,
380 // the page should scroll if the selection handle is dragged outside the
381 // window. That does not happen currently. So we just hide the handle for
382 // now.
383 // TODO(varunjain): Fix this: crbug.com/269003
384 dragging_handle_->SetDrawInvisible(!ShouldShowHandleFor(r2));
385
386 if (dragging_handle_ != cursor_handle_.get()) {
387 // The non-dragging-handle might have recently become visible.
388 EditingHandleView* non_dragging_handle = selection_handle_1_.get();
389 if (dragging_handle_ == selection_handle_1_) {
390 non_dragging_handle = selection_handle_2_.get();
391 // if handle 1 is being dragged, it is corresponding to the end of
392 // selection and the other handle to the start of selection.
393 selection_end_point_1_ = screen_rect_2;
394 selection_end_point_2_ = screen_rect_1;
395 selection_end_point_1_clipped_ = screen_rect_2_clipped;
396 selection_end_point_2_clipped_ = screen_rect_1_clipped;
397 }
398 SetHandleSelectionRect(non_dragging_handle, r1, screen_rect_1_clipped);
399 }
400 } else {
401 UpdateContextMenu();
402
403 // Check if there is any selection at all.
404 if (screen_rect_1.origin() == screen_rect_2.origin()) {
405 selection_handle_1_->SetWidgetVisible(false, false);
406 selection_handle_2_->SetWidgetVisible(false, false);
407 SetHandleSelectionRect(cursor_handle_.get(), r1, screen_rect_1_clipped);
408 return;
409 }
410
411 cursor_handle_->SetWidgetVisible(false, false);
412 SetHandleSelectionRect(selection_handle_1_.get(), r1,
413 screen_rect_1_clipped);
414 SetHandleSelectionRect(selection_handle_2_.get(), r2,
415 screen_rect_2_clipped);
416 }
417 }
418
IsHandleDragInProgress()419 bool TouchSelectionControllerImpl::IsHandleDragInProgress() {
420 return !!dragging_handle_;
421 }
422
HideHandles(bool quick)423 void TouchSelectionControllerImpl::HideHandles(bool quick) {
424 selection_handle_1_->SetWidgetVisible(false, quick);
425 selection_handle_2_->SetWidgetVisible(false, quick);
426 cursor_handle_->SetWidgetVisible(false, quick);
427 }
428
SetDraggingHandle(EditingHandleView * handle)429 void TouchSelectionControllerImpl::SetDraggingHandle(
430 EditingHandleView* handle) {
431 dragging_handle_ = handle;
432 if (dragging_handle_)
433 HideContextMenu();
434 else
435 StartContextMenuTimer();
436 }
437
SelectionHandleDragged(const gfx::Point & drag_pos)438 void TouchSelectionControllerImpl::SelectionHandleDragged(
439 const gfx::Point& drag_pos) {
440 // We do not want to show the context menu while dragging.
441 HideContextMenu();
442
443 DCHECK(dragging_handle_);
444 gfx::Point drag_pos_in_client = drag_pos;
445 ConvertPointToClientView(dragging_handle_, &drag_pos_in_client);
446
447 if (dragging_handle_ == cursor_handle_.get()) {
448 client_view_->MoveCaretTo(drag_pos_in_client);
449 return;
450 }
451
452 // Find the stationary selection handle.
453 gfx::Rect fixed_handle_rect = selection_end_point_1_;
454 if (selection_handle_1_ == dragging_handle_)
455 fixed_handle_rect = selection_end_point_2_;
456
457 // Find selection end points in client_view's coordinate system.
458 gfx::Point p2 = fixed_handle_rect.origin();
459 p2.Offset(0, fixed_handle_rect.height() / 2);
460 client_view_->ConvertPointFromScreen(&p2);
461
462 // Instruct client_view to select the region between p1 and p2. The position
463 // of |fixed_handle| is the start and that of |dragging_handle| is the end
464 // of selection.
465 client_view_->SelectRect(p2, drag_pos_in_client);
466 }
467
ConvertPointToClientView(EditingHandleView * source,gfx::Point * point)468 void TouchSelectionControllerImpl::ConvertPointToClientView(
469 EditingHandleView* source, gfx::Point* point) {
470 View::ConvertPointToScreen(source, point);
471 client_view_->ConvertPointFromScreen(point);
472 }
473
SetHandleSelectionRect(EditingHandleView * handle,const gfx::Rect & rect,const gfx::Rect & rect_in_screen)474 void TouchSelectionControllerImpl::SetHandleSelectionRect(
475 EditingHandleView* handle,
476 const gfx::Rect& rect,
477 const gfx::Rect& rect_in_screen) {
478 handle->SetWidgetVisible(ShouldShowHandleFor(rect), false);
479 if (handle->IsWidgetVisible())
480 handle->SetSelectionRectInScreen(rect_in_screen);
481 }
482
ShouldShowHandleFor(const gfx::Rect & rect) const483 bool TouchSelectionControllerImpl::ShouldShowHandleFor(
484 const gfx::Rect& rect) const {
485 if (rect.height() < kSelectionHandleBarMinHeight)
486 return false;
487 gfx::Rect bounds = client_view_->GetBounds();
488 bounds.Inset(0, 0, 0, -kSelectionHandleBarBottomAllowance);
489 return bounds.Contains(rect);
490 }
491
IsCommandIdEnabled(int command_id) const492 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
493 return client_view_->IsCommandIdEnabled(command_id);
494 }
495
ExecuteCommand(int command_id,int event_flags)496 void TouchSelectionControllerImpl::ExecuteCommand(int command_id,
497 int event_flags) {
498 HideContextMenu();
499 client_view_->ExecuteCommand(command_id, event_flags);
500 }
501
OpenContextMenu()502 void TouchSelectionControllerImpl::OpenContextMenu() {
503 // Context menu should appear centered on top of the selected region.
504 const gfx::Rect rect = context_menu_->GetAnchorRect();
505 const gfx::Point anchor(rect.CenterPoint().x(), rect.y());
506 HideContextMenu();
507 client_view_->OpenContextMenu(anchor);
508 }
509
OnMenuClosed(TouchEditingMenuView * menu)510 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
511 if (menu == context_menu_)
512 context_menu_ = NULL;
513 }
514
OnWidgetClosing(Widget * widget)515 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
516 DCHECK_EQ(client_widget_, widget);
517 client_widget_ = NULL;
518 }
519
OnWidgetBoundsChanged(Widget * widget,const gfx::Rect & new_bounds)520 void TouchSelectionControllerImpl::OnWidgetBoundsChanged(
521 Widget* widget,
522 const gfx::Rect& new_bounds) {
523 DCHECK_EQ(client_widget_, widget);
524 HideContextMenu();
525 SelectionChanged();
526 }
527
OnKeyEvent(ui::KeyEvent * event)528 void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) {
529 client_view_->DestroyTouchSelection();
530 }
531
OnMouseEvent(ui::MouseEvent * event)532 void TouchSelectionControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
533 aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
534 client_view_->GetNativeView()->GetRootWindow());
535 if (!cursor_client || cursor_client->IsMouseEventsEnabled())
536 client_view_->DestroyTouchSelection();
537 }
538
OnScrollEvent(ui::ScrollEvent * event)539 void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
540 client_view_->DestroyTouchSelection();
541 }
542
ContextMenuTimerFired()543 void TouchSelectionControllerImpl::ContextMenuTimerFired() {
544 // Get selection end points in client_view's space.
545 gfx::Rect end_rect_1_in_screen;
546 gfx::Rect end_rect_2_in_screen;
547 if (cursor_handle_->IsWidgetVisible()) {
548 end_rect_1_in_screen = selection_end_point_1_clipped_;
549 end_rect_2_in_screen = end_rect_1_in_screen;
550 } else {
551 end_rect_1_in_screen = selection_end_point_1_clipped_;
552 end_rect_2_in_screen = selection_end_point_2_clipped_;
553 }
554
555 // Convert from screen to client.
556 gfx::Rect end_rect_1(ConvertFromScreen(client_view_, end_rect_1_in_screen));
557 gfx::Rect end_rect_2(ConvertFromScreen(client_view_, end_rect_2_in_screen));
558
559 // if selection is completely inside the view, we display the context menu
560 // in the middle of the end points on the top. Else, we show it above the
561 // visible handle. If no handle is visible, we do not show the menu.
562 gfx::Rect menu_anchor;
563 if (ShouldShowHandleFor(end_rect_1) &&
564 ShouldShowHandleFor(end_rect_2))
565 menu_anchor = Union(end_rect_1_in_screen,end_rect_2_in_screen);
566 else if (ShouldShowHandleFor(end_rect_1))
567 menu_anchor = end_rect_1_in_screen;
568 else if (ShouldShowHandleFor(end_rect_2))
569 menu_anchor = end_rect_2_in_screen;
570 else
571 return;
572
573 DCHECK(!context_menu_);
574 context_menu_ = TouchEditingMenuView::Create(this, menu_anchor,
575 GetHandleImageSize(),
576 client_view_->GetNativeView());
577 }
578
StartContextMenuTimer()579 void TouchSelectionControllerImpl::StartContextMenuTimer() {
580 if (context_menu_timer_.IsRunning())
581 return;
582 context_menu_timer_.Start(
583 FROM_HERE,
584 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
585 this,
586 &TouchSelectionControllerImpl::ContextMenuTimerFired);
587 }
588
UpdateContextMenu()589 void TouchSelectionControllerImpl::UpdateContextMenu() {
590 // Hide context menu to be shown when the timer fires.
591 HideContextMenu();
592 StartContextMenuTimer();
593 }
594
HideContextMenu()595 void TouchSelectionControllerImpl::HideContextMenu() {
596 if (context_menu_)
597 context_menu_->Close();
598 context_menu_ = NULL;
599 context_menu_timer_.Stop();
600 }
601
GetCursorHandleNativeView()602 gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() {
603 return cursor_handle_->GetWidget()->GetNativeView();
604 }
605
GetSelectionHandle1Position()606 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
607 return selection_handle_1_->GetScreenPosition();
608 }
609
GetSelectionHandle2Position()610 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() {
611 return selection_handle_2_->GetScreenPosition();
612 }
613
GetCursorHandlePosition()614 gfx::Point TouchSelectionControllerImpl::GetCursorHandlePosition() {
615 return cursor_handle_->GetScreenPosition();
616 }
617
IsSelectionHandle1Visible()618 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() {
619 return selection_handle_1_->IsWidgetVisible();
620 }
621
IsSelectionHandle2Visible()622 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() {
623 return selection_handle_2_->IsWidgetVisible();
624 }
625
IsCursorHandleVisible()626 bool TouchSelectionControllerImpl::IsCursorHandleVisible() {
627 return cursor_handle_->IsWidgetVisible();
628 }
629
630 } // namespace views
631