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
3 // found in the LICENSE file.
5 #include "ui/touch_selection/longpress_drag_selector.h"
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "ui/events/test/motion_event_test_utils.h"
10 using ui::test::MockMotionEvent
;
15 const double kSlop
= 10.;
19 class LongPressDragSelectorTest
: public testing::Test
,
20 public LongPressDragSelectorClient
{
22 LongPressDragSelectorTest()
23 : dragging_(false), active_state_changed_(false) {}
25 ~LongPressDragSelectorTest() override
{}
27 void SetSelection(const gfx::PointF
& start
, const gfx::PointF
& end
) {
28 selection_start_
= start
;
32 bool GetAndResetActiveStateChanged() {
33 bool active_state_changed
= active_state_changed_
;
34 active_state_changed_
= false;
35 return active_state_changed
;
38 bool IsDragging() const { return dragging_
; }
39 const gfx::PointF
& DragPosition() const { return drag_position_
; }
41 // LongPressDragSelectorClient implementation.
42 void OnDragBegin(const TouchSelectionDraggable
& handler
,
43 const gfx::PointF
& drag_position
) override
{
45 drag_position_
= drag_position
;
48 void OnDragUpdate(const TouchSelectionDraggable
& handler
,
49 const gfx::PointF
& drag_position
) override
{
50 drag_position_
= drag_position
;
53 void OnDragEnd(const TouchSelectionDraggable
& handler
) override
{
57 bool IsWithinTapSlop(const gfx::Vector2dF
& delta
) const override
{
58 return delta
.LengthSquared() < (kSlop
* kSlop
);
61 void OnLongPressDragActiveStateChanged() override
{
62 active_state_changed_
= true;
65 gfx::PointF
GetSelectionStart() const override
{ return selection_start_
; }
67 gfx::PointF
GetSelectionEnd() const override
{ return selection_end_
; }
71 bool active_state_changed_
;
72 gfx::PointF drag_position_
;
74 gfx::PointF selection_start_
;
75 gfx::PointF selection_end_
;
78 TEST_F(LongPressDragSelectorTest
, BasicDrag
) {
79 LongPressDragSelector
selector(this);
80 MockMotionEvent event
;
82 // Start a touch sequence.
83 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
84 EXPECT_FALSE(GetAndResetActiveStateChanged());
86 // Activate a longpress-triggered selection.
87 gfx::PointF
selection_start(0, 10);
88 gfx::PointF
selection_end(10, 10);
89 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF());
90 EXPECT_TRUE(GetAndResetActiveStateChanged());
92 // Motion should not be consumed until a selection is detected.
93 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
94 SetSelection(selection_start
, selection_end
);
95 selector
.OnSelectionActivated();
96 EXPECT_FALSE(IsDragging());
98 // Initiate drag motion. Note that the first move event after activation is
99 // used to initialize the drag start anchor.
100 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
101 EXPECT_FALSE(IsDragging());
103 // The first slop exceeding motion will start the drag. As the motion is
104 // downward, the end selection point should be moved.
105 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
)));
106 EXPECT_TRUE(IsDragging());
107 EXPECT_EQ(selection_end
, DragPosition());
109 // Subsequent motion will extend the selection.
110 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 2)));
111 EXPECT_TRUE(IsDragging());
112 EXPECT_EQ(selection_end
+ gfx::Vector2dF(0, kSlop
), DragPosition());
113 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 3)));
114 EXPECT_TRUE(IsDragging());
115 EXPECT_EQ(selection_end
+ gfx::Vector2dF(0, kSlop
* 2), DragPosition());
117 // Release the touch sequence, ending the drag. The selector will never
118 // consume the start/end events, only move events after a longpress.
119 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.ReleasePoint()));
120 EXPECT_FALSE(IsDragging());
121 EXPECT_TRUE(GetAndResetActiveStateChanged());
124 TEST_F(LongPressDragSelectorTest
, BasicReverseDrag
) {
125 LongPressDragSelector
selector(this);
126 MockMotionEvent event
;
128 // Start a touch sequence.
129 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
130 EXPECT_FALSE(GetAndResetActiveStateChanged());
132 // Activate a longpress-triggered selection.
133 gfx::PointF
selection_start(0, 10);
134 gfx::PointF
selection_end(10, 10);
135 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF());
136 EXPECT_TRUE(GetAndResetActiveStateChanged());
137 SetSelection(selection_start
, selection_end
);
138 selector
.OnSelectionActivated();
139 EXPECT_FALSE(IsDragging());
141 // Initiate drag motion.
142 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 5, 0)));
143 EXPECT_FALSE(IsDragging());
145 // As the initial motion is leftward, toward the selection start, the
146 // selection start should be the drag point.
147 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, -kSlop
, 0)));
148 EXPECT_TRUE(IsDragging());
149 EXPECT_EQ(selection_start
, DragPosition());
151 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, -kSlop
)));
152 EXPECT_TRUE(IsDragging());
153 EXPECT_EQ(selection_start
+ gfx::Vector2dF(kSlop
, -kSlop
), DragPosition());
155 // Release the touch sequence, ending the drag.
156 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.ReleasePoint()));
157 EXPECT_FALSE(IsDragging());
158 EXPECT_TRUE(GetAndResetActiveStateChanged());
161 TEST_F(LongPressDragSelectorTest
, NoActiveTouch
) {
162 LongPressDragSelector
selector(this);
163 MockMotionEvent event
;
165 // Activate a longpress-triggered selection.
166 gfx::PointF
selection_start(0, 10);
167 gfx::PointF
selection_end(10, 10);
168 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF());
169 SetSelection(selection_start
, selection_end
);
170 selector
.OnSelectionActivated();
171 EXPECT_FALSE(GetAndResetActiveStateChanged());
172 EXPECT_FALSE(IsDragging());
174 // Start a new touch sequence; it shouldn't initiate selection drag as there
175 // was no active touch sequence when the longpress selection started.
176 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
177 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
178 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
)));
179 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 2)));
180 EXPECT_FALSE(IsDragging());
181 EXPECT_EQ(gfx::PointF(), DragPosition());
184 TEST_F(LongPressDragSelectorTest
, NoLongPress
) {
185 LongPressDragSelector
selector(this);
186 MockMotionEvent event
;
188 // Start a touch sequence.
189 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
190 EXPECT_FALSE(GetAndResetActiveStateChanged());
192 // Activate a selection without a preceding longpress.
193 gfx::PointF
selection_start(0, 10);
194 gfx::PointF
selection_end(10, 10);
195 SetSelection(selection_start
, selection_end
);
196 selector
.OnSelectionActivated();
197 EXPECT_FALSE(GetAndResetActiveStateChanged());
198 EXPECT_FALSE(IsDragging());
200 // Touch movement should not initiate selection drag.
201 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
202 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
)));
203 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 2)));
204 EXPECT_FALSE(IsDragging());
205 EXPECT_EQ(gfx::PointF(), DragPosition());
208 TEST_F(LongPressDragSelectorTest
, NoValidLongPress
) {
209 LongPressDragSelector
selector(this);
210 MockMotionEvent event
;
212 // Start a touch sequence.
213 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
214 EXPECT_FALSE(GetAndResetActiveStateChanged());
216 gfx::PointF
selection_start(0, 10);
217 gfx::PointF
selection_end(10, 10);
218 SetSelection(selection_start
, selection_end
);
220 // Activate a longpress-triggered selection, but at a time before the current
222 selector
.OnLongPressEvent(
223 event
.GetEventTime() - base::TimeDelta::FromSeconds(1), gfx::PointF());
224 selector
.OnSelectionActivated();
225 EXPECT_FALSE(GetAndResetActiveStateChanged());
226 EXPECT_FALSE(IsDragging());
228 // Activate a longpress-triggered selection, but at a place different than the
229 // current touch down event.
230 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF(kSlop
, 0));
231 selector
.OnSelectionActivated();
232 EXPECT_FALSE(GetAndResetActiveStateChanged());
233 EXPECT_FALSE(IsDragging());
235 // Touch movement should not initiate selection drag.
236 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
237 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
)));
238 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 2)));
239 EXPECT_FALSE(IsDragging());
240 EXPECT_EQ(gfx::PointF(), DragPosition());
243 TEST_F(LongPressDragSelectorTest
, NoSelection
) {
244 LongPressDragSelector
selector(this);
245 MockMotionEvent event
;
247 // Start a touch sequence.
248 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
249 EXPECT_FALSE(GetAndResetActiveStateChanged());
251 // Trigger a longpress. This will notify the client that detection is active,
252 // but until there's a longpress no drag selection should occur.
253 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF());
254 EXPECT_TRUE(GetAndResetActiveStateChanged());
255 EXPECT_FALSE(IsDragging());
257 // Touch movement should not initiate selection drag, as there is no active
259 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
260 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
)));
261 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 2)));
262 EXPECT_FALSE(IsDragging());
263 EXPECT_EQ(gfx::PointF(), DragPosition());
265 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.ReleasePoint()));
266 EXPECT_TRUE(GetAndResetActiveStateChanged());
269 TEST_F(LongPressDragSelectorTest
, NoDragMotion
) {
270 LongPressDragSelector
selector(this);
271 MockMotionEvent event
;
273 // Start a touch sequence.
274 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
275 EXPECT_FALSE(GetAndResetActiveStateChanged());
277 // Activate a longpress-triggered selection.
278 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF());
279 EXPECT_TRUE(GetAndResetActiveStateChanged());
280 gfx::PointF
selection_start(0, 10);
281 gfx::PointF
selection_end(10, 10);
282 SetSelection(selection_start
, selection_end
);
283 selector
.OnSelectionActivated();
284 EXPECT_FALSE(IsDragging());
286 // Touch movement within the slop region should not initiate selection drag.
287 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
288 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
/ 2)));
289 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, -kSlop
/ 2)));
290 EXPECT_FALSE(IsDragging());
291 EXPECT_EQ(gfx::PointF(), DragPosition());
293 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.ReleasePoint()));
294 EXPECT_TRUE(GetAndResetActiveStateChanged());
297 TEST_F(LongPressDragSelectorTest
, SelectionDeactivated
) {
298 LongPressDragSelector
selector(this);
299 MockMotionEvent event
;
301 // Start a touch sequence.
302 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
303 EXPECT_FALSE(GetAndResetActiveStateChanged());
305 // Activate a longpress-triggered selection.
306 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF());
307 EXPECT_TRUE(GetAndResetActiveStateChanged());
308 gfx::PointF
selection_start(0, 10);
309 gfx::PointF
selection_end(10, 10);
310 SetSelection(selection_start
, selection_end
);
311 selector
.OnSelectionActivated();
312 EXPECT_FALSE(IsDragging());
314 // Start a drag selection.
315 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
316 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
)));
317 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 2)));
318 EXPECT_TRUE(IsDragging());
320 // Clearing the selection should force an end to the drag.
321 selector
.OnSelectionDeactivated();
322 EXPECT_TRUE(GetAndResetActiveStateChanged());
323 EXPECT_FALSE(IsDragging());
325 // Subsequent motion should not be consumed.
326 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, 0)));
327 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
)));
328 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 0, kSlop
* 2)));
329 EXPECT_FALSE(IsDragging());
330 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.ReleasePoint()));
333 TEST_F(LongPressDragSelectorTest
, DragFast
) {
334 LongPressDragSelector
selector(this);
335 MockMotionEvent event
;
337 // Start a touch sequence.
338 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.PressPoint(0, 0)));
339 EXPECT_FALSE(GetAndResetActiveStateChanged());
341 // Activate a longpress-triggered selection.
342 gfx::PointF
selection_start(0, 10);
343 gfx::PointF
selection_end(10, 10);
344 selector
.OnLongPressEvent(event
.GetEventTime(), gfx::PointF());
345 EXPECT_TRUE(GetAndResetActiveStateChanged());
346 SetSelection(selection_start
, selection_end
);
347 selector
.OnSelectionActivated();
348 EXPECT_FALSE(IsDragging());
350 // Initiate drag motion.
351 EXPECT_TRUE(selector
.WillHandleTouchEvent(event
.MovePoint(0, 15, 5)));
352 EXPECT_FALSE(IsDragging());
354 // As the initial motion exceeds both endpoints, the closer bound should
355 // be used for dragging, in this case the selection end.
356 EXPECT_TRUE(selector
.WillHandleTouchEvent(
357 event
.MovePoint(0, 15.f
+ kSlop
* 2.f
, 5.f
+ kSlop
)));
358 EXPECT_TRUE(IsDragging());
359 EXPECT_EQ(selection_end
, DragPosition());
361 // Release the touch sequence, ending the drag.
362 EXPECT_FALSE(selector
.WillHandleTouchEvent(event
.ReleasePoint()));
363 EXPECT_FALSE(IsDragging());
364 EXPECT_TRUE(GetAndResetActiveStateChanged());