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 "ui/aura/client/cursor_client.h" 9 #include "ui/aura/env.h" 10 #include "ui/aura/window.h" 11 #include "ui/base/resource/resource_bundle.h" 12 #include "ui/gfx/canvas.h" 13 #include "ui/gfx/image/image.h" 14 #include "ui/gfx/path.h" 15 #include "ui/gfx/rect.h" 16 #include "ui/gfx/screen.h" 17 #include "ui/gfx/size.h" 18 #include "ui/resources/grit/ui_resources.h" 19 #include "ui/strings/grit/ui_strings.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. 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 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 95 gfx::Size GetHandleImageSize() { 96 return GetHandleImage()->Size(); 97 } 98 99 // Cannot use gfx::UnionRect since it does not work for empty rects. 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. 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 } 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 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: 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 168 virtual ~EditingHandleView() { 169 SetWidgetVisible(false, false); 170 } 171 172 // Overridden from views::WidgetDelegateView: 173 virtual bool WidgetHasHitTestMask() const OVERRIDE { 174 return true; 175 } 176 177 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 185 virtual void DeleteDelegate() OVERRIDE { 186 // We are owned and deleted by TouchSelectionController. 187 } 188 189 // Overridden from views::View: 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 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 233 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 240 bool IsWidgetVisible() const { 241 return widget_->IsVisible(); 242 } 243 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 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 268 gfx::Point GetScreenPosition() { 269 return widget_->GetClientAreaBoundsInScreen().origin(); 270 } 271 272 void SetDrawInvisible(bool draw_invisible) { 273 if (draw_invisible_ == draw_invisible) 274 return; 275 draw_invisible_ = draw_invisible; 276 SchedulePaint(); 277 } 278 279 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 300 TouchHandleWindowTargeter::TouchHandleWindowTargeter( 301 aura::Window* window, 302 EditingHandleView* handle_view) 303 : wm::MaskedWindowTargeter(window), 304 handle_view_(handle_view) { 305 } 306 307 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 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 aura::Window* client_window = client_view_->GetNativeView(); 331 client_window->AddObserver(this); 332 client_widget_ = Widget::GetTopLevelWidgetForNativeView(client_window); 333 if (client_widget_) 334 client_widget_->AddObserver(this); 335 aura::Env::GetInstance()->AddPreTargetHandler(this); 336 } 337 338 TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { 339 HideContextMenu(); 340 aura::Env::GetInstance()->RemovePreTargetHandler(this); 341 if (client_widget_) 342 client_widget_->RemoveObserver(this); 343 client_view_->GetNativeView()->RemoveObserver(this); 344 } 345 346 void TouchSelectionControllerImpl::SelectionChanged() { 347 gfx::Rect r1, r2; 348 client_view_->GetSelectionEndPoints(&r1, &r2); 349 gfx::Rect screen_rect_1 = ConvertToScreen(client_view_, r1); 350 gfx::Rect screen_rect_2 = ConvertToScreen(client_view_, r2); 351 gfx::Rect client_bounds = client_view_->GetBounds(); 352 if (r1.y() < client_bounds.y()) 353 r1.Inset(0, client_bounds.y() - r1.y(), 0, 0); 354 if (r2.y() < client_bounds.y()) 355 r2.Inset(0, client_bounds.y() - r2.y(), 0, 0); 356 gfx::Rect screen_rect_1_clipped = ConvertToScreen(client_view_, r1); 357 gfx::Rect screen_rect_2_clipped = ConvertToScreen(client_view_, r2); 358 if (screen_rect_1_clipped == selection_end_point_1_clipped_ && 359 screen_rect_2_clipped == selection_end_point_2_clipped_) 360 return; 361 362 selection_end_point_1_ = screen_rect_1; 363 selection_end_point_2_ = screen_rect_2; 364 selection_end_point_1_clipped_ = screen_rect_1_clipped; 365 selection_end_point_2_clipped_ = screen_rect_2_clipped; 366 367 if (client_view_->DrawsHandles()) { 368 UpdateContextMenu(); 369 return; 370 } 371 if (dragging_handle_) { 372 // We need to reposition only the selection handle that is being dragged. 373 // The other handle stays the same. Also, the selection handle being dragged 374 // will always be at the end of selection, while the other handle will be at 375 // the start. 376 // If the new location of this handle is out of client view, its widget 377 // should not get hidden, since it should still receive touch events. 378 // Hence, we are not using |SetHandleSelectionRect()| method here. 379 dragging_handle_->SetSelectionRectInScreen(screen_rect_2_clipped); 380 381 // Temporary fix for selection handle going outside a window. On a webpage, 382 // the page should scroll if the selection handle is dragged outside the 383 // window. That does not happen currently. So we just hide the handle for 384 // now. 385 // TODO(varunjain): Fix this: crbug.com/269003 386 dragging_handle_->SetDrawInvisible(!ShouldShowHandleFor(r2)); 387 388 if (dragging_handle_ != cursor_handle_.get()) { 389 // The non-dragging-handle might have recently become visible. 390 EditingHandleView* non_dragging_handle = selection_handle_1_.get(); 391 if (dragging_handle_ == selection_handle_1_) { 392 non_dragging_handle = selection_handle_2_.get(); 393 // if handle 1 is being dragged, it is corresponding to the end of 394 // selection and the other handle to the start of selection. 395 selection_end_point_1_ = screen_rect_2; 396 selection_end_point_2_ = screen_rect_1; 397 selection_end_point_1_clipped_ = screen_rect_2_clipped; 398 selection_end_point_2_clipped_ = screen_rect_1_clipped; 399 } 400 SetHandleSelectionRect(non_dragging_handle, r1, screen_rect_1_clipped); 401 } 402 } else { 403 UpdateContextMenu(); 404 405 // Check if there is any selection at all. 406 if (screen_rect_1.origin() == screen_rect_2.origin()) { 407 selection_handle_1_->SetWidgetVisible(false, false); 408 selection_handle_2_->SetWidgetVisible(false, false); 409 SetHandleSelectionRect(cursor_handle_.get(), r1, screen_rect_1_clipped); 410 return; 411 } 412 413 cursor_handle_->SetWidgetVisible(false, false); 414 SetHandleSelectionRect(selection_handle_1_.get(), r1, 415 screen_rect_1_clipped); 416 SetHandleSelectionRect(selection_handle_2_.get(), r2, 417 screen_rect_2_clipped); 418 } 419 } 420 421 bool TouchSelectionControllerImpl::IsHandleDragInProgress() { 422 return !!dragging_handle_; 423 } 424 425 void TouchSelectionControllerImpl::HideHandles(bool quick) { 426 selection_handle_1_->SetWidgetVisible(false, quick); 427 selection_handle_2_->SetWidgetVisible(false, quick); 428 cursor_handle_->SetWidgetVisible(false, quick); 429 } 430 431 void TouchSelectionControllerImpl::SetDraggingHandle( 432 EditingHandleView* handle) { 433 dragging_handle_ = handle; 434 if (dragging_handle_) 435 HideContextMenu(); 436 else 437 StartContextMenuTimer(); 438 } 439 440 void TouchSelectionControllerImpl::SelectionHandleDragged( 441 const gfx::Point& drag_pos) { 442 DCHECK(dragging_handle_); 443 gfx::Point drag_pos_in_client = drag_pos; 444 ConvertPointToClientView(dragging_handle_, &drag_pos_in_client); 445 446 if (dragging_handle_ == cursor_handle_.get()) { 447 client_view_->MoveCaretTo(drag_pos_in_client); 448 return; 449 } 450 451 // Find the stationary selection handle. 452 gfx::Rect fixed_handle_rect = selection_end_point_1_; 453 if (selection_handle_1_ == dragging_handle_) 454 fixed_handle_rect = selection_end_point_2_; 455 456 // Find selection end points in client_view's coordinate system. 457 gfx::Point p2 = fixed_handle_rect.origin(); 458 p2.Offset(0, fixed_handle_rect.height() / 2); 459 client_view_->ConvertPointFromScreen(&p2); 460 461 // Instruct client_view to select the region between p1 and p2. The position 462 // of |fixed_handle| is the start and that of |dragging_handle| is the end 463 // of selection. 464 client_view_->SelectRect(p2, drag_pos_in_client); 465 } 466 467 void TouchSelectionControllerImpl::ConvertPointToClientView( 468 EditingHandleView* source, gfx::Point* point) { 469 View::ConvertPointToScreen(source, point); 470 client_view_->ConvertPointFromScreen(point); 471 } 472 473 void TouchSelectionControllerImpl::SetHandleSelectionRect( 474 EditingHandleView* handle, 475 const gfx::Rect& rect, 476 const gfx::Rect& rect_in_screen) { 477 handle->SetWidgetVisible(ShouldShowHandleFor(rect), false); 478 if (handle->IsWidgetVisible()) 479 handle->SetSelectionRectInScreen(rect_in_screen); 480 } 481 482 bool TouchSelectionControllerImpl::ShouldShowHandleFor( 483 const gfx::Rect& rect) const { 484 if (rect.height() < kSelectionHandleBarMinHeight) 485 return false; 486 gfx::Rect bounds = client_view_->GetBounds(); 487 bounds.Inset(0, 0, 0, -kSelectionHandleBarBottomAllowance); 488 return bounds.Contains(rect); 489 } 490 491 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { 492 return client_view_->IsCommandIdEnabled(command_id); 493 } 494 495 void TouchSelectionControllerImpl::ExecuteCommand(int command_id, 496 int event_flags) { 497 HideContextMenu(); 498 client_view_->ExecuteCommand(command_id, event_flags); 499 } 500 501 void TouchSelectionControllerImpl::OpenContextMenu() { 502 // Context menu should appear centered on top of the selected region. 503 const gfx::Rect rect = context_menu_->GetAnchorRect(); 504 const gfx::Point anchor(rect.CenterPoint().x(), rect.y()); 505 HideContextMenu(); 506 client_view_->OpenContextMenu(anchor); 507 } 508 509 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) { 510 if (menu == context_menu_) 511 context_menu_ = NULL; 512 } 513 514 void TouchSelectionControllerImpl::OnAncestorWindowTransformed( 515 aura::Window* window, 516 aura::Window* ancestor) { 517 client_view_->DestroyTouchSelection(); 518 } 519 520 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) { 521 DCHECK_EQ(client_widget_, widget); 522 client_widget_ = NULL; 523 } 524 525 void TouchSelectionControllerImpl::OnWidgetBoundsChanged( 526 Widget* widget, 527 const gfx::Rect& new_bounds) { 528 DCHECK_EQ(client_widget_, widget); 529 SelectionChanged(); 530 } 531 532 void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) { 533 client_view_->DestroyTouchSelection(); 534 } 535 536 void TouchSelectionControllerImpl::OnMouseEvent(ui::MouseEvent* event) { 537 aura::client::CursorClient* cursor_client = aura::client::GetCursorClient( 538 client_view_->GetNativeView()->GetRootWindow()); 539 if (!cursor_client || cursor_client->IsMouseEventsEnabled()) 540 client_view_->DestroyTouchSelection(); 541 } 542 543 void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) { 544 client_view_->DestroyTouchSelection(); 545 } 546 547 void TouchSelectionControllerImpl::ContextMenuTimerFired() { 548 // Get selection end points in client_view's space. 549 gfx::Rect end_rect_1_in_screen; 550 gfx::Rect end_rect_2_in_screen; 551 if (cursor_handle_->IsWidgetVisible()) { 552 end_rect_1_in_screen = selection_end_point_1_clipped_; 553 end_rect_2_in_screen = end_rect_1_in_screen; 554 } else { 555 end_rect_1_in_screen = selection_end_point_1_clipped_; 556 end_rect_2_in_screen = selection_end_point_2_clipped_; 557 } 558 559 // Convert from screen to client. 560 gfx::Rect end_rect_1(ConvertFromScreen(client_view_, end_rect_1_in_screen)); 561 gfx::Rect end_rect_2(ConvertFromScreen(client_view_, end_rect_2_in_screen)); 562 563 // if selection is completely inside the view, we display the context menu 564 // in the middle of the end points on the top. Else, we show it above the 565 // visible handle. If no handle is visible, we do not show the menu. 566 gfx::Rect menu_anchor; 567 if (ShouldShowHandleFor(end_rect_1) && 568 ShouldShowHandleFor(end_rect_2)) 569 menu_anchor = Union(end_rect_1_in_screen,end_rect_2_in_screen); 570 else if (ShouldShowHandleFor(end_rect_1)) 571 menu_anchor = end_rect_1_in_screen; 572 else if (ShouldShowHandleFor(end_rect_2)) 573 menu_anchor = end_rect_2_in_screen; 574 else 575 return; 576 577 DCHECK(!context_menu_); 578 context_menu_ = TouchEditingMenuView::Create(this, menu_anchor, 579 GetHandleImageSize(), 580 client_view_->GetNativeView()); 581 } 582 583 void TouchSelectionControllerImpl::StartContextMenuTimer() { 584 if (context_menu_timer_.IsRunning()) 585 return; 586 context_menu_timer_.Start( 587 FROM_HERE, 588 base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs), 589 this, 590 &TouchSelectionControllerImpl::ContextMenuTimerFired); 591 } 592 593 void TouchSelectionControllerImpl::UpdateContextMenu() { 594 // Hide context menu to be shown when the timer fires. 595 HideContextMenu(); 596 StartContextMenuTimer(); 597 } 598 599 void TouchSelectionControllerImpl::HideContextMenu() { 600 if (context_menu_) 601 context_menu_->Close(); 602 context_menu_ = NULL; 603 context_menu_timer_.Stop(); 604 } 605 606 gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() { 607 return cursor_handle_->GetWidget()->GetNativeView(); 608 } 609 610 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { 611 return selection_handle_1_->GetScreenPosition(); 612 } 613 614 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() { 615 return selection_handle_2_->GetScreenPosition(); 616 } 617 618 gfx::Point TouchSelectionControllerImpl::GetCursorHandlePosition() { 619 return cursor_handle_->GetScreenPosition(); 620 } 621 622 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() { 623 return selection_handle_1_->IsWidgetVisible(); 624 } 625 626 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() { 627 return selection_handle_2_->IsWidgetVisible(); 628 } 629 630 bool TouchSelectionControllerImpl::IsCursorHandleVisible() { 631 return cursor_handle_->IsWidgetVisible(); 632 } 633 634 } // namespace views 635