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

Side by Side Diff: ui/touch_selection/touch_selection_controller.cc

Issue 2201853002: Blink handle selection handle visibility (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: try running even rebaseline tests Created 4 years, 2 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/touch_selection/touch_selection_controller.h" 5 #include "ui/touch_selection/touch_selection_controller.h"
6 6
7 #include "base/auto_reset.h" 7 #include "base/auto_reset.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/metrics/histogram_macros.h" 9 #include "base/metrics/histogram_macros.h"
10 #include "base/metrics/user_metrics.h" 10 #include "base/metrics/user_metrics.h"
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
49 enable_longpress_drag_selection(false) {} 49 enable_longpress_drag_selection(false) {}
50 50
51 TouchSelectionController::Config::~Config() { 51 TouchSelectionController::Config::~Config() {
52 } 52 }
53 53
54 TouchSelectionController::TouchSelectionController( 54 TouchSelectionController::TouchSelectionController(
55 TouchSelectionControllerClient* client, 55 TouchSelectionControllerClient* client,
56 const Config& config) 56 const Config& config)
57 : client_(client), 57 : client_(client),
58 config_(config), 58 config_(config),
59 force_next_update_(false),
60 response_pending_input_event_(INPUT_EVENT_TYPE_NONE), 59 response_pending_input_event_(INPUT_EVENT_TYPE_NONE),
61 start_orientation_(TouchHandleOrientation::UNDEFINED), 60 start_orientation_(TouchHandleOrientation::UNDEFINED),
62 end_orientation_(TouchHandleOrientation::UNDEFINED), 61 end_orientation_(TouchHandleOrientation::UNDEFINED),
63 active_status_(INACTIVE), 62 active_status_(INACTIVE),
64 activate_insertion_automatically_(false),
65 activate_selection_automatically_(false),
66 selection_empty_(false),
67 selection_editable_(false),
68 temporarily_hidden_(false), 63 temporarily_hidden_(false),
69 anchor_drag_to_selection_start_(false), 64 anchor_drag_to_selection_start_(false),
70 longpress_drag_selector_(this), 65 longpress_drag_selector_(this),
71 selection_handle_dragged_(false) { 66 selection_handle_dragged_(false) {
72 DCHECK(client_); 67 DCHECK(client_);
73 } 68 }
74 69
75 TouchSelectionController::~TouchSelectionController() { 70 TouchSelectionController::~TouchSelectionController() {
76 } 71 }
77 72
78 void TouchSelectionController::OnSelectionBoundsChanged( 73 void TouchSelectionController::OnSelectionBoundsChanged(
79 const gfx::SelectionBound& start, 74 const gfx::SelectionBound& start,
80 const gfx::SelectionBound& end) { 75 const gfx::SelectionBound& end) {
81 if (!force_next_update_ && start == start_ && end_ == end) 76 if (start == start_ && end_ == end)
82 return; 77 return;
83 78
84 // Notify if selection bounds have just been established or dissolved. 79 if (start.type() == gfx::SelectionBound::EMPTY
85 if (start.type() != gfx::SelectionBound::EMPTY && 80 || end.type() == gfx::SelectionBound::EMPTY) {
86 start_.type() == gfx::SelectionBound::EMPTY) { 81 HideAndDisallowShowingAutomatically();
87 client_->OnSelectionEvent(SELECTION_ESTABLISHED); 82 return;
88 } else if (start.type() == gfx::SelectionBound::EMPTY &&
89 start_.type() != gfx::SelectionBound::EMPTY) {
90 client_->OnSelectionEvent(SELECTION_DISSOLVED);
91 } 83 }
92 84
93 // Swap the Handles when the start and end selection points cross each other. 85 // Swap the Handles when the start and end selection points cross each other.
94 if (active_status_ == SELECTION_ACTIVE) { 86 if (active_status_ == SELECTION_ACTIVE) {
95 if ((start_selection_handle_->IsActive() && 87 if ((start_selection_handle_->IsActive() &&
96 end_.edge_bottom() == start.edge_bottom()) || 88 end_.edge_bottom() == start.edge_bottom()) ||
97 (end_selection_handle_->IsActive() && 89 (end_selection_handle_->IsActive() &&
98 end.edge_bottom() == start_.edge_bottom())) { 90 end.edge_bottom() == start_.edge_bottom())) {
99 start_selection_handle_.swap(end_selection_handle_); 91 start_selection_handle_.swap(end_selection_handle_);
100 } 92 }
101 } 93 }
102 94
103 start_ = start; 95 start_ = start;
104 end_ = end; 96 end_ = end;
105 start_orientation_ = ToTouchHandleOrientation(start_.type()); 97 start_orientation_ = ToTouchHandleOrientation(start_.type());
106 end_orientation_ = ToTouchHandleOrientation(end_.type()); 98 end_orientation_ = ToTouchHandleOrientation(end_.type());
107 force_next_update_ = false;
108
109 if (!activate_selection_automatically_ &&
110 !activate_insertion_automatically_) {
111 DCHECK_EQ(INACTIVE, active_status_);
112 DCHECK_EQ(INPUT_EVENT_TYPE_NONE, response_pending_input_event_);
113 return;
114 }
115 99
116 // Ensure that |response_pending_input_event_| is cleared after the method 100 // Ensure that |response_pending_input_event_| is cleared after the method
117 // completes, while also making its current value available for the duration 101 // completes, while also making its current value available for the duration
118 // of the call. 102 // of the call.
119 InputEventType causal_input_event = response_pending_input_event_; 103 InputEventType causal_input_event = response_pending_input_event_;
120 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; 104 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
121 base::AutoReset<InputEventType> auto_reset_response_pending_input_event( 105 base::AutoReset<InputEventType> auto_reset_response_pending_input_event(
122 &response_pending_input_event_, causal_input_event); 106 &response_pending_input_event_, causal_input_event);
123 107
124 const bool is_selection_dragging = active_status_ == SELECTION_ACTIVE && 108 const bool is_selection_dragging = active_status_ == SELECTION_ACTIVE &&
(...skipping 10 matching lines...) Expand all
135 } 119 }
136 120
137 if (GetStartPosition() != GetEndPosition() || 121 if (GetStartPosition() != GetEndPosition() ||
138 (is_selection_dragging && 122 (is_selection_dragging &&
139 start_orientation_ != TouchHandleOrientation::UNDEFINED && 123 start_orientation_ != TouchHandleOrientation::UNDEFINED &&
140 end_orientation_ != TouchHandleOrientation::UNDEFINED)) { 124 end_orientation_ != TouchHandleOrientation::UNDEFINED)) {
141 OnSelectionChanged(); 125 OnSelectionChanged();
142 return; 126 return;
143 } 127 }
144 128
145 if (start_orientation_ == TouchHandleOrientation::CENTER && 129 if (start_orientation_ == TouchHandleOrientation::CENTER) {
146 selection_editable_) {
147 OnInsertionChanged(); 130 OnInsertionChanged();
148 return; 131 return;
149 } 132 }
150 133
151 HideAndDisallowShowingAutomatically(); 134 HideAndDisallowShowingAutomatically();
152 } 135 }
153 136
154 void TouchSelectionController::OnViewportChanged( 137 void TouchSelectionController::OnViewportChanged(
155 const gfx::RectF viewport_rect) { 138 const gfx::RectF viewport_rect) {
156 // Trigger a force update if the viewport is changed, so that 139 // Trigger a force update if the viewport is changed, so that
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 if ((event_pos - GetStartPosition()).LengthSquared() <= 184 if ((event_pos - GetStartPosition()).LengthSquared() <=
202 (event_pos - GetEndPosition()).LengthSquared()) { 185 (event_pos - GetEndPosition()).LengthSquared()) {
203 return start_selection_handle_->WillHandleTouchEvent(event); 186 return start_selection_handle_->WillHandleTouchEvent(event);
204 } 187 }
205 return end_selection_handle_->WillHandleTouchEvent(event); 188 return end_selection_handle_->WillHandleTouchEvent(event);
206 } 189 }
207 190
208 return false; 191 return false;
209 } 192 }
210 193
211 bool TouchSelectionController::WillHandleTapEvent(const gfx::PointF& location, 194 void TouchSelectionController::WillHandleTapEvent(const gfx::PointF& location,
212 int tap_count) { 195 int tap_count) {
213 if (WillHandleTapOrLongPress(location))
214 return true;
215
216 if (tap_count > 1) { 196 if (tap_count > 1) {
217 response_pending_input_event_ = REPEATED_TAP; 197 response_pending_input_event_ = REPEATED_TAP;
218 ShowSelectionHandlesAutomatically();
219 } else { 198 } else {
220 response_pending_input_event_ = TAP; 199 response_pending_input_event_ = TAP;
221 if (active_status_ != SELECTION_ACTIVE)
222 activate_selection_automatically_ = false;
223 } 200 }
224 ShowInsertionHandleAutomatically();
225 if (selection_empty_)
226 DeactivateInsertion();
227 ForceNextUpdateIfInactive();
228 return false;
229 } 201 }
230 202
231 bool TouchSelectionController::WillHandleLongPressEvent( 203 void TouchSelectionController::WillHandleLongPressEvent(
232 base::TimeTicks event_time, 204 base::TimeTicks event_time,
233 const gfx::PointF& location) { 205 const gfx::PointF& location) {
234 if (WillHandleTapOrLongPress(location))
235 return true;
236
237 longpress_drag_selector_.OnLongPressEvent(event_time, location); 206 longpress_drag_selector_.OnLongPressEvent(event_time, location);
238 response_pending_input_event_ = LONG_PRESS; 207 response_pending_input_event_ = LONG_PRESS;
239 ShowSelectionHandlesAutomatically();
240 ShowInsertionHandleAutomatically();
241 ForceNextUpdateIfInactive();
242 return false;
243 } 208 }
244 209
245 void TouchSelectionController::OnScrollBeginEvent() { 210 void TouchSelectionController::OnScrollBeginEvent() {
246 // When there is an active selection, if the user performs a long-press that 211 // When there is an active selection, if the user performs a long-press that
247 // does not trigger a new selection (e.g. a long-press on an empty area) and 212 // does not trigger a new selection (e.g. a long-press on an empty area) and
248 // then scrolls, the scroll will move the selection. In this case we will 213 // then scrolls, the scroll will move the selection. In this case we will
249 // think incorrectly that the selection change was due to the long-press and 214 // think incorrectly that the selection change was due to the long-press and
250 // will activate touch selection and start long-press drag gesture (see 215 // will activate touch selection and start long-press drag gesture (see
251 // ActivateInsertionIfNecessary()). To prevent this, we need to reset the 216 // ActivateInsertionIfNecessary()). To prevent this, we need to reset the
252 // state of touch selection controller and long-press drag selector. 217 // state of touch selection controller and long-press drag selector.
253 // TODO(mohsen): Remove this workaround when we have enough information about 218 // TODO(mohsen): Remove this workaround when we have enough information about
254 // the cause of a selection change (see https://crbug.com/571897). 219 // the cause of a selection change (see https://crbug.com/571897).
255 longpress_drag_selector_.OnScrollBeginEvent(); 220 longpress_drag_selector_.OnScrollBeginEvent();
256 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; 221 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
257 if (active_status_ == INACTIVE) {
258 activate_insertion_automatically_ = false;
259 activate_selection_automatically_ = false;
260 }
261 }
262
263 void TouchSelectionController::AllowShowingFromCurrentSelection() {
264 if (active_status_ != INACTIVE)
265 return;
266
267 activate_selection_automatically_ = true;
268 activate_insertion_automatically_ = true;
269 if (GetStartPosition() != GetEndPosition()) {
270 OnSelectionChanged();
271 } else if (start_orientation_ == TouchHandleOrientation::CENTER &&
272 selection_editable_) {
273 OnInsertionChanged();
274 }
275 } 222 }
276 223
277 void TouchSelectionController::HideAndDisallowShowingAutomatically() { 224 void TouchSelectionController::HideAndDisallowShowingAutomatically() {
278 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; 225 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
279 DeactivateInsertion(); 226 DeactivateInsertion();
280 DeactivateSelection(); 227 DeactivateSelection();
281 activate_insertion_automatically_ = false; 228 start_ = gfx::SelectionBound();
282 activate_selection_automatically_ = false; 229 end_ = gfx::SelectionBound();
230 start_orientation_ = ToTouchHandleOrientation(start_.type());
231 end_orientation_ = ToTouchHandleOrientation(end_.type());
283 } 232 }
284 233
285 void TouchSelectionController::SetTemporarilyHidden(bool hidden) { 234 void TouchSelectionController::SetTemporarilyHidden(bool hidden) {
286 if (temporarily_hidden_ == hidden) 235 if (temporarily_hidden_ == hidden)
287 return; 236 return;
288 temporarily_hidden_ = hidden; 237 temporarily_hidden_ = hidden;
289 RefreshHandleVisibility(); 238 RefreshHandleVisibility();
290 } 239 }
291 240
292 void TouchSelectionController::OnSelectionEditable(bool editable) {
293 if (selection_editable_ == editable)
294 return;
295 selection_editable_ = editable;
296 ForceNextUpdateIfInactive();
297 if (!selection_editable_)
298 DeactivateInsertion();
299 }
300
301 void TouchSelectionController::OnSelectionEmpty(bool empty) {
302 if (selection_empty_ == empty)
303 return;
304 selection_empty_ = empty;
305 ForceNextUpdateIfInactive();
306 }
307
308 bool TouchSelectionController::Animate(base::TimeTicks frame_time) { 241 bool TouchSelectionController::Animate(base::TimeTicks frame_time) {
309 if (active_status_ == INSERTION_ACTIVE) 242 if (active_status_ == INSERTION_ACTIVE)
310 return insertion_handle_->Animate(frame_time); 243 return insertion_handle_->Animate(frame_time);
311 244
312 if (active_status_ == SELECTION_ACTIVE) { 245 if (active_status_ == SELECTION_ACTIVE) {
313 bool needs_animate = start_selection_handle_->Animate(frame_time); 246 bool needs_animate = start_selection_handle_->Animate(frame_time);
314 needs_animate |= end_selection_handle_->Animate(frame_time); 247 needs_animate |= end_selection_handle_->Animate(frame_time);
315 return needs_animate; 248 return needs_animate;
316 } 249 }
317 250
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
454 } 387 }
455 388
456 gfx::PointF TouchSelectionController::GetSelectionStart() const { 389 gfx::PointF TouchSelectionController::GetSelectionStart() const {
457 return GetStartPosition(); 390 return GetStartPosition();
458 } 391 }
459 392
460 gfx::PointF TouchSelectionController::GetSelectionEnd() const { 393 gfx::PointF TouchSelectionController::GetSelectionEnd() const {
461 return GetEndPosition(); 394 return GetEndPosition();
462 } 395 }
463 396
464 void TouchSelectionController::ShowInsertionHandleAutomatically() {
465 if (activate_insertion_automatically_)
466 return;
467 activate_insertion_automatically_ = true;
468 ForceNextUpdateIfInactive();
469 }
470
471 void TouchSelectionController::ShowSelectionHandlesAutomatically() {
472 if (activate_selection_automatically_)
473 return;
474 activate_selection_automatically_ = true;
475 ForceNextUpdateIfInactive();
476 }
477
478 bool TouchSelectionController::WillHandleTapOrLongPress(
479 const gfx::PointF& location) {
480 // If there is an active selection that was not triggered by a user gesture,
481 // allow showing the handles for that selection if a gesture occurs within
482 // the selection rect. Note that this hit test is at best a crude
483 // approximation, and may swallow taps that actually fall outside the
484 // real selection.
485 if (active_status_ == INACTIVE &&
486 GetStartPosition() != GetEndPosition() &&
487 RectFBetweenSelectionBounds(start_, end_).Contains(location)) {
488 AllowShowingFromCurrentSelection();
489 return true;
490 }
491 return false;
492 }
493
494 void TouchSelectionController::OnInsertionChanged() { 397 void TouchSelectionController::OnInsertionChanged() {
495 DeactivateSelection(); 398 DeactivateSelection();
496 399
497 if ((response_pending_input_event_ == TAP ||
498 response_pending_input_event_ == REPEATED_TAP) &&
499 selection_empty_) {
500 HideAndDisallowShowingAutomatically();
501 return;
502 }
503
504 if (!activate_insertion_automatically_)
505 return;
506
507 const bool activated = ActivateInsertionIfNecessary(); 400 const bool activated = ActivateInsertionIfNecessary();
508 401
509 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated); 402 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated);
510 insertion_handle_->SetFocus(start_.edge_top(), start_.edge_bottom()); 403 insertion_handle_->SetFocus(start_.edge_top(), start_.edge_bottom());
511 insertion_handle_->SetVisible(GetStartVisible(), animation); 404 insertion_handle_->SetVisible(GetStartVisible(), animation);
512 405
513 UpdateHandleLayoutIfNecessary(); 406 UpdateHandleLayoutIfNecessary();
514 407
515 client_->OnSelectionEvent(activated ? INSERTION_HANDLE_SHOWN 408 client_->OnSelectionEvent(activated ? INSERTION_HANDLE_SHOWN
516 : INSERTION_HANDLE_MOVED); 409 : INSERTION_HANDLE_MOVED);
517 } 410 }
518 411
519 void TouchSelectionController::OnSelectionChanged() { 412 void TouchSelectionController::OnSelectionChanged() {
520 DeactivateInsertion(); 413 DeactivateInsertion();
521 414
522 if (!activate_selection_automatically_)
523 return;
524
525 const bool activated = ActivateSelectionIfNecessary(); 415 const bool activated = ActivateSelectionIfNecessary();
526 416
527 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated); 417 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated);
528 418
529 start_selection_handle_->SetFocus(start_.edge_top(), start_.edge_bottom()); 419 start_selection_handle_->SetFocus(start_.edge_top(), start_.edge_bottom());
530 end_selection_handle_->SetFocus(end_.edge_top(), end_.edge_bottom()); 420 end_selection_handle_->SetFocus(end_.edge_top(), end_.edge_bottom());
531 421
532 start_selection_handle_->SetOrientation(start_orientation_); 422 start_selection_handle_->SetOrientation(start_orientation_);
533 end_selection_handle_->SetOrientation(end_orientation_); 423 end_selection_handle_->SetOrientation(end_orientation_);
534 424
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 DCHECK(start_selection_handle_); 503 DCHECK(start_selection_handle_);
614 DCHECK(end_selection_handle_); 504 DCHECK(end_selection_handle_);
615 LogSelectionEnd(); 505 LogSelectionEnd();
616 longpress_drag_selector_.OnSelectionDeactivated(); 506 longpress_drag_selector_.OnSelectionDeactivated();
617 start_selection_handle_->SetEnabled(false); 507 start_selection_handle_->SetEnabled(false);
618 end_selection_handle_->SetEnabled(false); 508 end_selection_handle_->SetEnabled(false);
619 active_status_ = INACTIVE; 509 active_status_ = INACTIVE;
620 client_->OnSelectionEvent(SELECTION_HANDLES_CLEARED); 510 client_->OnSelectionEvent(SELECTION_HANDLES_CLEARED);
621 } 511 }
622 512
623 void TouchSelectionController::ForceNextUpdateIfInactive() {
624 // Only force the update if the reported selection is non-empty but still
625 // considered "inactive", i.e., it wasn't preceded by a user gesture or
626 // the handles have since been explicitly hidden.
627 if (active_status_ == INACTIVE &&
628 start_.type() != gfx::SelectionBound::EMPTY &&
629 end_.type() != gfx::SelectionBound::EMPTY) {
630 force_next_update_ = true;
631 }
632 }
633
634 void TouchSelectionController::UpdateHandleLayoutIfNecessary() { 513 void TouchSelectionController::UpdateHandleLayoutIfNecessary() {
635 if (active_status_ == INSERTION_ACTIVE) { 514 if (active_status_ == INSERTION_ACTIVE) {
636 DCHECK(insertion_handle_); 515 DCHECK(insertion_handle_);
637 insertion_handle_->UpdateHandleLayout(); 516 insertion_handle_->UpdateHandleLayout();
638 } else if (active_status_ == SELECTION_ACTIVE) { 517 } else if (active_status_ == SELECTION_ACTIVE) {
639 DCHECK(start_selection_handle_); 518 DCHECK(start_selection_handle_);
640 DCHECK(end_selection_handle_); 519 DCHECK(end_selection_handle_);
641 start_selection_handle_->UpdateHandleLayout(); 520 start_selection_handle_->UpdateHandleLayout();
642 end_selection_handle_->UpdateHandleLayout(); 521 end_selection_handle_->UpdateHandleLayout();
643 } 522 }
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
694 base::TimeDelta duration = base::TimeTicks::Now() - selection_start_time_; 573 base::TimeDelta duration = base::TimeTicks::Now() - selection_start_time_;
695 UMA_HISTOGRAM_CUSTOM_TIMES("Event.TouchSelection.WasDraggedDuration", 574 UMA_HISTOGRAM_CUSTOM_TIMES("Event.TouchSelection.WasDraggedDuration",
696 duration, 575 duration,
697 base::TimeDelta::FromMilliseconds(500), 576 base::TimeDelta::FromMilliseconds(500),
698 base::TimeDelta::FromSeconds(60), 577 base::TimeDelta::FromSeconds(60),
699 60); 578 60);
700 } 579 }
701 } 580 }
702 581
703 } // namespace ui 582 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698