Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(266)

Side by Side Diff: ui/views/touchui/touch_selection_controller_impl.cc

Issue 16580009: Change touch selection handles to images given by UX. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 = 1;
22 const int kSelectionHandleAlpha = 0x7F; 25 const SkColor kSelectionHandleLineColor =
23 const SkColor kSelectionHandleColor = 26 SkColorSetRGB(0x42, 0x81, 0xf4);
24 SkColorSetA(SK_ColorBLACK, kSelectionHandleAlpha); 27
28 // Padding around the selection handle defining the area that will be included
29 // in the touch target to make dragging the handle easier.
30 const int kSelectionHandlePadding = 10;
25 31
26 // The minimum selection size to trigger selection controller. 32 // The minimum selection size to trigger selection controller.
27 const int kMinSelectionSize = 4; 33 const int kMinSelectionSize = 4;
28 34
29 const int kContextMenuTimoutMs = 200; 35 const int kContextMenuTimoutMs = 200;
30 36
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. 37 // Creates a widget to host SelectionHandleView.
39 views::Widget* CreateTouchSelectionPopupWidget( 38 views::Widget* CreateTouchSelectionPopupWidget(
40 gfx::NativeView context, 39 gfx::NativeView context,
41 views::WidgetDelegate* widget_delegate) { 40 views::WidgetDelegate* widget_delegate) {
42 views::Widget* widget = new views::Widget; 41 views::Widget* widget = new views::Widget;
43 views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP); 42 views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP);
44 params.can_activate = false; 43 params.can_activate = false;
45 params.transparent = true; 44 params.transparent = true;
46 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 45 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
47 params.context = context; 46 params.context = context;
48 params.delegate = widget_delegate; 47 params.delegate = widget_delegate;
49 widget->Init(params); 48 widget->Init(params);
50 #if defined(USE_AURA) 49 #if defined(USE_AURA)
51 SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE); 50 SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE);
52 #endif 51 #endif
53 return widget; 52 return widget;
54 } 53 }
55 54
56 void PaintCircle(const Circle& circle, gfx::Canvas* canvas) { 55 gfx::Image* GetHandleImage() {
57 SkPaint paint; 56 static gfx::Image* handle_image = NULL;
58 paint.setAntiAlias(true); 57 if (!handle_image) {
59 paint.setStyle(SkPaint::kFill_Style); 58 handle_image = &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
60 paint.setColor(circle.color); 59 IDR_TEXT_SELECTION_HANDLE);
61 canvas->DrawCircle(circle.center, circle.radius, paint); 60 }
61 return handle_image;
62 }
63
64 gfx::Size GetHandleImageSize() {
65 return GetHandleImage()->Size();
62 } 66 }
63 67
64 // The points may not match exactly, since the selection range computation may 68 // 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 69 // introduce some floating point errors. So check for a minimum size to decide
66 // whether or not there is any selection. 70 // whether or not there is any selection.
67 bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) { 71 bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) {
68 int delta_x = p2.x() - p1.x(); 72 int delta_x = p2.x() - p1.x();
69 int delta_y = p2.y() - p1.y(); 73 int delta_y = p2.y() - p1.y();
70 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize); 74 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize);
71 } 75 }
72 76
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 77 } // namespace
80 78
81 namespace views { 79 namespace views {
82 80
83 // A View that displays the text selection handle. 81 // A View that displays the text selection handle.
84 class TouchSelectionControllerImpl::EditingHandleView 82 class TouchSelectionControllerImpl::EditingHandleView
85 : public views::WidgetDelegateView { 83 : public views::WidgetDelegateView {
86 public: 84 public:
87 explicit EditingHandleView(TouchSelectionControllerImpl* controller, 85 explicit EditingHandleView(TouchSelectionControllerImpl* controller,
88 gfx::NativeView context) 86 gfx::NativeView context)
(...skipping 11 matching lines...) Expand all
100 } 98 }
101 99
102 int cursor_height() const { return cursor_height_; } 100 int cursor_height() const { return cursor_height_; }
103 101
104 // Overridden from views::WidgetDelegateView: 102 // Overridden from views::WidgetDelegateView:
105 virtual bool WidgetHasHitTestMask() const OVERRIDE { 103 virtual bool WidgetHasHitTestMask() const OVERRIDE {
106 return true; 104 return true;
107 } 105 }
108 106
109 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE { 107 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE {
110 mask->addCircle(SkIntToScalar(kSelectionHandleRadius), 108 gfx::Size image_size = GetHandleImageSize();
111 SkIntToScalar(kSelectionHandleRadius + cursor_height_), 109 mask->addRect(SkIntToScalar(0), SkIntToScalar(cursor_height_),
112 SkIntToScalar(kSelectionHandleRadius)); 110 SkIntToScalar(image_size.width()) + 2 * kSelectionHandlePadding,
111 SkIntToScalar(cursor_height_ + image_size.height() +
112 kSelectionHandlePadding));
113 } 113 }
114 114
115 virtual void DeleteDelegate() OVERRIDE { 115 virtual void DeleteDelegate() OVERRIDE {
116 // We are owned and deleted by TouchSelectionController. 116 // We are owned and deleted by TouchSelectionController.
117 } 117 }
118 118
119 // Overridden from views::View: 119 // Overridden from views::View:
120 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 120 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
121 Circle circle = {kSelectionHandleRadius, gfx::Point(kSelectionHandleRadius, 121 gfx::Size image_size = GetHandleImageSize();
122 kSelectionHandleRadius + cursor_height_), 122 int cursor_pos_x = image_size.width() / 2 - kSelectionHandleLineWidth +
123 kSelectionHandleColor}; 123 kSelectionHandlePadding;
124 PaintCircle(circle, canvas); 124
125 canvas->DrawLine(gfx::Point(kSelectionHandleRadius, 0), 125 // Draw the cursor line.
126 gfx::Point(kSelectionHandleRadius, cursor_height_), 126 canvas->FillRect(
127 kSelectionHandleColor); 127 gfx::Rect(cursor_pos_x, 0,
128 2 * kSelectionHandleLineWidth + 1, cursor_height_),
129 kSelectionHandleLineColor);
130
131 // Draw the handle image.
132 canvas->DrawImageInt(*GetHandleImage()->ToImageSkia(),
133 kSelectionHandlePadding, cursor_height_);
128 } 134 }
129 135
130 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { 136 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
131 event->SetHandled(); 137 event->SetHandled();
132 switch (event->type()) { 138 switch (event->type()) {
133 case ui::ET_GESTURE_SCROLL_BEGIN: 139 case ui::ET_GESTURE_SCROLL_BEGIN:
134 controller_->SetDraggingHandle(this); 140 controller_->SetDraggingHandle(this);
135 break; 141 break;
136 case ui::ET_GESTURE_SCROLL_UPDATE: 142 case ui::ET_GESTURE_SCROLL_UPDATE:
137 controller_->SelectionHandleDragged(event->location()); 143 controller_->SelectionHandleDragged(event->location());
(...skipping 11 matching lines...) Expand all
149 if (visible != widget_->IsVisible()) { 155 if (visible != widget_->IsVisible()) {
150 if (visible) 156 if (visible)
151 widget_->Show(); 157 widget_->Show();
152 else 158 else
153 widget_->Hide(); 159 widget_->Hide();
154 } 160 }
155 View::SetVisible(visible); 161 View::SetVisible(visible);
156 } 162 }
157 163
158 virtual gfx::Size GetPreferredSize() OVERRIDE { 164 virtual gfx::Size GetPreferredSize() OVERRIDE {
159 return gfx::Size(2 * kSelectionHandleRadius, 165 gfx::Size image_size = GetHandleImageSize();
160 2 * kSelectionHandleRadius + cursor_height_); 166 return gfx::Size(image_size.width() + 2 * kSelectionHandlePadding,
167 image_size.height() + cursor_height_ + kSelectionHandlePadding);
161 } 168 }
162 169
163 bool IsWidgetVisible() const { 170 bool IsWidgetVisible() const {
164 return widget_->IsVisible(); 171 return widget_->IsVisible();
165 } 172 }
166 173
167 void SetSelectionRectInScreen(const gfx::Rect& rect) { 174 void SetSelectionRectInScreen(const gfx::Rect& rect) {
175 gfx::Size image_size = GetHandleImageSize();
168 cursor_height_ = rect.height(); 176 cursor_height_ = rect.height();
169 gfx::Rect widget_bounds = GetHandleBoundsFromCursor(rect); 177 gfx::Rect widget_bounds(
178 rect.x() - image_size.width() / 2 - kSelectionHandlePadding,
179 rect.y(),
180 image_size.width() + 2 * kSelectionHandlePadding,
181 rect.height() + image_size.height() + kSelectionHandlePadding);
170 widget_->SetBounds(widget_bounds); 182 widget_->SetBounds(widget_bounds);
171 } 183 }
172 184
173 gfx::Point GetScreenPosition() { 185 gfx::Point GetScreenPosition() {
174 return widget_->GetClientAreaBoundsInScreen().origin(); 186 return widget_->GetClientAreaBoundsInScreen().origin();
175 } 187 }
176 188
177 private: 189 private:
178 scoped_ptr<Widget> widget_; 190 scoped_ptr<Widget> widget_;
179 TouchSelectionControllerImpl* controller_; 191 TouchSelectionControllerImpl* controller_;
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
281 StartContextMenuTimer(); 293 StartContextMenuTimer();
282 } 294 }
283 295
284 void TouchSelectionControllerImpl::SelectionHandleDragged( 296 void TouchSelectionControllerImpl::SelectionHandleDragged(
285 const gfx::Point& drag_pos) { 297 const gfx::Point& drag_pos) {
286 // We do not want to show the context menu while dragging. 298 // We do not want to show the context menu while dragging.
287 HideContextMenu(); 299 HideContextMenu();
288 300
289 DCHECK(dragging_handle_); 301 DCHECK(dragging_handle_);
290 302
303 gfx::Size image_size = GetHandleImageSize();
291 gfx::Point offset_drag_pos(drag_pos.x(), 304 gfx::Point offset_drag_pos(drag_pos.x(),
292 drag_pos.y() - dragging_handle_->cursor_height() / 2 - 305 drag_pos.y() - dragging_handle_->cursor_height() / 2 -
293 2 * kSelectionHandleRadius); 306 image_size.height() / 2);
294 ConvertPointToClientView(dragging_handle_, &offset_drag_pos); 307 ConvertPointToClientView(dragging_handle_, &offset_drag_pos);
295 if (dragging_handle_ == cursor_handle_.get()) { 308 if (dragging_handle_ == cursor_handle_.get()) {
296 client_view_->MoveCaretTo(offset_drag_pos); 309 client_view_->MoveCaretTo(offset_drag_pos);
297 return; 310 return;
298 } 311 }
299 312
300 // Find the stationary selection handle. 313 // Find the stationary selection handle.
301 EditingHandleView* fixed_handle = selection_handle_1_.get(); 314 EditingHandleView* fixed_handle = selection_handle_1_.get();
302 if (fixed_handle == dragging_handle_) 315 if (fixed_handle == dragging_handle_)
303 fixed_handle = selection_handle_2_.get(); 316 fixed_handle = selection_handle_2_.get();
304 317
305 // Find selection end points in client_view's coordinate system. 318 // Find selection end points in client_view's coordinate system.
306 gfx::Point p2(kSelectionHandleRadius, fixed_handle->cursor_height() / 2); 319 gfx::Point p2(image_size.width() / 2 + kSelectionHandlePadding,
320 fixed_handle->cursor_height() / 2);
307 ConvertPointToClientView(fixed_handle, &p2); 321 ConvertPointToClientView(fixed_handle, &p2);
308 322
309 // Instruct client_view to select the region between p1 and p2. The position 323 // 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 324 // of |fixed_handle| is the start and that of |dragging_handle| is the end
311 // of selection. 325 // of selection.
312 client_view_->SelectRect(p2, offset_drag_pos); 326 client_view_->SelectRect(p2, offset_drag_pos);
313 } 327 }
314 328
315 void TouchSelectionControllerImpl::ConvertPointToClientView( 329 void TouchSelectionControllerImpl::ConvertPointToClientView(
316 EditingHandleView* source, gfx::Point* point) { 330 EditingHandleView* source, gfx::Point* point) {
317 View::ConvertPointToScreen(source, point); 331 View::ConvertPointToScreen(source, point);
318 client_view_->ConvertPointFromScreen(point); 332 client_view_->ConvertPointFromScreen(point);
319 } 333 }
320 334
321 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { 335 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
322 return client_view_->IsCommandIdEnabled(command_id); 336 return client_view_->IsCommandIdEnabled(command_id);
323 } 337 }
324 338
325 void TouchSelectionControllerImpl::ExecuteCommand(int command_id, 339 void TouchSelectionControllerImpl::ExecuteCommand(int command_id,
326 int event_flags) { 340 int event_flags) {
327 HideContextMenu(); 341 HideContextMenu();
328 client_view_->ExecuteCommand(command_id, event_flags); 342 client_view_->ExecuteCommand(command_id, event_flags);
329 } 343 }
330 344
331 void TouchSelectionControllerImpl::OpenContextMenu() { 345 void TouchSelectionControllerImpl::OpenContextMenu() {
332 gfx::Point anchor = context_menu_->anchor_rect().origin(); 346 gfx::Size image_size = GetHandleImageSize();
333 anchor.Offset(0, -kSelectionHandleRadius); 347 gfx::Point anchor = context_menu_->anchor_rect().CenterPoint();
348 anchor.Offset(0, -image_size.height() / 2);
334 HideContextMenu(); 349 HideContextMenu();
335 client_view_->OpenContextMenu(anchor); 350 client_view_->OpenContextMenu(anchor);
336 } 351 }
337 352
338 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) { 353 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
339 if (menu == context_menu_) 354 if (menu == context_menu_)
340 context_menu_ = NULL; 355 context_menu_ = NULL;
341 } 356 }
342 357
343 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) { 358 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
344 DCHECK_EQ(client_widget_, widget); 359 DCHECK_EQ(client_widget_, widget);
345 client_widget_ = NULL; 360 client_widget_ = NULL;
346 } 361 }
347 362
348 void TouchSelectionControllerImpl::OnWidgetBoundsChanged( 363 void TouchSelectionControllerImpl::OnWidgetBoundsChanged(
349 Widget* widget, 364 Widget* widget,
350 const gfx::Rect& new_bounds) { 365 const gfx::Rect& new_bounds) {
351 DCHECK_EQ(client_widget_, widget); 366 DCHECK_EQ(client_widget_, widget);
352 HideContextMenu(); 367 HideContextMenu();
353 SelectionChanged(); 368 SelectionChanged();
354 } 369 }
355 370
356 void TouchSelectionControllerImpl::ContextMenuTimerFired() { 371 void TouchSelectionControllerImpl::ContextMenuTimerFired() {
357 // Get selection end points in client_view's space. 372 // Get selection end points in client_view's space.
358 gfx::Rect r1, r2; 373 gfx::Rect r1, r2;
359 client_view_->GetSelectionEndPoints(&r1, &r2); 374 client_view_->GetSelectionEndPoints(&r1, &r2);
360 375
361 gfx::Rect handle_1_bounds = GetHandleBoundsFromCursor(r1); 376 gfx::Rect handle_1_bounds;
362 gfx::Rect handle_2_bounds = GetHandleBoundsFromCursor(r2); 377 gfx::Rect handle_2_bounds;
378 if (cursor_handle_->IsWidgetVisible()) {
379 handle_1_bounds = cursor_handle_->GetBoundsInScreen();
380 handle_2_bounds = handle_1_bounds;
381 } else {
382 handle_1_bounds = selection_handle_1_->GetBoundsInScreen();
383 handle_2_bounds = selection_handle_2_->GetBoundsInScreen();
384 }
363 385
364 // if selection is completely inside the view, we display the context menu 386 // 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 387 // 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. 388 // visible handle. If no handle is visible, we do not show the menu.
367 gfx::Rect menu_anchor; 389 gfx::Rect menu_anchor;
368 gfx::Rect client_bounds = client_view_->GetBounds(); 390 gfx::Rect client_bounds = client_view_->GetBounds();
369 if (client_bounds.Contains(r1.origin()) && 391 if (client_bounds.Contains(r1.origin()) &&
370 client_bounds.Contains(r2.origin())) { 392 client_bounds.Contains(r2.origin())) {
371 menu_anchor = gfx::UnionRects(handle_1_bounds, handle_2_bounds); 393 menu_anchor = gfx::UnionRects(handle_1_bounds, handle_2_bounds);
372 } else if (client_bounds.Contains(r1.origin())) { 394 } else if (client_bounds.Contains(r1.origin())) {
373 menu_anchor = handle_1_bounds; 395 menu_anchor = handle_1_bounds;
374 } else if (client_bounds.Contains(r2.origin())) { 396 } else if (client_bounds.Contains(r2.origin())) {
375 menu_anchor = handle_2_bounds; 397 menu_anchor = handle_2_bounds;
376 } else { 398 } else {
377 return; 399 return;
378 } 400 }
379 401
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_); 402 DCHECK(!context_menu_);
385 context_menu_ = new TouchEditingMenuView(this, menu_anchor, 403 context_menu_ = new TouchEditingMenuView(this, menu_anchor,
386 client_view_->GetNativeView()); 404 client_view_->GetNativeView());
387 } 405 }
388 406
389 void TouchSelectionControllerImpl::StartContextMenuTimer() { 407 void TouchSelectionControllerImpl::StartContextMenuTimer() {
390 if (context_menu_timer_.IsRunning()) 408 if (context_menu_timer_.IsRunning())
391 return; 409 return;
392 context_menu_timer_.Start( 410 context_menu_timer_.Start(
393 FROM_HERE, 411 FROM_HERE,
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
438 } 456 }
439 457
440 ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( 458 ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create(
441 ui::TouchEditable* client_view) { 459 ui::TouchEditable* client_view) {
442 if (switches::IsTouchEditingEnabled()) 460 if (switches::IsTouchEditingEnabled())
443 return new views::TouchSelectionControllerImpl(client_view); 461 return new views::TouchSelectionControllerImpl(client_view);
444 return NULL; 462 return NULL;
445 } 463 }
446 464
447 } // namespace views 465 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698