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

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: fixed contextual search test function 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),
mohsen 2016/10/15 03:49:36 Is |response_pending_input_event_| variable still
amaralp 2016/10/19 04:26:38 I don't think it is needed. If it's OK with you I'
mohsen 2016/10/19 16:08:34 Sure. Maybe just add a TODO.
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 consume_touch_sequence_(false) { 67 consume_touch_sequence_(false) {
73 DCHECK(client_); 68 DCHECK(client_);
74 } 69 }
75 70
76 TouchSelectionController::~TouchSelectionController() { 71 TouchSelectionController::~TouchSelectionController() {
77 } 72 }
78 73
79 void TouchSelectionController::OnSelectionBoundsChanged( 74 void TouchSelectionController::OnSelectionBoundsChanged(
80 const gfx::SelectionBound& start, 75 const gfx::SelectionBound& start,
81 const gfx::SelectionBound& end) { 76 const gfx::SelectionBound& end) {
82 if (!force_next_update_ && start == start_ && end_ == end) 77 if (start == start_ && end_ == end)
83 return; 78 return;
84 79
85 // Notify if selection bounds have just been established or dissolved. 80 if (start.type() == gfx::SelectionBound::EMPTY
86 if (start.type() != gfx::SelectionBound::EMPTY && 81 || end.type() == gfx::SelectionBound::EMPTY) {
mohsen 2016/10/15 03:49:36 nit: putting || at the end of the previous line wo
amaralp 2016/10/19 04:26:38 done
87 start_.type() == gfx::SelectionBound::EMPTY) { 82 HideAndDisallowShowingAutomatically();
88 client_->OnSelectionEvent(SELECTION_ESTABLISHED); 83 return;
89 } else if (start.type() == gfx::SelectionBound::EMPTY &&
90 start_.type() != gfx::SelectionBound::EMPTY) {
91 client_->OnSelectionEvent(SELECTION_DISSOLVED);
92 } 84 }
93 85
94 // Swap the Handles when the start and end selection points cross each other. 86 // Swap the Handles when the start and end selection points cross each other.
95 if (active_status_ == SELECTION_ACTIVE) { 87 if (active_status_ == SELECTION_ACTIVE) {
96 if ((start_selection_handle_->IsActive() && 88 if ((start_selection_handle_->IsActive() &&
97 end_.edge_bottom() == start.edge_bottom()) || 89 end_.edge_bottom() == start.edge_bottom()) ||
98 (end_selection_handle_->IsActive() && 90 (end_selection_handle_->IsActive() &&
99 end.edge_bottom() == start_.edge_bottom())) { 91 end.edge_bottom() == start_.edge_bottom())) {
100 start_selection_handle_.swap(end_selection_handle_); 92 start_selection_handle_.swap(end_selection_handle_);
101 } 93 }
102 } 94 }
103 95
104 start_ = start; 96 start_ = start;
105 end_ = end; 97 end_ = end;
106 start_orientation_ = ToTouchHandleOrientation(start_.type()); 98 start_orientation_ = ToTouchHandleOrientation(start_.type());
107 end_orientation_ = ToTouchHandleOrientation(end_.type()); 99 end_orientation_ = ToTouchHandleOrientation(end_.type());
108 force_next_update_ = false;
109
110 if (!activate_selection_automatically_ &&
111 !activate_insertion_automatically_) {
112 DCHECK_EQ(INACTIVE, active_status_);
113 DCHECK_EQ(INPUT_EVENT_TYPE_NONE, response_pending_input_event_);
114 return;
115 }
116 100
117 // Ensure that |response_pending_input_event_| is cleared after the method 101 // Ensure that |response_pending_input_event_| is cleared after the method
118 // completes, while also making its current value available for the duration 102 // completes, while also making its current value available for the duration
119 // of the call. 103 // of the call.
120 InputEventType causal_input_event = response_pending_input_event_; 104 InputEventType causal_input_event = response_pending_input_event_;
121 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; 105 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
122 base::AutoReset<InputEventType> auto_reset_response_pending_input_event( 106 base::AutoReset<InputEventType> auto_reset_response_pending_input_event(
123 &response_pending_input_event_, causal_input_event); 107 &response_pending_input_event_, causal_input_event);
124 108
125 const bool is_selection_dragging = active_status_ == SELECTION_ACTIVE && 109 const bool is_selection_dragging = active_status_ == SELECTION_ACTIVE &&
(...skipping 10 matching lines...) Expand all
136 } 120 }
137 121
138 if (GetStartPosition() != GetEndPosition() || 122 if (GetStartPosition() != GetEndPosition() ||
139 (is_selection_dragging && 123 (is_selection_dragging &&
140 start_orientation_ != TouchHandleOrientation::UNDEFINED && 124 start_orientation_ != TouchHandleOrientation::UNDEFINED &&
141 end_orientation_ != TouchHandleOrientation::UNDEFINED)) { 125 end_orientation_ != TouchHandleOrientation::UNDEFINED)) {
142 OnSelectionChanged(); 126 OnSelectionChanged();
143 return; 127 return;
144 } 128 }
145 129
146 if (start_orientation_ == TouchHandleOrientation::CENTER && 130 if (start_orientation_ == TouchHandleOrientation::CENTER) {
147 selection_editable_) {
148 OnInsertionChanged(); 131 OnInsertionChanged();
149 return; 132 return;
150 } 133 }
151 134
152 HideAndDisallowShowingAutomatically(); 135 HideAndDisallowShowingAutomatically();
153 } 136 }
154 137
155 void TouchSelectionController::OnViewportChanged( 138 void TouchSelectionController::OnViewportChanged(
156 const gfx::RectF viewport_rect) { 139 const gfx::RectF viewport_rect) {
157 // Trigger a force update if the viewport is changed, so that 140 // Trigger a force update if the viewport is changed, so that
(...skipping 26 matching lines...) Expand all
184 // too, regardless of value of |handled|. 167 // too, regardless of value of |handled|.
185 // TODO(mohsen): This will consume touch events until the next ACTION_DOWN. 168 // TODO(mohsen): This will consume touch events until the next ACTION_DOWN.
186 // Ideally we should consume until the final ACTION_UP/ACTION_CANCEL. 169 // Ideally we should consume until the final ACTION_UP/ACTION_CANCEL.
187 // But, apparently, we can't reliably determine the final ACTION_CANCEL in a 170 // But, apparently, we can't reliably determine the final ACTION_CANCEL in a
188 // multi-touch scenario. See https://crbug.com/653212. 171 // multi-touch scenario. See https://crbug.com/653212.
189 if (event.GetAction() == MotionEvent::ACTION_DOWN) 172 if (event.GetAction() == MotionEvent::ACTION_DOWN)
190 consume_touch_sequence_ = handled; 173 consume_touch_sequence_ = handled;
191 return handled || consume_touch_sequence_; 174 return handled || consume_touch_sequence_;
192 } 175 }
193 176
194 bool TouchSelectionController::WillHandleTapEvent(const gfx::PointF& location, 177 void TouchSelectionController::WillHandleTapEvent(const gfx::PointF& location,
195 int tap_count) { 178 int tap_count) {
196 if (WillHandleTapOrLongPress(location))
197 return true;
198
199 if (tap_count > 1) { 179 if (tap_count > 1) {
200 response_pending_input_event_ = REPEATED_TAP; 180 response_pending_input_event_ = REPEATED_TAP;
201 ShowSelectionHandlesAutomatically();
202 } else { 181 } else {
203 response_pending_input_event_ = TAP; 182 response_pending_input_event_ = TAP;
204 if (active_status_ != SELECTION_ACTIVE)
205 activate_selection_automatically_ = false;
206 } 183 }
207 ShowInsertionHandleAutomatically();
208 if (selection_empty_)
209 DeactivateInsertion();
210 ForceNextUpdateIfInactive();
211 return false;
212 } 184 }
213 185
214 bool TouchSelectionController::WillHandleLongPressEvent( 186 void TouchSelectionController::WillHandleLongPressEvent(
215 base::TimeTicks event_time, 187 base::TimeTicks event_time,
216 const gfx::PointF& location) { 188 const gfx::PointF& location) {
217 if (WillHandleTapOrLongPress(location))
218 return true;
219
220 longpress_drag_selector_.OnLongPressEvent(event_time, location); 189 longpress_drag_selector_.OnLongPressEvent(event_time, location);
221 response_pending_input_event_ = LONG_PRESS; 190 response_pending_input_event_ = LONG_PRESS;
222 ShowSelectionHandlesAutomatically();
223 ShowInsertionHandleAutomatically();
224 ForceNextUpdateIfInactive();
225 return false;
226 } 191 }
227 192
228 void TouchSelectionController::OnScrollBeginEvent() { 193 void TouchSelectionController::OnScrollBeginEvent() {
229 // When there is an active selection, if the user performs a long-press that 194 // When there is an active selection, if the user performs a long-press that
230 // does not trigger a new selection (e.g. a long-press on an empty area) and 195 // does not trigger a new selection (e.g. a long-press on an empty area) and
231 // then scrolls, the scroll will move the selection. In this case we will 196 // then scrolls, the scroll will move the selection. In this case we will
232 // think incorrectly that the selection change was due to the long-press and 197 // think incorrectly that the selection change was due to the long-press and
233 // will activate touch selection and start long-press drag gesture (see 198 // will activate touch selection and start long-press drag gesture (see
234 // ActivateInsertionIfNecessary()). To prevent this, we need to reset the 199 // ActivateInsertionIfNecessary()). To prevent this, we need to reset the
235 // state of touch selection controller and long-press drag selector. 200 // state of touch selection controller and long-press drag selector.
236 // TODO(mohsen): Remove this workaround when we have enough information about 201 // TODO(mohsen): Remove this workaround when we have enough information about
237 // the cause of a selection change (see https://crbug.com/571897). 202 // the cause of a selection change (see https://crbug.com/571897).
238 longpress_drag_selector_.OnScrollBeginEvent(); 203 longpress_drag_selector_.OnScrollBeginEvent();
239 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; 204 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
240 if (active_status_ == INACTIVE) {
241 activate_insertion_automatically_ = false;
242 activate_selection_automatically_ = false;
243 }
244 }
245
246 void TouchSelectionController::AllowShowingFromCurrentSelection() {
247 if (active_status_ != INACTIVE)
248 return;
249
250 activate_selection_automatically_ = true;
251 activate_insertion_automatically_ = true;
252 if (GetStartPosition() != GetEndPosition()) {
253 OnSelectionChanged();
254 } else if (start_orientation_ == TouchHandleOrientation::CENTER &&
255 selection_editable_) {
256 OnInsertionChanged();
257 }
258 } 205 }
259 206
260 void TouchSelectionController::HideAndDisallowShowingAutomatically() { 207 void TouchSelectionController::HideAndDisallowShowingAutomatically() {
261 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; 208 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
262 DeactivateInsertion(); 209 DeactivateInsertion();
263 DeactivateSelection(); 210 DeactivateSelection();
264 activate_insertion_automatically_ = false; 211 start_ = gfx::SelectionBound();
265 activate_selection_automatically_ = false; 212 end_ = gfx::SelectionBound();
213 start_orientation_ = ToTouchHandleOrientation(start_.type());
214 end_orientation_ = ToTouchHandleOrientation(end_.type());
266 } 215 }
267 216
268 void TouchSelectionController::SetTemporarilyHidden(bool hidden) { 217 void TouchSelectionController::SetTemporarilyHidden(bool hidden) {
269 if (temporarily_hidden_ == hidden) 218 if (temporarily_hidden_ == hidden)
270 return; 219 return;
271 temporarily_hidden_ = hidden; 220 temporarily_hidden_ = hidden;
272 RefreshHandleVisibility(); 221 RefreshHandleVisibility();
273 } 222 }
274 223
275 void TouchSelectionController::OnSelectionEditable(bool editable) {
276 if (selection_editable_ == editable)
277 return;
278 selection_editable_ = editable;
279 ForceNextUpdateIfInactive();
280 if (!selection_editable_)
281 DeactivateInsertion();
282 }
283
284 void TouchSelectionController::OnSelectionEmpty(bool empty) {
285 if (selection_empty_ == empty)
286 return;
287 selection_empty_ = empty;
288 ForceNextUpdateIfInactive();
289 }
290
291 bool TouchSelectionController::Animate(base::TimeTicks frame_time) { 224 bool TouchSelectionController::Animate(base::TimeTicks frame_time) {
292 if (active_status_ == INSERTION_ACTIVE) 225 if (active_status_ == INSERTION_ACTIVE)
293 return insertion_handle_->Animate(frame_time); 226 return insertion_handle_->Animate(frame_time);
294 227
295 if (active_status_ == SELECTION_ACTIVE) { 228 if (active_status_ == SELECTION_ACTIVE) {
296 bool needs_animate = start_selection_handle_->Animate(frame_time); 229 bool needs_animate = start_selection_handle_->Animate(frame_time);
297 needs_animate |= end_selection_handle_->Animate(frame_time); 230 needs_animate |= end_selection_handle_->Animate(frame_time);
298 return needs_animate; 231 return needs_animate;
299 } 232 }
300 233
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 } 402 }
470 403
471 gfx::PointF TouchSelectionController::GetSelectionStart() const { 404 gfx::PointF TouchSelectionController::GetSelectionStart() const {
472 return GetStartPosition(); 405 return GetStartPosition();
473 } 406 }
474 407
475 gfx::PointF TouchSelectionController::GetSelectionEnd() const { 408 gfx::PointF TouchSelectionController::GetSelectionEnd() const {
476 return GetEndPosition(); 409 return GetEndPosition();
477 } 410 }
478 411
479 void TouchSelectionController::ShowInsertionHandleAutomatically() {
480 if (activate_insertion_automatically_)
481 return;
482 activate_insertion_automatically_ = true;
483 ForceNextUpdateIfInactive();
484 }
485
486 void TouchSelectionController::ShowSelectionHandlesAutomatically() {
487 if (activate_selection_automatically_)
488 return;
489 activate_selection_automatically_ = true;
490 ForceNextUpdateIfInactive();
491 }
492
493 bool TouchSelectionController::WillHandleTapOrLongPress(
494 const gfx::PointF& location) {
495 // If there is an active selection that was not triggered by a user gesture,
496 // allow showing the handles for that selection if a gesture occurs within
497 // the selection rect. Note that this hit test is at best a crude
498 // approximation, and may swallow taps that actually fall outside the
499 // real selection.
500 if (active_status_ == INACTIVE &&
501 GetStartPosition() != GetEndPosition() &&
502 RectFBetweenSelectionBounds(start_, end_).Contains(location)) {
503 AllowShowingFromCurrentSelection();
504 return true;
505 }
506 return false;
507 }
508
509 void TouchSelectionController::OnInsertionChanged() { 412 void TouchSelectionController::OnInsertionChanged() {
510 DeactivateSelection(); 413 DeactivateSelection();
511 414
512 if ((response_pending_input_event_ == TAP ||
513 response_pending_input_event_ == REPEATED_TAP) &&
514 selection_empty_) {
515 HideAndDisallowShowingAutomatically();
516 return;
517 }
518
519 if (!activate_insertion_automatically_)
520 return;
521
522 const bool activated = ActivateInsertionIfNecessary(); 415 const bool activated = ActivateInsertionIfNecessary();
523 416
524 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated); 417 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated);
525 insertion_handle_->SetFocus(start_.edge_top(), start_.edge_bottom()); 418 insertion_handle_->SetFocus(start_.edge_top(), start_.edge_bottom());
526 insertion_handle_->SetVisible(GetStartVisible(), animation); 419 insertion_handle_->SetVisible(GetStartVisible(), animation);
527 420
528 UpdateHandleLayoutIfNecessary(); 421 UpdateHandleLayoutIfNecessary();
529 422
530 client_->OnSelectionEvent(activated ? INSERTION_HANDLE_SHOWN 423 client_->OnSelectionEvent(activated ? INSERTION_HANDLE_SHOWN
531 : INSERTION_HANDLE_MOVED); 424 : INSERTION_HANDLE_MOVED);
532 } 425 }
533 426
534 void TouchSelectionController::OnSelectionChanged() { 427 void TouchSelectionController::OnSelectionChanged() {
535 DeactivateInsertion(); 428 DeactivateInsertion();
536 429
537 if (!activate_selection_automatically_)
538 return;
539
540 const bool activated = ActivateSelectionIfNecessary(); 430 const bool activated = ActivateSelectionIfNecessary();
541 431
542 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated); 432 const TouchHandle::AnimationStyle animation = GetAnimationStyle(!activated);
543 433
544 start_selection_handle_->SetFocus(start_.edge_top(), start_.edge_bottom()); 434 start_selection_handle_->SetFocus(start_.edge_top(), start_.edge_bottom());
545 end_selection_handle_->SetFocus(end_.edge_top(), end_.edge_bottom()); 435 end_selection_handle_->SetFocus(end_.edge_top(), end_.edge_bottom());
546 436
547 start_selection_handle_->SetOrientation(start_orientation_); 437 start_selection_handle_->SetOrientation(start_orientation_);
548 end_selection_handle_->SetOrientation(end_orientation_); 438 end_selection_handle_->SetOrientation(end_orientation_);
549 439
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
628 DCHECK(start_selection_handle_); 518 DCHECK(start_selection_handle_);
629 DCHECK(end_selection_handle_); 519 DCHECK(end_selection_handle_);
630 LogSelectionEnd(); 520 LogSelectionEnd();
631 longpress_drag_selector_.OnSelectionDeactivated(); 521 longpress_drag_selector_.OnSelectionDeactivated();
632 start_selection_handle_->SetEnabled(false); 522 start_selection_handle_->SetEnabled(false);
633 end_selection_handle_->SetEnabled(false); 523 end_selection_handle_->SetEnabled(false);
634 active_status_ = INACTIVE; 524 active_status_ = INACTIVE;
635 client_->OnSelectionEvent(SELECTION_HANDLES_CLEARED); 525 client_->OnSelectionEvent(SELECTION_HANDLES_CLEARED);
636 } 526 }
637 527
638 void TouchSelectionController::ForceNextUpdateIfInactive() {
639 // Only force the update if the reported selection is non-empty but still
640 // considered "inactive", i.e., it wasn't preceded by a user gesture or
641 // the handles have since been explicitly hidden.
642 if (active_status_ == INACTIVE &&
643 start_.type() != gfx::SelectionBound::EMPTY &&
644 end_.type() != gfx::SelectionBound::EMPTY) {
645 force_next_update_ = true;
646 }
647 }
648
649 void TouchSelectionController::UpdateHandleLayoutIfNecessary() { 528 void TouchSelectionController::UpdateHandleLayoutIfNecessary() {
650 if (active_status_ == INSERTION_ACTIVE) { 529 if (active_status_ == INSERTION_ACTIVE) {
651 DCHECK(insertion_handle_); 530 DCHECK(insertion_handle_);
652 insertion_handle_->UpdateHandleLayout(); 531 insertion_handle_->UpdateHandleLayout();
653 } else if (active_status_ == SELECTION_ACTIVE) { 532 } else if (active_status_ == SELECTION_ACTIVE) {
654 DCHECK(start_selection_handle_); 533 DCHECK(start_selection_handle_);
655 DCHECK(end_selection_handle_); 534 DCHECK(end_selection_handle_);
656 start_selection_handle_->UpdateHandleLayout(); 535 start_selection_handle_->UpdateHandleLayout();
657 end_selection_handle_->UpdateHandleLayout(); 536 end_selection_handle_->UpdateHandleLayout();
658 } 537 }
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
709 base::TimeDelta duration = base::TimeTicks::Now() - selection_start_time_; 588 base::TimeDelta duration = base::TimeTicks::Now() - selection_start_time_;
710 UMA_HISTOGRAM_CUSTOM_TIMES("Event.TouchSelection.WasDraggedDuration", 589 UMA_HISTOGRAM_CUSTOM_TIMES("Event.TouchSelection.WasDraggedDuration",
711 duration, 590 duration,
712 base::TimeDelta::FromMilliseconds(500), 591 base::TimeDelta::FromMilliseconds(500),
713 base::TimeDelta::FromSeconds(60), 592 base::TimeDelta::FromSeconds(60),
714 60); 593 60);
715 } 594 }
716 } 595 }
717 596
718 } // namespace ui 597 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698