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

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

Issue 1087893003: Support longpress drag selection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Cleanup Created 5 years, 8 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 10
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 response_pending_input_event_(INPUT_EVENT_TYPE_NONE), 52 response_pending_input_event_(INPUT_EVENT_TYPE_NONE),
53 start_orientation_(TouchHandleOrientation::UNDEFINED), 53 start_orientation_(TouchHandleOrientation::UNDEFINED),
54 end_orientation_(TouchHandleOrientation::UNDEFINED), 54 end_orientation_(TouchHandleOrientation::UNDEFINED),
55 is_insertion_active_(false), 55 is_insertion_active_(false),
56 activate_insertion_automatically_(false), 56 activate_insertion_automatically_(false),
57 is_selection_active_(false), 57 is_selection_active_(false),
58 activate_selection_automatically_(false), 58 activate_selection_automatically_(false),
59 selection_empty_(false), 59 selection_empty_(false),
60 selection_editable_(false), 60 selection_editable_(false),
61 temporarily_hidden_(false), 61 temporarily_hidden_(false),
62 temporarily_hidden_for_longpress_drag_(false),
63 consume_remaining_motion_for_longpress_drag_(false),
64 has_begun_longpress_drag_(false),
65 has_active_touch_sequence_(false),
62 selection_handle_dragged_(false) { 66 selection_handle_dragged_(false) {
63 DCHECK(client_); 67 DCHECK(client_);
64 } 68 }
65 69
66 TouchSelectionController::~TouchSelectionController() { 70 TouchSelectionController::~TouchSelectionController() {
67 } 71 }
68 72
69 void TouchSelectionController::OnSelectionBoundsChanged( 73 void TouchSelectionController::OnSelectionBoundsChanged(
70 const SelectionBound& start, 74 const SelectionBound& start,
71 const SelectionBound& end) { 75 const SelectionBound& end) {
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 if (start_orientation_ == TouchHandleOrientation::CENTER && 123 if (start_orientation_ == TouchHandleOrientation::CENTER &&
120 selection_editable_) { 124 selection_editable_) {
121 OnInsertionChanged(); 125 OnInsertionChanged();
122 return; 126 return;
123 } 127 }
124 128
125 HideAndDisallowShowingAutomatically(); 129 HideAndDisallowShowingAutomatically();
126 } 130 }
127 131
128 bool TouchSelectionController::WillHandleTouchEvent(const MotionEvent& event) { 132 bool TouchSelectionController::WillHandleTouchEvent(const MotionEvent& event) {
133 if (WillHandleTouchEventForLongPressDrag(event))
134 return true;
135
129 if (is_insertion_active_) { 136 if (is_insertion_active_) {
130 DCHECK(insertion_handle_); 137 DCHECK(insertion_handle_);
131 return insertion_handle_->WillHandleTouchEvent(event); 138 return insertion_handle_->WillHandleTouchEvent(event);
132 } 139 }
133 140
134 if (is_selection_active_) { 141 if (is_selection_active_) {
135 DCHECK(start_selection_handle_); 142 DCHECK(start_selection_handle_);
136 DCHECK(end_selection_handle_); 143 DCHECK(end_selection_handle_);
137 if (start_selection_handle_->is_dragging()) 144 if (start_selection_handle_->is_dragging())
138 return start_selection_handle_->WillHandleTouchEvent(event); 145 return start_selection_handle_->WillHandleTouchEvent(event);
139 146
140 if (end_selection_handle_->is_dragging()) 147 if (end_selection_handle_->is_dragging())
141 return end_selection_handle_->WillHandleTouchEvent(event); 148 return end_selection_handle_->WillHandleTouchEvent(event);
142 149
143 const gfx::PointF event_pos(event.GetX(), event.GetY()); 150 const gfx::PointF event_pos(event.GetX(), event.GetY());
144 if ((event_pos - GetStartPosition()).LengthSquared() <= 151 if ((event_pos - GetStartPosition()).LengthSquared() <=
145 (event_pos - GetEndPosition()).LengthSquared()) 152 (event_pos - GetEndPosition()).LengthSquared())
146 return start_selection_handle_->WillHandleTouchEvent(event); 153 return start_selection_handle_->WillHandleTouchEvent(event);
147 else 154 else
148 return end_selection_handle_->WillHandleTouchEvent(event); 155 return end_selection_handle_->WillHandleTouchEvent(event);
149 } 156 }
150 157
151 return false; 158 return false;
152 } 159 }
153 160
154 void TouchSelectionController::OnLongPressEvent() { 161 void TouchSelectionController::OnLongPressEvent(const gfx::PointF& position) {
162 // If the longpress occurs at the same location as start of the active
163 // touch sequence, allow the resulting selection to be dragged by the
164 // remainder of the active touch sequence.
165 if (has_active_touch_sequence_ &&
166 ((touch_sequence_start_position_ - position).LengthSquared() <
mfomitchev 2015/04/18 16:09:08 I must be missing something, but why do we need to
jdduke (slow) 2015/04/21 19:25:57 So, the issue right now is that touch events are f
mfomitchev 2015/04/21 21:08:57 If do #3, then the webpage will get all the touch
jdduke (slow) 2015/04/21 21:28:33 Indeed, we'd be at the mercy of the web page. This
mfomitchev 2015/04/21 22:20:29 Agreed. So.. it seems like we could get a long pre
167 (tap_slop_ * tap_slop_))) {
168 SetTemporarilyHiddenForLongPressDrag(true);
169 }
155 response_pending_input_event_ = LONG_PRESS; 170 response_pending_input_event_ = LONG_PRESS;
156 ShowSelectionHandlesAutomatically(); 171 ShowSelectionHandlesAutomatically();
157 ShowInsertionHandleAutomatically(); 172 ShowInsertionHandleAutomatically();
158 ResetCachedValuesIfInactive(); 173 ResetCachedValuesIfInactive();
159 } 174 }
160 175
161 void TouchSelectionController::AllowShowingFromCurrentSelection() { 176 void TouchSelectionController::AllowShowingFromCurrentSelection() {
162 if (is_selection_active_ || is_insertion_active_) 177 if (is_selection_active_ || is_insertion_active_)
163 return; 178 return;
164 179
(...skipping 19 matching lines...) Expand all
184 DeactivateInsertion(); 199 DeactivateInsertion();
185 DeactivateSelection(); 200 DeactivateSelection();
186 activate_insertion_automatically_ = false; 201 activate_insertion_automatically_ = false;
187 activate_selection_automatically_ = false; 202 activate_selection_automatically_ = false;
188 } 203 }
189 204
190 void TouchSelectionController::SetTemporarilyHidden(bool hidden) { 205 void TouchSelectionController::SetTemporarilyHidden(bool hidden) {
191 if (temporarily_hidden_ == hidden) 206 if (temporarily_hidden_ == hidden)
192 return; 207 return;
193 temporarily_hidden_ = hidden; 208 temporarily_hidden_ = hidden;
194 209 OnHandleVisibilityOverrideChanged();
195 TouchHandle::AnimationStyle animation_style = GetAnimationStyle(true);
196 if (is_selection_active_) {
197 start_selection_handle_->SetVisible(GetStartVisible(), animation_style);
198 end_selection_handle_->SetVisible(GetEndVisible(), animation_style);
199 }
200 if (is_insertion_active_)
201 insertion_handle_->SetVisible(GetStartVisible(), animation_style);
202 } 210 }
203 211
204 void TouchSelectionController::OnSelectionEditable(bool editable) { 212 void TouchSelectionController::OnSelectionEditable(bool editable) {
205 if (selection_editable_ == editable) 213 if (selection_editable_ == editable)
206 return; 214 return;
207 selection_editable_ = editable; 215 selection_editable_ = editable;
208 ResetCachedValuesIfInactive(); 216 ResetCachedValuesIfInactive();
209 if (!selection_editable_) 217 if (!selection_editable_)
210 DeactivateInsertion(); 218 DeactivateInsertion();
211 } 219 }
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
438 if (!is_selection_active_ || response_pending_input_event_ == LONG_PRESS) { 446 if (!is_selection_active_ || response_pending_input_event_ == LONG_PRESS) {
439 if (is_selection_active_) { 447 if (is_selection_active_) {
440 // The active selection session finishes with the start of the new one. 448 // The active selection session finishes with the start of the new one.
441 LogSelectionEnd(); 449 LogSelectionEnd();
442 } 450 }
443 is_selection_active_ = true; 451 is_selection_active_ = true;
444 selection_handle_dragged_ = false; 452 selection_handle_dragged_ = false;
445 selection_start_time_ = base::TimeTicks::Now(); 453 selection_start_time_ = base::TimeTicks::Now();
446 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; 454 response_pending_input_event_ = INPUT_EVENT_TYPE_NONE;
447 client_->OnSelectionEvent(SELECTION_SHOWN); 455 client_->OnSelectionEvent(SELECTION_SHOWN);
456 if (temporarily_hidden_for_longpress_drag_) {
457 consume_remaining_motion_for_longpress_drag_ = true;
458 longpress_drag_selection_offset_ = gfx::Vector2dF();
459 longpress_drag_initial_position_ = gfx::PointF();
460 has_begun_longpress_drag_ = false;
461 }
448 } 462 }
449 } 463 }
450 464
451 void TouchSelectionController::DeactivateSelection() { 465 void TouchSelectionController::DeactivateSelection() {
452 if (!is_selection_active_) 466 if (!is_selection_active_)
453 return; 467 return;
454 DCHECK(start_selection_handle_); 468 DCHECK(start_selection_handle_);
455 DCHECK(end_selection_handle_); 469 DCHECK(end_selection_handle_);
456 LogSelectionEnd(); 470 LogSelectionEnd();
457 start_selection_handle_->SetEnabled(false); 471 start_selection_handle_->SetEnabled(false);
458 end_selection_handle_->SetEnabled(false); 472 end_selection_handle_->SetEnabled(false);
459 is_selection_active_ = false; 473 SetTemporarilyHiddenForLongPressDrag(false);
474 consume_remaining_motion_for_longpress_drag_ = false;
460 client_->OnSelectionEvent(SELECTION_CLEARED); 475 client_->OnSelectionEvent(SELECTION_CLEARED);
461 } 476 }
462 477
463 void TouchSelectionController::ResetCachedValuesIfInactive() { 478 void TouchSelectionController::ResetCachedValuesIfInactive() {
464 if (is_selection_active_ || is_insertion_active_) 479 if (is_selection_active_ || is_insertion_active_)
465 return; 480 return;
466 start_ = SelectionBound(); 481 start_ = SelectionBound();
467 end_ = SelectionBound(); 482 end_ = SelectionBound();
468 start_orientation_ = TouchHandleOrientation::UNDEFINED; 483 start_orientation_ = TouchHandleOrientation::UNDEFINED;
469 end_orientation_ = TouchHandleOrientation::UNDEFINED; 484 end_orientation_ = TouchHandleOrientation::UNDEFINED;
470 } 485 }
471 486
487 bool TouchSelectionController::WillHandleTouchEventForLongPressDrag(
488 const MotionEvent& event) {
489 if (event.GetAction() == MotionEvent::ACTION_DOWN) {
490 has_active_touch_sequence_ = true;
491 touch_sequence_start_position_.SetPoint(event.GetX(), event.GetY());
492 SetTemporarilyHiddenForLongPressDrag(false);
493 consume_remaining_motion_for_longpress_drag_ = false;
494 }
495
496 if (event.GetAction() == MotionEvent::ACTION_UP ||
497 event.GetAction() == MotionEvent::ACTION_CANCEL) {
498 has_active_touch_sequence_ = false;
499 touch_sequence_start_position_ = gfx::PointF();
500 SetTemporarilyHiddenForLongPressDrag(false);
mfomitchev 2015/04/18 16:09:08 It would be nice to do UMA logging for this usecas
501 consume_remaining_motion_for_longpress_drag_ = false;
mfomitchev 2015/04/21 22:20:29 When the user lifts the finger at the end of the l
jdduke (slow) 2015/04/21 22:29:34 Yep, at least, yes if we want to match the native
mfomitchev 2015/04/22 19:19:50 Ah, right. Ok, cool.
502 }
503
504 if (!consume_remaining_motion_for_longpress_drag_)
505 return false;
506
507 if (event.GetAction() != MotionEvent::ACTION_MOVE)
508 return false;
509
510 gfx::PointF position(event.GetX(), event.GetY());
511 if (has_begun_longpress_drag_) {
512 gfx::PointF drag_position = position + longpress_drag_selection_offset_;
513 client_->MoveRangeSelectionExtent(drag_position);
514 return true;
515 }
516
517 // We can't use |touch_sequence_start_position_| as the offset anchor, as
518 // showing the selection UI may have shifted the motion coordinates.
519 if (longpress_drag_initial_position_.IsOrigin()) {
520 longpress_drag_initial_position_ = position;
521 return true;
522 }
523
524 // Allow an additional slop affordance after the longpress occurs.
525 gfx::Vector2dF delta = position - longpress_drag_initial_position_;
526 if (delta.LengthSquared() < tap_slop_ * tap_slop_)
527 return true;
528
529 has_begun_longpress_drag_ = true;
530 gfx::PointF start_position =
531 start_selection_handle_->position() + GetStartLineOffset();
532 gfx::PointF end_position =
533 end_selection_handle_->position() + GetEndLineOffset();
534 gfx::PointF base, extent;
535 if (std::abs(delta.y()) > std::abs(delta.x())) {
536 // If initial motion is vertical, anchor to the appropriate handle position.
537 if (delta.y() < 0) {
538 base = end_position;
539 extent = start_position;
540 } else {
541 base = start_position;
542 extent = end_position;
543 }
544 } else {
545 // Otherwise anchor selection to the handle away from which we're moving.
546 gfx::Vector2dF start_delta = start_position - position;
547 gfx::Vector2dF end_delta = end_position - position;
548 if (gfx::DotProduct(start_delta, delta) >
549 gfx::DotProduct(end_delta, delta)) {
550 base = end_position;
551 extent = start_position;
552 } else {
553 base = start_position;
554 extent = end_position;
555 }
556 }
557 longpress_drag_selection_offset_ = extent - position;
558 client_->SelectBetweenCoordinates(base, extent);
559 return true;
560 }
561
562 void TouchSelectionController::SetTemporarilyHiddenForLongPressDrag(
563 bool hidden) {
564 if (hidden == temporarily_hidden_for_longpress_drag_)
565 return;
566 temporarily_hidden_for_longpress_drag_ = hidden;
567 OnHandleVisibilityOverrideChanged();
568 }
569
570 void TouchSelectionController::OnHandleVisibilityOverrideChanged() {
571 TouchHandle::AnimationStyle animation_style = GetAnimationStyle(true);
572 if (is_selection_active_) {
573 start_selection_handle_->SetVisible(GetStartVisible(), animation_style);
574 end_selection_handle_->SetVisible(GetEndVisible(), animation_style);
575 }
576 if (is_insertion_active_)
577 insertion_handle_->SetVisible(GetStartVisible(), animation_style);
578 }
579
472 gfx::Vector2dF TouchSelectionController::GetStartLineOffset() const { 580 gfx::Vector2dF TouchSelectionController::GetStartLineOffset() const {
473 return ComputeLineOffsetFromBottom(start_); 581 return ComputeLineOffsetFromBottom(start_);
474 } 582 }
475 583
476 gfx::Vector2dF TouchSelectionController::GetEndLineOffset() const { 584 gfx::Vector2dF TouchSelectionController::GetEndLineOffset() const {
477 return ComputeLineOffsetFromBottom(end_); 585 return ComputeLineOffsetFromBottom(end_);
478 } 586 }
479 587
480 bool TouchSelectionController::GetStartVisible() const { 588 bool TouchSelectionController::GetStartVisible() const {
481 return start_.visible() && !temporarily_hidden_; 589 if (!start_.visible())
590 return false;
591
592 return !temporarily_hidden_ && !temporarily_hidden_for_longpress_drag_;
482 } 593 }
483 594
484 bool TouchSelectionController::GetEndVisible() const { 595 bool TouchSelectionController::GetEndVisible() const {
485 return end_.visible() && !temporarily_hidden_; 596 if (!end_.visible())
597 return false;
598
599 return !temporarily_hidden_ && !temporarily_hidden_for_longpress_drag_;
486 } 600 }
487 601
488 TouchHandle::AnimationStyle TouchSelectionController::GetAnimationStyle( 602 TouchHandle::AnimationStyle TouchSelectionController::GetAnimationStyle(
489 bool was_active) const { 603 bool was_active) const {
490 return was_active && client_->SupportsAnimation() 604 return was_active && client_->SupportsAnimation()
491 ? TouchHandle::ANIMATION_SMOOTH 605 ? TouchHandle::ANIMATION_SMOOTH
492 : TouchHandle::ANIMATION_NONE; 606 : TouchHandle::ANIMATION_NONE;
493 } 607 }
494 608
495 void TouchSelectionController::LogSelectionEnd() { 609 void TouchSelectionController::LogSelectionEnd() {
496 // TODO(mfomitchev): Once we are able to tell the difference between 610 // TODO(mfomitchev): Once we are able to tell the difference between
497 // 'successful' and 'unsuccessful' selections - log 611 // 'successful' and 'unsuccessful' selections - log
498 // Event.TouchSelection.Duration instead and get rid of 612 // Event.TouchSelection.Duration instead and get rid of
499 // Event.TouchSelectionD.WasDraggeduration. 613 // Event.TouchSelectionD.WasDraggeduration.
500 if (selection_handle_dragged_) { 614 if (selection_handle_dragged_) {
501 base::TimeDelta duration = base::TimeTicks::Now() - selection_start_time_; 615 base::TimeDelta duration = base::TimeTicks::Now() - selection_start_time_;
502 UMA_HISTOGRAM_CUSTOM_TIMES("Event.TouchSelection.WasDraggedDuration", 616 UMA_HISTOGRAM_CUSTOM_TIMES("Event.TouchSelection.WasDraggedDuration",
503 duration, 617 duration,
504 base::TimeDelta::FromMilliseconds(500), 618 base::TimeDelta::FromMilliseconds(500),
505 base::TimeDelta::FromSeconds(60), 619 base::TimeDelta::FromSeconds(60),
506 60); 620 60);
507 } 621 }
508 } 622 }
509 623
510 } // namespace ui 624 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698