OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/touchui/touch_selection_controller_impl.h" | 5 #include "ui/views/touchui/touch_selection_controller_impl.h" |
6 | 6 |
7 #include "base/time.h" | 7 #include "base/time.h" |
8 #include "grit/ui_resources.h" | |
8 #include "grit/ui_strings.h" | 9 #include "grit/ui_strings.h" |
10 #include "ui/base/resource/resource_bundle.h" | |
9 #include "ui/base/ui_base_switches_util.h" | 11 #include "ui/base/ui_base_switches_util.h" |
10 #include "ui/gfx/canvas.h" | 12 #include "ui/gfx/canvas.h" |
13 #include "ui/gfx/image/image.h" | |
11 #include "ui/gfx/path.h" | 14 #include "ui/gfx/path.h" |
12 #include "ui/gfx/rect.h" | 15 #include "ui/gfx/rect.h" |
13 #include "ui/gfx/screen.h" | 16 #include "ui/gfx/screen.h" |
14 #include "ui/gfx/size.h" | 17 #include "ui/gfx/size.h" |
15 #include "ui/views/corewm/shadow_types.h" | 18 #include "ui/views/corewm/shadow_types.h" |
16 #include "ui/views/widget/widget.h" | 19 #include "ui/views/widget/widget.h" |
17 | 20 |
18 namespace { | 21 namespace { |
19 | 22 |
20 // Constants defining the visual attributes of selection handles | 23 // Constants defining the visual attributes of selection handles |
21 const int kSelectionHandleRadius = 10; | 24 const int kSelectionHandleLineWidth = 2; |
22 const int kSelectionHandleAlpha = 0x7F; | 25 const SkColor kSelectionHandleLineColor = |
23 const SkColor kSelectionHandleColor = | 26 SkColorSetRGB(0x42, 0x81, 0xf4); |
24 SkColorSetA(SK_ColorBLACK, kSelectionHandleAlpha); | |
25 | 27 |
26 // The minimum selection size to trigger selection controller. | 28 // The minimum selection size to trigger selection controller. |
27 const int kMinSelectionSize = 4; | 29 const int kMinSelectionSize = 4; |
28 | 30 |
29 const int kContextMenuTimoutMs = 200; | 31 const int kContextMenuTimoutMs = 200; |
30 | 32 |
31 // Convenience struct to represent a circle shape. | |
32 struct Circle { | |
33 int radius; | |
34 gfx::Point center; | |
35 SkColor color; | |
36 }; | |
37 | |
38 // Creates a widget to host SelectionHandleView. | 33 // Creates a widget to host SelectionHandleView. |
39 views::Widget* CreateTouchSelectionPopupWidget( | 34 views::Widget* CreateTouchSelectionPopupWidget( |
40 gfx::NativeView context, | 35 gfx::NativeView context, |
41 views::WidgetDelegate* widget_delegate) { | 36 views::WidgetDelegate* widget_delegate) { |
42 views::Widget* widget = new views::Widget; | 37 views::Widget* widget = new views::Widget; |
43 views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP); | 38 views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP); |
44 params.can_activate = false; | 39 params.can_activate = false; |
45 params.transparent = true; | 40 params.transparent = true; |
46 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 41 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
47 params.context = context; | 42 params.context = context; |
48 params.delegate = widget_delegate; | 43 params.delegate = widget_delegate; |
49 widget->Init(params); | 44 widget->Init(params); |
50 #if defined(USE_AURA) | 45 #if defined(USE_AURA) |
51 SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE); | 46 SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE); |
52 #endif | 47 #endif |
53 return widget; | 48 return widget; |
54 } | 49 } |
55 | 50 |
56 void PaintCircle(const Circle& circle, gfx::Canvas* canvas) { | 51 gfx::Image* GetHandleImage() { |
57 SkPaint paint; | 52 static gfx::Image* handle_image = NULL; |
58 paint.setAntiAlias(true); | 53 if (!handle_image) { |
59 paint.setStyle(SkPaint::kFill_Style); | 54 handle_image = &ui::ResourceBundle::GetSharedInstance().GetImageNamed( |
sadrul
2013/06/07 02:19:10
indentation is off here.
varunjain
2013/06/07 06:09:04
Done.
| |
60 paint.setColor(circle.color); | 55 IDR_TEXT_SELECTION_HANDLE); |
61 canvas->DrawCircle(circle.center, circle.radius, paint); | 56 } |
57 return handle_image; | |
58 } | |
59 | |
60 gfx::Size GetHandleImageSize() { | |
61 return GetHandleImage()->Size(); | |
62 } | 62 } |
63 | 63 |
64 // The points may not match exactly, since the selection range computation may | 64 // The points may not match exactly, since the selection range computation may |
65 // introduce some floating point errors. So check for a minimum size to decide | 65 // introduce some floating point errors. So check for a minimum size to decide |
66 // whether or not there is any selection. | 66 // whether or not there is any selection. |
67 bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) { | 67 bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) { |
68 int delta_x = p2.x() - p1.x(); | 68 int delta_x = p2.x() - p1.x(); |
69 int delta_y = p2.y() - p1.y(); | 69 int delta_y = p2.y() - p1.y(); |
70 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize); | 70 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize); |
71 } | 71 } |
72 | 72 |
73 gfx::Rect GetHandleBoundsFromCursor(const gfx::Rect& cursor) { | |
74 return gfx::Rect(cursor.x() - kSelectionHandleRadius, cursor.y(), | |
75 2 * kSelectionHandleRadius, | |
76 2 * kSelectionHandleRadius + cursor.height()); | |
77 } | |
78 | |
79 } // namespace | 73 } // namespace |
80 | 74 |
81 namespace views { | 75 namespace views { |
82 | 76 |
83 // A View that displays the text selection handle. | 77 // A View that displays the text selection handle. |
84 class TouchSelectionControllerImpl::EditingHandleView | 78 class TouchSelectionControllerImpl::EditingHandleView |
85 : public views::WidgetDelegateView { | 79 : public views::WidgetDelegateView { |
86 public: | 80 public: |
87 explicit EditingHandleView(TouchSelectionControllerImpl* controller, | 81 explicit EditingHandleView(TouchSelectionControllerImpl* controller, |
88 gfx::NativeView context) | 82 gfx::NativeView context) |
(...skipping 11 matching lines...) Expand all Loading... | |
100 } | 94 } |
101 | 95 |
102 int cursor_height() const { return cursor_height_; } | 96 int cursor_height() const { return cursor_height_; } |
103 | 97 |
104 // Overridden from views::WidgetDelegateView: | 98 // Overridden from views::WidgetDelegateView: |
105 virtual bool WidgetHasHitTestMask() const OVERRIDE { | 99 virtual bool WidgetHasHitTestMask() const OVERRIDE { |
106 return true; | 100 return true; |
107 } | 101 } |
108 | 102 |
109 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE { | 103 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE { |
110 mask->addCircle(SkIntToScalar(kSelectionHandleRadius), | 104 gfx::Size image_size = GetHandleImageSize(); |
111 SkIntToScalar(kSelectionHandleRadius + cursor_height_), | 105 mask->addRect(SkIntToScalar(0), SkIntToScalar(cursor_height_), |
112 SkIntToScalar(kSelectionHandleRadius)); | 106 SkIntToScalar(image_size.width()), |
107 SkIntToScalar(cursor_height_ + image_size.height())); | |
113 } | 108 } |
114 | 109 |
115 virtual void DeleteDelegate() OVERRIDE { | 110 virtual void DeleteDelegate() OVERRIDE { |
116 // We are owned and deleted by TouchSelectionController. | 111 // We are owned and deleted by TouchSelectionController. |
117 } | 112 } |
118 | 113 |
119 // Overridden from views::View: | 114 // Overridden from views::View: |
120 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | 115 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
121 Circle circle = {kSelectionHandleRadius, gfx::Point(kSelectionHandleRadius, | 116 gfx::Size image_size = GetHandleImageSize(); |
122 kSelectionHandleRadius + cursor_height_), | 117 canvas->FillRect( |
123 kSelectionHandleColor}; | 118 gfx::Rect(image_size.width() / 2 - kSelectionHandleLineWidth, 0, |
124 PaintCircle(circle, canvas); | 119 2 * kSelectionHandleLineWidth, cursor_height_), |
125 canvas->DrawLine(gfx::Point(kSelectionHandleRadius, 0), | 120 kSelectionHandleLineColor); |
126 gfx::Point(kSelectionHandleRadius, cursor_height_), | 121 canvas->DrawImageInt(*GetHandleImage()->ToImageSkia(), 0, cursor_height_); |
127 kSelectionHandleColor); | |
128 } | 122 } |
129 | 123 |
130 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { | 124 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { |
131 event->SetHandled(); | 125 event->SetHandled(); |
132 switch (event->type()) { | 126 switch (event->type()) { |
133 case ui::ET_GESTURE_SCROLL_BEGIN: | 127 case ui::ET_GESTURE_SCROLL_BEGIN: |
134 controller_->SetDraggingHandle(this); | 128 controller_->SetDraggingHandle(this); |
135 break; | 129 break; |
136 case ui::ET_GESTURE_SCROLL_UPDATE: | 130 case ui::ET_GESTURE_SCROLL_UPDATE: |
137 controller_->SelectionHandleDragged(event->location()); | 131 controller_->SelectionHandleDragged(event->location()); |
(...skipping 11 matching lines...) Expand all Loading... | |
149 if (visible != widget_->IsVisible()) { | 143 if (visible != widget_->IsVisible()) { |
150 if (visible) | 144 if (visible) |
151 widget_->Show(); | 145 widget_->Show(); |
152 else | 146 else |
153 widget_->Hide(); | 147 widget_->Hide(); |
154 } | 148 } |
155 View::SetVisible(visible); | 149 View::SetVisible(visible); |
156 } | 150 } |
157 | 151 |
158 virtual gfx::Size GetPreferredSize() OVERRIDE { | 152 virtual gfx::Size GetPreferredSize() OVERRIDE { |
159 return gfx::Size(2 * kSelectionHandleRadius, | 153 gfx::Size image_size = GetHandleImageSize(); |
160 2 * kSelectionHandleRadius + cursor_height_); | 154 return gfx::Size(image_size.width(), |
155 image_size.height() + cursor_height_); | |
161 } | 156 } |
162 | 157 |
163 bool IsWidgetVisible() const { | 158 bool IsWidgetVisible() const { |
164 return widget_->IsVisible(); | 159 return widget_->IsVisible(); |
165 } | 160 } |
166 | 161 |
167 void SetSelectionRectInScreen(const gfx::Rect& rect) { | 162 void SetSelectionRectInScreen(const gfx::Rect& rect) { |
163 gfx::Size image_size = GetHandleImageSize(); | |
168 cursor_height_ = rect.height(); | 164 cursor_height_ = rect.height(); |
169 gfx::Rect widget_bounds = GetHandleBoundsFromCursor(rect); | 165 gfx::Rect widget_bounds(rect.x() - image_size.width() / 2, rect.y(), |
166 image_size.width(), rect.height() + image_size.height()); | |
170 widget_->SetBounds(widget_bounds); | 167 widget_->SetBounds(widget_bounds); |
171 } | 168 } |
172 | 169 |
173 gfx::Point GetScreenPosition() { | 170 gfx::Point GetScreenPosition() { |
174 return widget_->GetClientAreaBoundsInScreen().origin(); | 171 return widget_->GetClientAreaBoundsInScreen().origin(); |
175 } | 172 } |
176 | 173 |
177 private: | 174 private: |
178 scoped_ptr<Widget> widget_; | 175 scoped_ptr<Widget> widget_; |
179 TouchSelectionControllerImpl* controller_; | 176 TouchSelectionControllerImpl* controller_; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
281 StartContextMenuTimer(); | 278 StartContextMenuTimer(); |
282 } | 279 } |
283 | 280 |
284 void TouchSelectionControllerImpl::SelectionHandleDragged( | 281 void TouchSelectionControllerImpl::SelectionHandleDragged( |
285 const gfx::Point& drag_pos) { | 282 const gfx::Point& drag_pos) { |
286 // We do not want to show the context menu while dragging. | 283 // We do not want to show the context menu while dragging. |
287 HideContextMenu(); | 284 HideContextMenu(); |
288 | 285 |
289 DCHECK(dragging_handle_); | 286 DCHECK(dragging_handle_); |
290 | 287 |
288 gfx::Size image_size = GetHandleImageSize(); | |
291 gfx::Point offset_drag_pos(drag_pos.x(), | 289 gfx::Point offset_drag_pos(drag_pos.x(), |
292 drag_pos.y() - dragging_handle_->cursor_height() / 2 - | 290 drag_pos.y() - dragging_handle_->cursor_height() / 2 - |
293 2 * kSelectionHandleRadius); | 291 image_size.height() / 2); |
294 ConvertPointToClientView(dragging_handle_, &offset_drag_pos); | 292 ConvertPointToClientView(dragging_handle_, &offset_drag_pos); |
295 if (dragging_handle_ == cursor_handle_.get()) { | 293 if (dragging_handle_ == cursor_handle_.get()) { |
296 client_view_->MoveCaretTo(offset_drag_pos); | 294 client_view_->MoveCaretTo(offset_drag_pos); |
297 return; | 295 return; |
298 } | 296 } |
299 | 297 |
300 // Find the stationary selection handle. | 298 // Find the stationary selection handle. |
301 EditingHandleView* fixed_handle = selection_handle_1_.get(); | 299 EditingHandleView* fixed_handle = selection_handle_1_.get(); |
302 if (fixed_handle == dragging_handle_) | 300 if (fixed_handle == dragging_handle_) |
303 fixed_handle = selection_handle_2_.get(); | 301 fixed_handle = selection_handle_2_.get(); |
304 | 302 |
305 // Find selection end points in client_view's coordinate system. | 303 // Find selection end points in client_view's coordinate system. |
306 gfx::Point p2(kSelectionHandleRadius, fixed_handle->cursor_height() / 2); | 304 gfx::Point p2(image_size.width() / 2, fixed_handle->cursor_height() / 2); |
307 ConvertPointToClientView(fixed_handle, &p2); | 305 ConvertPointToClientView(fixed_handle, &p2); |
308 | 306 |
309 // Instruct client_view to select the region between p1 and p2. The position | 307 // Instruct client_view to select the region between p1 and p2. The position |
310 // of |fixed_handle| is the start and that of |dragging_handle| is the end | 308 // of |fixed_handle| is the start and that of |dragging_handle| is the end |
311 // of selection. | 309 // of selection. |
312 client_view_->SelectRect(p2, offset_drag_pos); | 310 client_view_->SelectRect(p2, offset_drag_pos); |
313 } | 311 } |
314 | 312 |
315 void TouchSelectionControllerImpl::ConvertPointToClientView( | 313 void TouchSelectionControllerImpl::ConvertPointToClientView( |
316 EditingHandleView* source, gfx::Point* point) { | 314 EditingHandleView* source, gfx::Point* point) { |
317 View::ConvertPointToScreen(source, point); | 315 View::ConvertPointToScreen(source, point); |
318 client_view_->ConvertPointFromScreen(point); | 316 client_view_->ConvertPointFromScreen(point); |
319 } | 317 } |
320 | 318 |
321 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { | 319 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { |
322 return client_view_->IsCommandIdEnabled(command_id); | 320 return client_view_->IsCommandIdEnabled(command_id); |
323 } | 321 } |
324 | 322 |
325 void TouchSelectionControllerImpl::ExecuteCommand(int command_id, | 323 void TouchSelectionControllerImpl::ExecuteCommand(int command_id, |
326 int event_flags) { | 324 int event_flags) { |
327 HideContextMenu(); | 325 HideContextMenu(); |
328 client_view_->ExecuteCommand(command_id, event_flags); | 326 client_view_->ExecuteCommand(command_id, event_flags); |
329 } | 327 } |
330 | 328 |
331 void TouchSelectionControllerImpl::OpenContextMenu() { | 329 void TouchSelectionControllerImpl::OpenContextMenu() { |
332 gfx::Point anchor = context_menu_->anchor_rect().origin(); | 330 gfx::Point anchor = context_menu_->anchor_rect().origin(); |
333 anchor.Offset(0, -kSelectionHandleRadius); | 331 // anchor.Offset(0, -kSelectionHandleRadius); |
sadrul
2013/06/07 02:19:10
remove
varunjain
2013/06/07 06:09:04
Done.
| |
334 HideContextMenu(); | 332 HideContextMenu(); |
335 client_view_->OpenContextMenu(anchor); | 333 client_view_->OpenContextMenu(anchor); |
336 } | 334 } |
337 | 335 |
338 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) { | 336 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) { |
339 if (menu == context_menu_) | 337 if (menu == context_menu_) |
340 context_menu_ = NULL; | 338 context_menu_ = NULL; |
341 } | 339 } |
342 | 340 |
343 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) { | 341 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) { |
344 DCHECK_EQ(client_widget_, widget); | 342 DCHECK_EQ(client_widget_, widget); |
345 client_widget_ = NULL; | 343 client_widget_ = NULL; |
346 } | 344 } |
347 | 345 |
348 void TouchSelectionControllerImpl::OnWidgetBoundsChanged( | 346 void TouchSelectionControllerImpl::OnWidgetBoundsChanged( |
349 Widget* widget, | 347 Widget* widget, |
350 const gfx::Rect& new_bounds) { | 348 const gfx::Rect& new_bounds) { |
351 DCHECK_EQ(client_widget_, widget); | 349 DCHECK_EQ(client_widget_, widget); |
352 HideContextMenu(); | 350 HideContextMenu(); |
353 SelectionChanged(); | 351 SelectionChanged(); |
354 } | 352 } |
355 | 353 |
356 void TouchSelectionControllerImpl::ContextMenuTimerFired() { | 354 void TouchSelectionControllerImpl::ContextMenuTimerFired() { |
357 // Get selection end points in client_view's space. | 355 // Get selection end points in client_view's space. |
358 gfx::Rect r1, r2; | 356 gfx::Rect r1, r2; |
359 client_view_->GetSelectionEndPoints(&r1, &r2); | 357 client_view_->GetSelectionEndPoints(&r1, &r2); |
360 | 358 |
361 gfx::Rect handle_1_bounds = GetHandleBoundsFromCursor(r1); | 359 gfx::Rect handle_1_bounds; |
362 gfx::Rect handle_2_bounds = GetHandleBoundsFromCursor(r2); | 360 gfx::Rect handle_2_bounds; |
361 if (cursor_handle_->IsWidgetVisible()) { | |
362 handle_1_bounds = cursor_handle_->GetBoundsInScreen(); | |
363 handle_2_bounds = handle_1_bounds; | |
364 } else { | |
365 handle_1_bounds = selection_handle_1_->GetBoundsInScreen(); | |
366 handle_2_bounds = selection_handle_2_->GetBoundsInScreen(); | |
367 } | |
363 | 368 |
364 // if selection is completely inside the view, we display the context menu | 369 // if selection is completely inside the view, we display the context menu |
365 // in the middle of the end points on the top. Else, we show it above the | 370 // in the middle of the end points on the top. Else, we show it above the |
366 // visible handle. If no handle is visible, we do not show the menu. | 371 // visible handle. If no handle is visible, we do not show the menu. |
367 gfx::Rect menu_anchor; | 372 gfx::Rect menu_anchor; |
368 gfx::Rect client_bounds = client_view_->GetBounds(); | 373 gfx::Rect client_bounds = client_view_->GetBounds(); |
369 if (client_bounds.Contains(r1.origin()) && | 374 if (client_bounds.Contains(r1.origin()) && |
370 client_bounds.Contains(r2.origin())) { | 375 client_bounds.Contains(r2.origin())) { |
371 menu_anchor = gfx::UnionRects(handle_1_bounds, handle_2_bounds); | 376 menu_anchor = gfx::UnionRects(handle_1_bounds, handle_2_bounds); |
372 } else if (client_bounds.Contains(r1.origin())) { | 377 } else if (client_bounds.Contains(r1.origin())) { |
373 menu_anchor = handle_1_bounds; | 378 menu_anchor = handle_1_bounds; |
374 } else if (client_bounds.Contains(r2.origin())) { | 379 } else if (client_bounds.Contains(r2.origin())) { |
375 menu_anchor = handle_2_bounds; | 380 menu_anchor = handle_2_bounds; |
376 } else { | 381 } else { |
377 return; | 382 return; |
378 } | 383 } |
379 | 384 |
380 gfx::Point menu_origin = menu_anchor.origin(); | |
381 client_view_->ConvertPointToScreen(&menu_origin); | |
382 menu_anchor.set_origin(menu_origin); | |
383 | |
384 DCHECK(!context_menu_); | 385 DCHECK(!context_menu_); |
385 context_menu_ = new TouchEditingMenuView(this, menu_anchor, | 386 context_menu_ = new TouchEditingMenuView(this, menu_anchor, |
386 client_view_->GetNativeView()); | 387 client_view_->GetNativeView()); |
387 } | 388 } |
388 | 389 |
389 void TouchSelectionControllerImpl::StartContextMenuTimer() { | 390 void TouchSelectionControllerImpl::StartContextMenuTimer() { |
390 if (context_menu_timer_.IsRunning()) | 391 if (context_menu_timer_.IsRunning()) |
391 return; | 392 return; |
392 context_menu_timer_.Start( | 393 context_menu_timer_.Start( |
393 FROM_HERE, | 394 FROM_HERE, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
438 } | 439 } |
439 | 440 |
440 ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( | 441 ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( |
441 ui::TouchEditable* client_view) { | 442 ui::TouchEditable* client_view) { |
442 if (switches::IsTouchEditingEnabled()) | 443 if (switches::IsTouchEditingEnabled()) |
443 return new views::TouchSelectionControllerImpl(client_view); | 444 return new views::TouchSelectionControllerImpl(client_view); |
444 return NULL; | 445 return NULL; |
445 } | 446 } |
446 | 447 |
447 } // namespace views | 448 } // namespace views |
OLD | NEW |