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

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

Issue 1087893003: Support longpress drag selection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Factor out logic 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
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/touch_selection/longpress_drag_selector.h"
6
7 #include "ui/events/gesture_detection/motion_event.h"
8
9 namespace ui {
10 namespace {
11
12 const int kLongPressTimeEpsilonMicros = 10;
13
14 } // namespace
15
16 LongPressDragSelector::LongPressDragSelector(
17 TouchSelectionDraggableClient* client,
18 float slop_length)
19 : client_(client), slop_length_(slop_length), state_(INACTIVE) {
20 }
21
22 LongPressDragSelector::~LongPressDragSelector() {
23 }
24
25 bool LongPressDragSelector::WillHandleTouchEvent(const MotionEvent& event) {
26 switch (event.GetAction()) {
27 case MotionEvent::ACTION_DOWN:
28 touch_down_position_.SetPoint(event.GetX(), event.GetY());
29 touch_down_time_ = event.GetEventTime();
30 longpress_drag_start_position_ = gfx::PointF();
31 SetState(LONGPRESS_PENDING);
32 return false;
33
34 case MotionEvent::ACTION_UP:
35 case MotionEvent::ACTION_CANCEL:
36 SetState(INACTIVE);
37 return false;
38
39 case MotionEvent::ACTION_MOVE:
40 break;
41
42 default:
43 return false;
44 }
45
46 if (state_ != DRAG_PENDING && state_ != DRAGGING)
47 return false;
48
49 gfx::PointF position(event.GetX(), event.GetY());
50 if (state_ == DRAGGING) {
51 gfx::PointF drag_position = position + longpress_drag_selection_offset_;
52 client_->OnDragUpdate(*this, drag_position);
53 return true;
54 }
55
56 // We can't use |touch_down_position_| as the offset anchor, as
57 // showing the selection UI may have shifted the motion coordinates.
58 if (longpress_drag_start_position_.IsOrigin()) {
59 longpress_drag_start_position_ = position;
60 return true;
61 }
62
63 // Allow an additional slop affordance after the longpress occurs.
64 gfx::Vector2dF delta = position - longpress_drag_start_position_;
65 if (delta.LengthSquared() < (slop_length_ * slop_length_))
66 return true;
67
68 gfx::PointF base = selection_start_;
69 gfx::PointF extent = selection_end_;
70 if (std::abs(delta.y()) > std::abs(delta.x())) {
71 // If initial motion is upward, extend the starting selection bound.
mfomitchev 2015/04/23 21:05:53 By the similar logic, wouldn't we want to extend t
jdduke (slow) 2015/04/27 20:24:25 We do, the exceptional case is dragging 'backward'
mfomitchev 2015/04/28 02:17:30 Ah, right, I misread the code.
72 if (delta.y() < 0)
73 std::swap(base, extent);
74 } else {
75 // Otherwise extend the selection bound toward which we're moving.
76 gfx::Vector2dF start_delta = selection_start_ - position;
77 if (!start_delta.IsZero())
78 start_delta.Scale(1.f / start_delta.Length());
79 gfx::Vector2dF end_delta = selection_end_ - position;
80 if (!end_delta.IsZero())
81 end_delta.Scale(1.f / start_delta.Length());
82 if (gfx::DotProduct(start_delta, delta) >
mfomitchev 2015/04/23 21:05:53 Heh. Smart. Long-press is going to select a single
jdduke (slow) 2015/04/27 20:24:25 Yeah, without awareness of text direction, making
mfomitchev 2015/04/28 02:17:30 We can already make the decision based on whether
jdduke (slow) 2015/04/28 20:37:48 Hmm, yeah, the math isn't quite as clear. That s
83 gfx::DotProduct(end_delta, delta)) {
84 std::swap(base, extent);
85 }
86 }
87 longpress_drag_selection_offset_ = extent - position;
88 client_->OnDragBegin(*this, extent);
89 SetState(DRAGGING);
90 return true;
91 }
92
93 void LongPressDragSelector::OnLongPressEvent(base::TimeTicks event_time,
94 const gfx::PointF& position) {
95 // We have no guarantees that the current gesture stream is aligned with the
96 // observed touch stream. We only know that the gesture sequence is downstream
97 // from the touch sequence. Using a time/distance heuristic helps ensure that
98 // the observed longpress corresponds to the active touch sequence.
99 if (state_ == LONGPRESS_PENDING &&
100 // Use a small time epsilon to account for floating point time conversion.
101 (touch_down_time_ - event_time) <
102 base::TimeDelta::FromMicroseconds(kLongPressTimeEpsilonMicros) &&
103 ((touch_down_position_ - position).LengthSquared() <
104 (slop_length_ * slop_length_))) {
105 SetState(SELECTION_PENDING);
106 }
107 }
108
109 void LongPressDragSelector::OnSelectionActivated(const gfx::PointF& start,
110 const gfx::PointF& end) {
111 if (state_ == SELECTION_PENDING) {
112 SetState(DRAG_PENDING);
113 selection_start_ = start;
114 selection_end_ = end;
115 }
116 }
117
118 void LongPressDragSelector::OnSelectionDeactivated() {
119 SetState(INACTIVE);
120 }
121
122 bool LongPressDragSelector::IsActive() const {
mfomitchev 2015/04/23 21:05:53 This is similar to is_dragging() in TouchHandle. P
jdduke (slow) 2015/04/27 20:24:25 Hmm, the current result would be slightly misleadi
mfomitchev 2015/04/28 02:17:30 Sorry, I should've clarified my comment. I was sug
jdduke (slow) 2015/04/28 20:37:48 IsActive() sounds good to me.
123 return state_ != INACTIVE && state_ != LONGPRESS_PENDING;
124 }
125
126 void LongPressDragSelector::SetState(SelectionState state) {
127 if (state_ == state)
128 return;
129
130 const bool was_dragging = state_ == DRAGGING;
131 state_ = state;
132
133 // TODO(jdduke): Add UMA for tracking relative longpress drag frequency.
134 if (was_dragging)
135 client_->OnDragEnd(*this);
136 }
137
138 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698