| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |