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 24namespace { 25 26// Constants defining the visual attributes of selection handles 27const int kSelectionHandleLineWidth = 1; 28const 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// 53const 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). 57const int kSelectionHandleHorizPadding = 10; 58const int kSelectionHandleVertPadding = 20; 59 60const int kContextMenuTimoutMs = 200; 61 62const 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. 66const int kSelectionHandleBarMinHeight = 5; 67// Maximum amount that selection handle bar can stick out of client view's 68// boundaries. 69const int kSelectionHandleBarBottomAllowance = 3; 70 71// Creates a widget to host SelectionHandleView. 72views::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 86gfx::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 95gfx::Size GetHandleImageSize() { 96 return GetHandleImage()->Size(); 97} 98 99// Cannot use gfx::UnionRect since it does not work for empty rects. 100gfx::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. 114gfx::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} 119gfx::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 127namespace views { 128 129typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView; 130 131class 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. 149class 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 300TouchHandleWindowTargeter::TouchHandleWindowTargeter( 301 aura::Window* window, 302 EditingHandleView* handle_view) 303 : wm::MaskedWindowTargeter(window), 304 handle_view_(handle_view) { 305} 306 307bool 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 318TouchSelectionControllerImpl::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 337TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { 338 HideContextMenu(); 339 aura::Env::GetInstance()->RemovePreTargetHandler(this); 340 if (client_widget_) 341 client_widget_->RemoveObserver(this); 342} 343 344void 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 419bool TouchSelectionControllerImpl::IsHandleDragInProgress() { 420 return !!dragging_handle_; 421} 422 423void 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 429void TouchSelectionControllerImpl::SetDraggingHandle( 430 EditingHandleView* handle) { 431 dragging_handle_ = handle; 432 if (dragging_handle_) 433 HideContextMenu(); 434 else 435 StartContextMenuTimer(); 436} 437 438void 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 468void TouchSelectionControllerImpl::ConvertPointToClientView( 469 EditingHandleView* source, gfx::Point* point) { 470 View::ConvertPointToScreen(source, point); 471 client_view_->ConvertPointFromScreen(point); 472} 473 474void 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 483bool 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 492bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { 493 return client_view_->IsCommandIdEnabled(command_id); 494} 495 496void TouchSelectionControllerImpl::ExecuteCommand(int command_id, 497 int event_flags) { 498 HideContextMenu(); 499 client_view_->ExecuteCommand(command_id, event_flags); 500} 501 502void 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 510void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) { 511 if (menu == context_menu_) 512 context_menu_ = NULL; 513} 514 515void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) { 516 DCHECK_EQ(client_widget_, widget); 517 client_widget_ = NULL; 518} 519 520void TouchSelectionControllerImpl::OnWidgetBoundsChanged( 521 Widget* widget, 522 const gfx::Rect& new_bounds) { 523 DCHECK_EQ(client_widget_, widget); 524 HideContextMenu(); 525 SelectionChanged(); 526} 527 528void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) { 529 client_view_->DestroyTouchSelection(); 530} 531 532void 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 539void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) { 540 client_view_->DestroyTouchSelection(); 541} 542 543void 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 579void 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 589void TouchSelectionControllerImpl::UpdateContextMenu() { 590 // Hide context menu to be shown when the timer fires. 591 HideContextMenu(); 592 StartContextMenuTimer(); 593} 594 595void TouchSelectionControllerImpl::HideContextMenu() { 596 if (context_menu_) 597 context_menu_->Close(); 598 context_menu_ = NULL; 599 context_menu_timer_.Stop(); 600} 601 602gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() { 603 return cursor_handle_->GetWidget()->GetNativeView(); 604} 605 606gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() { 607 return selection_handle_1_->GetScreenPosition(); 608} 609 610gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() { 611 return selection_handle_2_->GetScreenPosition(); 612} 613 614gfx::Point TouchSelectionControllerImpl::GetCursorHandlePosition() { 615 return cursor_handle_->GetScreenPosition(); 616} 617 618bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() { 619 return selection_handle_1_->IsWidgetVisible(); 620} 621 622bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() { 623 return selection_handle_2_->IsWidgetVisible(); 624} 625 626bool TouchSelectionControllerImpl::IsCursorHandleVisible() { 627 return cursor_handle_->IsWidgetVisible(); 628} 629 630} // namespace views 631