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/touch_selection_controller.h"
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "ui/events/test/motion_event_test_utils.h"
13 using testing::ElementsAre
;
14 using testing::IsEmpty
;
15 using ui::test::MockMotionEvent
;
20 const int kDefaultTapTimeoutMs
= 200;
21 const float kDefaulTapSlop
= 10.f
;
23 class MockTouchHandleDrawable
: public TouchHandleDrawable
{
25 explicit MockTouchHandleDrawable(bool* contains_point
)
26 : intersects_rect_(contains_point
) {}
27 ~MockTouchHandleDrawable() override
{}
28 void SetEnabled(bool enabled
) override
{}
29 void SetOrientation(TouchHandleOrientation orientation
) override
{}
30 void SetAlpha(float alpha
) override
{}
31 void SetFocus(const gfx::PointF
& position
) override
{}
32 gfx::RectF
GetVisibleBounds() const override
{
33 return *intersects_rect_
? gfx::RectF(-1000, -1000, 2000, 2000)
34 : gfx::RectF(-1000, -1000, 0, 0);
38 bool* intersects_rect_
;
43 class TouchSelectionControllerTest
: public testing::Test
,
44 public TouchSelectionControllerClient
{
46 TouchSelectionControllerTest()
47 : caret_moved_(false),
48 selection_moved_(false),
49 selection_points_swapped_(false),
50 needs_animate_(false),
51 animation_enabled_(true),
52 dragging_enabled_(false) {}
54 ~TouchSelectionControllerTest() override
{}
56 // testing::Test implementation.
58 void SetUp() override
{
59 // Default touch selection controller is created with
60 // |show_on_tap_for_empty_editable| flag set to false. Use
61 // |AllowShowingOnTapForEmptyEditable()| function to override it.
62 bool show_on_tap_for_empty_editable
= false;
63 controller_
.reset(new TouchSelectionController(
65 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs
),
67 show_on_tap_for_empty_editable
));
70 void TearDown() override
{ controller_
.reset(); }
72 // TouchSelectionControllerClient implementation.
74 bool SupportsAnimation() const override
{ return animation_enabled_
; }
76 void SetNeedsAnimate() override
{ needs_animate_
= true; }
78 void MoveCaret(const gfx::PointF
& position
) override
{
80 caret_position_
= position
;
83 void SelectBetweenCoordinates(const gfx::PointF
& base
,
84 const gfx::PointF
& extent
) override
{
85 if (base
== selection_end_
&& extent
== selection_start_
)
86 selection_points_swapped_
= true;
88 selection_start_
= base
;
89 selection_end_
= extent
;
92 void MoveRangeSelectionExtent(const gfx::PointF
& extent
) override
{
93 selection_moved_
= true;
94 selection_end_
= extent
;
97 void OnSelectionEvent(SelectionEventType event
) override
{
98 events_
.push_back(event
);
99 last_event_start_
= controller_
->GetStartPosition();
100 last_event_end_
= controller_
->GetEndPosition();
103 scoped_ptr
<TouchHandleDrawable
> CreateDrawable() override
{
104 return make_scoped_ptr(new MockTouchHandleDrawable(&dragging_enabled_
));
107 void AllowShowingOnTapForEmptyEditable() {
108 bool show_on_tap_for_empty_editable
= true;
109 controller_
.reset(new TouchSelectionController(
111 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs
),
113 show_on_tap_for_empty_editable
));
116 void SetAnimationEnabled(bool enabled
) { animation_enabled_
= enabled
; }
117 void SetDraggingEnabled(bool enabled
) { dragging_enabled_
= enabled
; }
119 void ClearSelection() {
120 controller_
->OnSelectionBoundsChanged(SelectionBound(),
124 void ClearInsertion() { ClearSelection(); }
126 void ChangeInsertion(const gfx::RectF
& rect
, bool visible
) {
127 SelectionBound bound
;
128 bound
.set_type(SelectionBound::CENTER
);
129 bound
.SetEdge(rect
.origin(), rect
.bottom_left());
130 bound
.set_visible(visible
);
131 controller_
->OnSelectionBoundsChanged(bound
, bound
);
134 void ChangeSelection(const gfx::RectF
& start_rect
,
136 const gfx::RectF
& end_rect
,
138 SelectionBound start_bound
, end_bound
;
139 start_bound
.set_type(SelectionBound::LEFT
);
140 end_bound
.set_type(SelectionBound::RIGHT
);
141 start_bound
.SetEdge(start_rect
.origin(), start_rect
.bottom_left());
142 end_bound
.SetEdge(end_rect
.origin(), end_rect
.bottom_left());
143 start_bound
.set_visible(start_visible
);
144 end_bound
.set_visible(end_visible
);
145 controller_
->OnSelectionBoundsChanged(start_bound
, end_bound
);
149 base::TimeTicks now
= base::TimeTicks::Now();
150 while (needs_animate_
) {
151 needs_animate_
= controller_
->Animate(now
);
152 now
+= base::TimeDelta::FromMilliseconds(16);
156 bool GetAndResetNeedsAnimate() {
157 bool needs_animate
= needs_animate_
;
159 return needs_animate
;
162 bool GetAndResetCaretMoved() {
163 bool moved
= caret_moved_
;
164 caret_moved_
= false;
168 bool GetAndResetSelectionMoved() {
169 bool moved
= selection_moved_
;
170 selection_moved_
= false;
174 bool GetAndResetSelectionPointsSwapped() {
175 bool swapped
= selection_points_swapped_
;
176 selection_points_swapped_
= false;
180 const gfx::PointF
& GetLastCaretPosition() const { return caret_position_
; }
181 const gfx::PointF
& GetLastSelectionStart() const { return selection_start_
; }
182 const gfx::PointF
& GetLastSelectionEnd() const { return selection_end_
; }
183 const gfx::PointF
& GetLastEventStart() const { return last_event_start_
; }
184 const gfx::PointF
& GetLastEventEnd() const { return last_event_end_
; }
186 std::vector
<SelectionEventType
> GetAndResetEvents() {
187 std::vector
<SelectionEventType
> events
;
188 events
.swap(events_
);
192 TouchSelectionController
& controller() { return *controller_
; }
195 gfx::PointF last_event_start_
;
196 gfx::PointF last_event_end_
;
197 gfx::PointF caret_position_
;
198 gfx::PointF selection_start_
;
199 gfx::PointF selection_end_
;
200 std::vector
<SelectionEventType
> events_
;
202 bool selection_moved_
;
203 bool selection_points_swapped_
;
205 bool animation_enabled_
;
206 bool dragging_enabled_
;
207 scoped_ptr
<TouchSelectionController
> controller_
;
210 TEST_F(TouchSelectionControllerTest
, InsertionBasic
) {
211 gfx::RectF
insertion_rect(5, 5, 0, 10);
214 // Insertion events are ignored until automatic showing is enabled.
215 ChangeInsertion(insertion_rect
, visible
);
216 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
217 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
218 controller().OnTapEvent();
220 // Insertion events are ignored until the selection region is marked editable.
221 ChangeInsertion(insertion_rect
, visible
);
222 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
223 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
225 controller().OnTapEvent();
226 controller().OnSelectionEditable(true);
227 ChangeInsertion(insertion_rect
, visible
);
228 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
229 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
231 insertion_rect
.Offset(1, 0);
232 ChangeInsertion(insertion_rect
, visible
);
233 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_MOVED
));
234 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
236 insertion_rect
.Offset(0, 1);
237 ChangeInsertion(insertion_rect
, visible
);
238 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_MOVED
));
239 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
242 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
245 TEST_F(TouchSelectionControllerTest
, InsertionClearedWhenNoLongerEditable
) {
246 gfx::RectF
insertion_rect(5, 5, 0, 10);
248 controller().OnTapEvent();
249 controller().OnSelectionEditable(true);
251 ChangeInsertion(insertion_rect
, visible
);
252 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
253 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
255 controller().OnSelectionEditable(false);
256 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
259 TEST_F(TouchSelectionControllerTest
, InsertionWithNoShowOnTapForEmptyEditable
) {
260 gfx::RectF
insertion_rect(5, 5, 0, 10);
262 controller().OnSelectionEditable(true);
264 // Taps on an empty editable region should be ignored if the controller is
265 // created with |show_on_tap_for_empty_editable| set to false.
266 controller().OnTapEvent();
267 controller().OnSelectionEmpty(true);
268 ChangeInsertion(insertion_rect
, visible
);
269 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
271 // Once the region becomes non-empty, taps should show the insertion handle.
272 controller().OnTapEvent();
273 controller().OnSelectionEmpty(false);
274 ChangeInsertion(insertion_rect
, visible
);
275 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
276 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
278 // Reset the selection.
279 controller().HideAndDisallowShowingAutomatically();
280 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
282 // Long-pressing should show the handle even if the editable region is empty.
283 insertion_rect
.Offset(2, -2);
284 controller().OnLongPressEvent();
285 controller().OnSelectionEmpty(true);
286 ChangeInsertion(insertion_rect
, visible
);
287 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
288 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
290 // Single Tap on an empty edit field should clear insertion handle.
291 controller().OnTapEvent();
292 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
295 TEST_F(TouchSelectionControllerTest
, InsertionWithShowOnTapForEmptyEditable
) {
296 AllowShowingOnTapForEmptyEditable();
298 gfx::RectF
insertion_rect(5, 5, 0, 10);
300 controller().OnSelectionEditable(true);
302 // Taps on an empty editable region should show the insertion handle if the
303 // controller is created with |show_on_tap_for_empty_editable| set to true.
304 controller().OnTapEvent();
305 controller().OnSelectionEmpty(true);
306 ChangeInsertion(insertion_rect
, visible
);
307 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
308 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
310 // Additional taps should not hide the insertion handle in this case.
311 controller().OnTapEvent();
312 ChangeInsertion(insertion_rect
, visible
);
313 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
316 TEST_F(TouchSelectionControllerTest
, InsertionAppearsAfterTapFollowingTyping
) {
317 gfx::RectF
insertion_rect(5, 5, 0, 10);
320 // Simulate the user tapping an empty text field.
321 controller().OnTapEvent();
322 controller().OnSelectionEditable(true);
323 controller().OnSelectionEmpty(true);
324 ChangeInsertion(insertion_rect
, visible
);
325 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
327 // Simulate the cursor moving while a user is typing.
328 insertion_rect
.Offset(10, 0);
329 controller().OnSelectionEmpty(false);
330 ChangeInsertion(insertion_rect
, visible
);
331 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
333 // If the user taps the *same* position as the cursor at the end of the text
334 // entry, the handle should appear.
335 controller().OnTapEvent();
336 ChangeInsertion(insertion_rect
, visible
);
337 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
338 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());
341 TEST_F(TouchSelectionControllerTest
, InsertionToSelectionTransition
) {
342 controller().OnLongPressEvent();
343 controller().OnSelectionEditable(true);
345 gfx::RectF
start_rect(5, 5, 0, 10);
346 gfx::RectF
end_rect(50, 5, 0, 10);
349 ChangeInsertion(start_rect
, visible
);
350 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
351 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
353 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
354 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
356 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
358 ChangeInsertion(end_rect
, visible
);
359 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
,
361 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventStart());
363 controller().HideAndDisallowShowingAutomatically();
364 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
366 controller().OnTapEvent();
367 ChangeInsertion(end_rect
, visible
);
368 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
369 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventStart());
372 TEST_F(TouchSelectionControllerTest
, InsertionDragged
) {
373 base::TimeTicks event_time
= base::TimeTicks::Now();
374 controller().OnTapEvent();
375 controller().OnSelectionEditable(true);
377 // The touch sequence should not be handled if insertion is not active.
378 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
379 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
381 float line_height
= 10.f
;
382 gfx::RectF
start_rect(10, 0, 0, line_height
);
384 ChangeInsertion(start_rect
, visible
);
385 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
386 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
388 // The touch sequence should be handled only if the drawable reports a hit.
389 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
390 SetDraggingEnabled(true);
391 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
392 EXPECT_FALSE(GetAndResetCaretMoved());
393 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
395 // The MoveCaret() result should reflect the movement.
396 // The reported position is offset from the center of |start_rect|.
397 gfx::PointF start_offset
= start_rect
.CenterPoint();
398 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 5);
399 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
400 EXPECT_TRUE(GetAndResetCaretMoved());
401 EXPECT_EQ(start_offset
+ gfx::Vector2dF(0, 5), GetLastCaretPosition());
403 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 5, 5);
404 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
405 EXPECT_TRUE(GetAndResetCaretMoved());
406 EXPECT_EQ(start_offset
+ gfx::Vector2dF(5, 5), GetLastCaretPosition());
408 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 10, 10);
409 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
410 EXPECT_TRUE(GetAndResetCaretMoved());
411 EXPECT_EQ(start_offset
+ gfx::Vector2dF(10, 10), GetLastCaretPosition());
413 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
414 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
415 EXPECT_FALSE(GetAndResetCaretMoved());
416 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STOPPED
));
418 // Once the drag is complete, no more touch events should be consumed until
419 // the next ACTION_DOWN.
420 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
423 TEST_F(TouchSelectionControllerTest
, InsertionTapped
) {
424 base::TimeTicks event_time
= base::TimeTicks::Now();
425 controller().OnTapEvent();
426 controller().OnSelectionEditable(true);
427 SetDraggingEnabled(true);
429 gfx::RectF
start_rect(10, 0, 0, 10);
431 ChangeInsertion(start_rect
, visible
);
432 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
434 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
435 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
436 //TODO(AKV): this test case has to be modified once crbug.com/394093 is fixed.
437 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
439 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
440 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
441 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_TAPPED
,
442 INSERTION_DRAG_STOPPED
));
444 // Reset the insertion.
446 controller().OnTapEvent();
447 ChangeInsertion(start_rect
, visible
);
448 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
451 // No tap should be signalled if the time between DOWN and UP was too long.
452 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
453 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
454 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
,
455 event_time
+ base::TimeDelta::FromSeconds(1),
458 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
459 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
,
460 INSERTION_DRAG_STOPPED
));
462 // Reset the insertion.
464 controller().OnTapEvent();
465 ChangeInsertion(start_rect
, visible
);
466 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
469 // No tap should be signalled if the drag was too long.
470 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
471 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
472 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 100, 0);
473 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
474 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 100, 0);
475 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
476 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
,
477 INSERTION_DRAG_STOPPED
));
479 // Reset the insertion.
481 controller().OnTapEvent();
482 ChangeInsertion(start_rect
, visible
);
483 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
486 // No tap should be signalled if the touch sequence is cancelled.
487 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
488 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
489 event
= MockMotionEvent(MockMotionEvent::ACTION_CANCEL
, event_time
, 0, 0);
490 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
491 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
,
492 INSERTION_DRAG_STOPPED
));
495 TEST_F(TouchSelectionControllerTest
, InsertionNotResetByRepeatedTapOrPress
) {
496 base::TimeTicks event_time
= base::TimeTicks::Now();
497 controller().OnTapEvent();
498 controller().OnSelectionEditable(true);
499 SetDraggingEnabled(true);
501 gfx::RectF
anchor_rect(10, 0, 0, 10);
503 ChangeInsertion(anchor_rect
, visible
);
504 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
505 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventStart());
507 // Tapping again shouldn't reset the active insertion point.
508 controller().OnTapEvent();
509 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
510 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
511 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
512 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventStart());
514 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
515 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
516 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_TAPPED
,
517 INSERTION_DRAG_STOPPED
));
518 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventStart());
520 anchor_rect
.Offset(5, 15);
521 ChangeInsertion(anchor_rect
, visible
);
522 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_MOVED
));
523 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventStart());
525 // Pressing shouldn't reset the active insertion point.
526 controller().OnLongPressEvent();
527 controller().OnSelectionEmpty(true);
528 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
529 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
530 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
531 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventStart());
533 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
534 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
535 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_TAPPED
,
536 INSERTION_DRAG_STOPPED
));
537 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventStart());
540 TEST_F(TouchSelectionControllerTest
, SelectionBasic
) {
541 gfx::RectF
start_rect(5, 5, 0, 10);
542 gfx::RectF
end_rect(50, 5, 0, 10);
545 // Selection events are ignored until automatic showing is enabled.
546 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
547 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
549 controller().OnLongPressEvent();
550 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
551 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
552 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
554 start_rect
.Offset(1, 0);
555 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
556 // Selection movement does not currently trigger a separate event.
557 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_MOVED
));
558 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
559 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventEnd());
562 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
));
563 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
566 TEST_F(TouchSelectionControllerTest
, SelectionRepeatedLongPress
) {
567 gfx::RectF
start_rect(5, 5, 0, 10);
568 gfx::RectF
end_rect(50, 5, 0, 10);
571 controller().OnLongPressEvent();
572 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
573 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
574 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
575 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventEnd());
577 // A long press triggering a new selection should re-send the SELECTION_SHOWN
578 // event notification.
579 start_rect
.Offset(10, 10);
580 controller().OnLongPressEvent();
581 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
582 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
583 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
584 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventEnd());
587 TEST_F(TouchSelectionControllerTest
, SelectionDragged
) {
588 base::TimeTicks event_time
= base::TimeTicks::Now();
589 controller().OnLongPressEvent();
591 // The touch sequence should not be handled if selection is not active.
592 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
593 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
595 float line_height
= 10.f
;
596 gfx::RectF
start_rect(0, 0, 0, line_height
);
597 gfx::RectF
end_rect(50, 0, 0, line_height
);
599 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
600 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
601 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
603 // The touch sequence should be handled only if the drawable reports a hit.
604 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
605 SetDraggingEnabled(true);
606 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
607 EXPECT_FALSE(GetAndResetSelectionMoved());
609 // The SelectBetweenCoordinates() result should reflect the movement. Note
610 // that the start coordinate will always reflect the "fixed" handle's
611 // position, in this case the position from |end_rect|.
612 // Note that the reported position is offset from the center of the
613 // input rects (i.e., the middle of the corresponding text line).
614 gfx::PointF fixed_offset
= end_rect
.CenterPoint();
615 gfx::PointF start_offset
= start_rect
.CenterPoint();
616 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 5);
617 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
618 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
619 EXPECT_TRUE(GetAndResetSelectionMoved());
620 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
621 EXPECT_EQ(start_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
623 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 5, 5);
624 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
625 EXPECT_TRUE(GetAndResetSelectionMoved());
626 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
627 EXPECT_EQ(start_offset
+ gfx::Vector2dF(5, 5), GetLastSelectionEnd());
629 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 10, 5);
630 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
631 EXPECT_TRUE(GetAndResetSelectionMoved());
632 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
633 EXPECT_EQ(start_offset
+ gfx::Vector2dF(10, 5), GetLastSelectionEnd());
635 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
636 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
637 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
638 EXPECT_FALSE(GetAndResetSelectionMoved());
640 // Once the drag is complete, no more touch events should be consumed until
641 // the next ACTION_DOWN.
642 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
645 TEST_F(TouchSelectionControllerTest
, SelectionDraggedWithOverlap
) {
646 base::TimeTicks event_time
= base::TimeTicks::Now();
647 controller().OnLongPressEvent();
649 float line_height
= 10.f
;
650 gfx::RectF
start_rect(0, 0, 0, line_height
);
651 gfx::RectF
end_rect(50, 0, 0, line_height
);
653 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
654 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
655 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
657 // The ACTION_DOWN should lock to the closest handle.
658 gfx::PointF end_offset
= end_rect
.CenterPoint();
659 gfx::PointF fixed_offset
= start_rect
.CenterPoint();
660 float touch_down_x
= (end_offset
.x() + fixed_offset
.x()) / 2 + 1.f
;
661 MockMotionEvent
event(
662 MockMotionEvent::ACTION_DOWN
, event_time
, touch_down_x
, 0);
663 SetDraggingEnabled(true);
664 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
665 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
666 EXPECT_FALSE(GetAndResetSelectionMoved());
668 // Even though the ACTION_MOVE is over the start handle, it should continue
669 // targetting the end handle that consumed the ACTION_DOWN.
670 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 0);
671 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
672 EXPECT_TRUE(GetAndResetSelectionMoved());
673 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
674 EXPECT_EQ(end_offset
- gfx::Vector2dF(touch_down_x
, 0),
675 GetLastSelectionEnd());
677 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
678 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
679 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
680 EXPECT_FALSE(GetAndResetSelectionMoved());
683 TEST_F(TouchSelectionControllerTest
, SelectionDraggedToSwitchBaseAndExtent
) {
684 base::TimeTicks event_time
= base::TimeTicks::Now();
685 controller().OnLongPressEvent();
687 float line_height
= 10.f
;
688 gfx::RectF
start_rect(50, line_height
, 0, line_height
);
689 gfx::RectF
end_rect(100, line_height
, 0, line_height
);
691 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
692 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
693 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
695 SetDraggingEnabled(true);
697 // Move the extent, not triggering a swap of points.
698 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
,
699 end_rect
.x(), end_rect
.bottom());
700 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
701 EXPECT_FALSE(GetAndResetSelectionMoved());
702 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
704 gfx::PointF base_offset
= start_rect
.CenterPoint();
705 gfx::PointF extent_offset
= end_rect
.CenterPoint();
706 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
707 end_rect
.x(), end_rect
.bottom() + 5);
708 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
709 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
710 EXPECT_TRUE(GetAndResetSelectionMoved());
711 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
712 EXPECT_EQ(base_offset
, GetLastSelectionStart());
713 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
715 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
716 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
717 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
718 EXPECT_FALSE(GetAndResetSelectionMoved());
720 end_rect
+= gfx::Vector2dF(0, 5);
721 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
722 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_MOVED
));
724 // Move the base, triggering a swap of points.
725 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
726 start_rect
.x(), start_rect
.bottom());
727 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
728 EXPECT_FALSE(GetAndResetSelectionMoved());
729 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
731 base_offset
= end_rect
.CenterPoint();
732 extent_offset
= start_rect
.CenterPoint();
733 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
734 start_rect
.x(), start_rect
.bottom() + 5);
735 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
736 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
737 EXPECT_TRUE(GetAndResetSelectionMoved());
738 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
739 EXPECT_EQ(base_offset
, GetLastSelectionStart());
740 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
742 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
743 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
744 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
745 EXPECT_FALSE(GetAndResetSelectionMoved());
747 start_rect
+= gfx::Vector2dF(0, 5);
748 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
749 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_MOVED
));
751 // Move the same point again, not triggering a swap of points.
752 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
753 start_rect
.x(), start_rect
.bottom());
754 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
755 EXPECT_FALSE(GetAndResetSelectionMoved());
756 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
758 base_offset
= end_rect
.CenterPoint();
759 extent_offset
= start_rect
.CenterPoint();
760 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
761 start_rect
.x(), start_rect
.bottom() + 5);
762 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
763 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
764 EXPECT_TRUE(GetAndResetSelectionMoved());
765 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
766 EXPECT_EQ(base_offset
, GetLastSelectionStart());
767 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
769 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
770 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
771 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
772 EXPECT_FALSE(GetAndResetSelectionMoved());
774 start_rect
+= gfx::Vector2dF(0, 5);
775 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
776 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_MOVED
));
778 // Move the base, triggering a swap of points.
779 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
780 end_rect
.x(), end_rect
.bottom());
781 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
782 EXPECT_FALSE(GetAndResetSelectionMoved());
783 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
785 base_offset
= start_rect
.CenterPoint();
786 extent_offset
= end_rect
.CenterPoint();
787 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
788 end_rect
.x(), end_rect
.bottom() + 5);
789 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
790 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
791 EXPECT_TRUE(GetAndResetSelectionMoved());
792 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
793 EXPECT_EQ(base_offset
, GetLastSelectionStart());
794 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
796 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
797 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
798 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
799 EXPECT_FALSE(GetAndResetSelectionMoved());
802 TEST_F(TouchSelectionControllerTest
, SelectionDragExtremeLineSize
) {
803 base::TimeTicks event_time
= base::TimeTicks::Now();
804 controller().OnLongPressEvent();
806 float small_line_height
= 1.f
;
807 float large_line_height
= 50.f
;
808 gfx::RectF
small_line_rect(0, 0, 0, small_line_height
);
809 gfx::RectF
large_line_rect(50, 50, 0, large_line_height
);
811 ChangeSelection(small_line_rect
, visible
, large_line_rect
, visible
);
812 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
813 EXPECT_EQ(small_line_rect
.bottom_left(), GetLastEventStart());
815 // Start dragging the handle on the small line.
816 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
,
817 small_line_rect
.x(), small_line_rect
.y());
818 SetDraggingEnabled(true);
819 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
820 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
821 // The drag coordinate for large lines should be capped to a reasonable
822 // offset, allowing seamless transition to neighboring lines with different
823 // sizes. The drag coordinate for small lines should have an offset
824 // commensurate with the small line size.
825 EXPECT_EQ(large_line_rect
.bottom_left() - gfx::Vector2dF(0, 5.f
),
826 GetLastSelectionStart());
827 EXPECT_EQ(small_line_rect
.CenterPoint(), GetLastSelectionEnd());
829 small_line_rect
+= gfx::Vector2dF(25.f
, 0);
830 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
831 small_line_rect
.x(), small_line_rect
.y());
832 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
833 EXPECT_TRUE(GetAndResetSelectionMoved());
834 EXPECT_EQ(small_line_rect
.CenterPoint(), GetLastSelectionEnd());
837 TEST_F(TouchSelectionControllerTest
, Animation
) {
838 controller().OnTapEvent();
839 controller().OnSelectionEditable(true);
841 gfx::RectF
insertion_rect(5, 5, 0, 10);
844 ChangeInsertion(insertion_rect
, visible
);
845 EXPECT_FALSE(GetAndResetNeedsAnimate());
848 ChangeInsertion(insertion_rect
, visible
);
849 EXPECT_TRUE(GetAndResetNeedsAnimate());
852 ChangeInsertion(insertion_rect
, visible
);
853 EXPECT_TRUE(GetAndResetNeedsAnimate());
855 // If the handles are explicity hidden, no animation should be triggered.
856 controller().HideAndDisallowShowingAutomatically();
857 EXPECT_FALSE(GetAndResetNeedsAnimate());
859 // If the client doesn't support animation, no animation should be triggered.
860 SetAnimationEnabled(false);
861 controller().OnTapEvent();
863 ChangeInsertion(insertion_rect
, visible
);
864 EXPECT_FALSE(GetAndResetNeedsAnimate());
867 TEST_F(TouchSelectionControllerTest
, TemporarilyHidden
) {
868 controller().OnTapEvent();
869 controller().OnSelectionEditable(true);
871 gfx::RectF
insertion_rect(5, 5, 0, 10);
874 ChangeInsertion(insertion_rect
, visible
);
875 EXPECT_FALSE(GetAndResetNeedsAnimate());
877 controller().SetTemporarilyHidden(true);
878 EXPECT_TRUE(GetAndResetNeedsAnimate());
881 ChangeInsertion(insertion_rect
, visible
);
882 EXPECT_FALSE(GetAndResetNeedsAnimate());
885 ChangeInsertion(insertion_rect
, visible
);
886 EXPECT_FALSE(GetAndResetNeedsAnimate());
888 controller().SetTemporarilyHidden(false);
889 EXPECT_TRUE(GetAndResetNeedsAnimate());
892 TEST_F(TouchSelectionControllerTest
, SelectionClearOnTap
) {
893 gfx::RectF
start_rect(5, 5, 0, 10);
894 gfx::RectF
end_rect(50, 5, 0, 10);
897 controller().OnLongPressEvent();
898 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
899 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
901 // Selection should not be cleared if the selection bounds have not changed.
902 controller().OnTapEvent();
903 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
904 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
906 controller().OnTapEvent();
908 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
));
909 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
912 TEST_F(TouchSelectionControllerTest
, NoSelectionAfterLongpressThenTap
) {
913 gfx::RectF
start_rect(5, 5, 0, 10);
914 gfx::RectF
end_rect(50, 5, 0, 10);
917 // Tap-triggered selections should not be allowed.
918 controller().OnLongPressEvent();
919 controller().OnTapEvent();
920 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
921 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
923 // Subsequent longpress selections will be allowed.
924 controller().OnLongPressEvent();
925 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
926 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
928 // Tapping again shouldn't have any effect on subsequent selection events.
929 controller().OnTapEvent();
930 end_rect
.Offset(10, 10);
931 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
932 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_MOVED
));
934 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
));
937 TEST_F(TouchSelectionControllerTest
, AllowShowingFromCurrentSelection
) {
938 gfx::RectF
start_rect(5, 5, 0, 10);
939 gfx::RectF
end_rect(50, 5, 0, 10);
942 // The selection should not be activated, as it wasn't yet allowed.
943 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
944 EXPECT_EQ(gfx::PointF(), GetLastEventStart());
946 // Now explicitly allow showing from the previously supplied bounds.
947 controller().AllowShowingFromCurrentSelection();
948 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
949 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
951 // Repeated calls to show from the current selection should be ignored.
952 controller().AllowShowingFromCurrentSelection();
953 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
954 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventStart());
956 // Trying to show from an empty selection will have no result.
958 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
));
959 controller().AllowShowingFromCurrentSelection();
960 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
962 // Showing the insertion handle should also be supported.
963 controller().OnSelectionEditable(true);
964 controller().OnSelectionEmpty(false);
965 controller().HideAndDisallowShowingAutomatically();
966 gfx::RectF
insertion_rect(5, 5, 0, 10);
967 ChangeInsertion(insertion_rect
, visible
);
968 controller().AllowShowingFromCurrentSelection();
969 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
970 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventStart());