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
,
98 const gfx::PointF
& end_position
) override
{
99 events_
.push_back(event
);
100 last_event_start_
= end_position
;
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
& GetLastEventAnchor() const { return last_event_start_
; }
185 std::vector
<SelectionEventType
> GetAndResetEvents() {
186 std::vector
<SelectionEventType
> events
;
187 events
.swap(events_
);
191 TouchSelectionController
& controller() { return *controller_
; }
194 gfx::PointF last_event_start_
;
195 gfx::PointF caret_position_
;
196 gfx::PointF selection_start_
;
197 gfx::PointF selection_end_
;
198 std::vector
<SelectionEventType
> events_
;
200 bool selection_moved_
;
201 bool selection_points_swapped_
;
203 bool animation_enabled_
;
204 bool dragging_enabled_
;
205 scoped_ptr
<TouchSelectionController
> controller_
;
208 TEST_F(TouchSelectionControllerTest
, InsertionBasic
) {
209 gfx::RectF
insertion_rect(5, 5, 0, 10);
212 // Insertion events are ignored until automatic showing is enabled.
213 ChangeInsertion(insertion_rect
, visible
);
214 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
215 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
216 controller().OnTapEvent();
218 // Insertion events are ignored until the selection region is marked editable.
219 ChangeInsertion(insertion_rect
, visible
);
220 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
221 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
223 controller().OnTapEvent();
224 controller().OnSelectionEditable(true);
225 ChangeInsertion(insertion_rect
, visible
);
226 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
227 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
229 insertion_rect
.Offset(1, 0);
230 ChangeInsertion(insertion_rect
, visible
);
231 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_MOVED
));
232 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
234 insertion_rect
.Offset(0, 1);
235 ChangeInsertion(insertion_rect
, visible
);
236 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_MOVED
));
237 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
240 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
243 TEST_F(TouchSelectionControllerTest
, InsertionClearedWhenNoLongerEditable
) {
244 gfx::RectF
insertion_rect(5, 5, 0, 10);
246 controller().OnTapEvent();
247 controller().OnSelectionEditable(true);
249 ChangeInsertion(insertion_rect
, visible
);
250 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
251 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
253 controller().OnSelectionEditable(false);
254 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
257 TEST_F(TouchSelectionControllerTest
, InsertionWithNoShowOnTapForEmptyEditable
) {
258 gfx::RectF
insertion_rect(5, 5, 0, 10);
260 controller().OnSelectionEditable(true);
262 // Taps on an empty editable region should be ignored if the controller is
263 // created with |show_on_tap_for_empty_editable| set to false.
264 controller().OnTapEvent();
265 controller().OnSelectionEmpty(true);
266 ChangeInsertion(insertion_rect
, visible
);
267 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
269 // Once the region becomes non-empty, taps should show the insertion handle.
270 controller().OnTapEvent();
271 controller().OnSelectionEmpty(false);
272 ChangeInsertion(insertion_rect
, visible
);
273 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
274 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
276 // Reset the selection.
277 controller().HideAndDisallowShowingAutomatically();
278 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
280 // Long-pressing should show the handle even if the editable region is empty.
281 insertion_rect
.Offset(2, -2);
282 controller().OnLongPressEvent();
283 controller().OnSelectionEmpty(true);
284 ChangeInsertion(insertion_rect
, visible
);
285 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
286 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
288 // Single Tap on an empty edit field should clear insertion handle.
289 controller().OnTapEvent();
290 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
293 TEST_F(TouchSelectionControllerTest
, InsertionWithShowOnTapForEmptyEditable
) {
294 AllowShowingOnTapForEmptyEditable();
296 gfx::RectF
insertion_rect(5, 5, 0, 10);
298 controller().OnSelectionEditable(true);
300 // Taps on an empty editable region should show the insertion handle if the
301 // controller is created with |show_on_tap_for_empty_editable| set to true.
302 controller().OnTapEvent();
303 controller().OnSelectionEmpty(true);
304 ChangeInsertion(insertion_rect
, visible
);
305 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
306 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
308 // Additional taps should not hide the insertion handle in this case.
309 controller().OnTapEvent();
310 ChangeInsertion(insertion_rect
, visible
);
311 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
314 TEST_F(TouchSelectionControllerTest
, InsertionAppearsAfterTapFollowingTyping
) {
315 gfx::RectF
insertion_rect(5, 5, 0, 10);
318 // Simulate the user tapping an empty text field.
319 controller().OnTapEvent();
320 controller().OnSelectionEditable(true);
321 controller().OnSelectionEmpty(true);
322 ChangeInsertion(insertion_rect
, visible
);
323 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
325 // Simulate the cursor moving while a user is typing.
326 insertion_rect
.Offset(10, 0);
327 controller().OnSelectionEmpty(false);
328 ChangeInsertion(insertion_rect
, visible
);
329 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
331 // If the user taps the *same* position as the cursor at the end of the text
332 // entry, the handle should appear.
333 controller().OnTapEvent();
334 ChangeInsertion(insertion_rect
, visible
);
335 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
336 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
339 TEST_F(TouchSelectionControllerTest
, InsertionToSelectionTransition
) {
340 controller().OnLongPressEvent();
341 controller().OnSelectionEditable(true);
343 gfx::RectF
start_rect(5, 5, 0, 10);
344 gfx::RectF
end_rect(50, 5, 0, 10);
347 ChangeInsertion(start_rect
, visible
);
348 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
349 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
351 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
352 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
354 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
356 ChangeInsertion(end_rect
, visible
);
357 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
,
359 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventAnchor());
361 controller().HideAndDisallowShowingAutomatically();
362 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
));
364 controller().OnTapEvent();
365 ChangeInsertion(end_rect
, visible
);
366 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
367 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventAnchor());
370 TEST_F(TouchSelectionControllerTest
, InsertionDragged
) {
371 base::TimeTicks event_time
= base::TimeTicks::Now();
372 controller().OnTapEvent();
373 controller().OnSelectionEditable(true);
375 // The touch sequence should not be handled if insertion is not active.
376 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
377 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
379 float line_height
= 10.f
;
380 gfx::RectF
start_rect(10, 0, 0, line_height
);
382 ChangeInsertion(start_rect
, visible
);
383 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
384 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
386 // The touch sequence should be handled only if the drawable reports a hit.
387 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
388 SetDraggingEnabled(true);
389 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
390 EXPECT_FALSE(GetAndResetCaretMoved());
391 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
393 // The MoveCaret() result should reflect the movement.
394 // The reported position is offset from the center of |start_rect|.
395 gfx::PointF start_offset
= start_rect
.CenterPoint();
396 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 5);
397 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
398 EXPECT_TRUE(GetAndResetCaretMoved());
399 EXPECT_EQ(start_offset
+ gfx::Vector2dF(0, 5), GetLastCaretPosition());
401 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 5, 5);
402 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
403 EXPECT_TRUE(GetAndResetCaretMoved());
404 EXPECT_EQ(start_offset
+ gfx::Vector2dF(5, 5), GetLastCaretPosition());
406 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 10, 10);
407 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
408 EXPECT_TRUE(GetAndResetCaretMoved());
409 EXPECT_EQ(start_offset
+ gfx::Vector2dF(10, 10), GetLastCaretPosition());
411 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
412 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
413 EXPECT_FALSE(GetAndResetCaretMoved());
414 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STOPPED
));
416 // Once the drag is complete, no more touch events should be consumed until
417 // the next ACTION_DOWN.
418 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
421 TEST_F(TouchSelectionControllerTest
, InsertionTapped
) {
422 base::TimeTicks event_time
= base::TimeTicks::Now();
423 controller().OnTapEvent();
424 controller().OnSelectionEditable(true);
425 SetDraggingEnabled(true);
427 gfx::RectF
start_rect(10, 0, 0, 10);
429 ChangeInsertion(start_rect
, visible
);
430 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
432 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
433 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
434 //TODO(AKV): this test case has to be modified once crbug.com/394093 is fixed.
435 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
437 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
438 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
439 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_TAPPED
,
440 INSERTION_DRAG_STOPPED
));
442 // Reset the insertion.
444 controller().OnTapEvent();
445 ChangeInsertion(start_rect
, visible
);
446 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
449 // No tap should be signalled if the time between DOWN and UP was too long.
450 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
451 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
452 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
,
453 event_time
+ base::TimeDelta::FromSeconds(1),
456 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
457 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
,
458 INSERTION_DRAG_STOPPED
));
460 // Reset the insertion.
462 controller().OnTapEvent();
463 ChangeInsertion(start_rect
, visible
);
464 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
467 // No tap should be signalled if the drag was too long.
468 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
469 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
470 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 100, 0);
471 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
472 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 100, 0);
473 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
474 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
,
475 INSERTION_DRAG_STOPPED
));
477 // Reset the insertion.
479 controller().OnTapEvent();
480 ChangeInsertion(start_rect
, visible
);
481 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_CLEARED
,
484 // No tap should be signalled if the touch sequence is cancelled.
485 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
486 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
487 event
= MockMotionEvent(MockMotionEvent::ACTION_CANCEL
, event_time
, 0, 0);
488 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
489 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
,
490 INSERTION_DRAG_STOPPED
));
493 TEST_F(TouchSelectionControllerTest
, InsertionNotResetByRepeatedTapOrPress
) {
494 base::TimeTicks event_time
= base::TimeTicks::Now();
495 controller().OnTapEvent();
496 controller().OnSelectionEditable(true);
497 SetDraggingEnabled(true);
499 gfx::RectF
anchor_rect(10, 0, 0, 10);
501 ChangeInsertion(anchor_rect
, visible
);
502 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
503 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
505 // Tapping again shouldn't reset the active insertion point.
506 controller().OnTapEvent();
507 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
508 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
509 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
510 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
512 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
513 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
514 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_TAPPED
,
515 INSERTION_DRAG_STOPPED
));
516 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
518 anchor_rect
.Offset(5, 15);
519 ChangeInsertion(anchor_rect
, visible
);
520 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_MOVED
));
521 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
523 // Pressing shouldn't reset the active insertion point.
524 controller().OnLongPressEvent();
525 controller().OnSelectionEmpty(true);
526 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
527 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
528 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_DRAG_STARTED
));
529 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
531 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
532 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
533 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_TAPPED
,
534 INSERTION_DRAG_STOPPED
));
535 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
538 TEST_F(TouchSelectionControllerTest
, SelectionBasic
) {
539 gfx::RectF
start_rect(5, 5, 0, 10);
540 gfx::RectF
end_rect(50, 5, 0, 10);
543 // Selection events are ignored until automatic showing is enabled.
544 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
545 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
547 controller().OnLongPressEvent();
548 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
549 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
550 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
552 start_rect
.Offset(1, 0);
553 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
554 // Selection movement does not currently trigger a separate event.
555 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
558 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
));
559 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
562 TEST_F(TouchSelectionControllerTest
, SelectionRepeatedLongPress
) {
563 gfx::RectF
start_rect(5, 5, 0, 10);
564 gfx::RectF
end_rect(50, 5, 0, 10);
567 controller().OnLongPressEvent();
568 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
569 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
570 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
572 // A long press triggering a new selection should re-send the SELECTION_SHOWN
573 // event notification.
574 start_rect
.Offset(10, 10);
575 controller().OnLongPressEvent();
576 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
577 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
578 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
581 TEST_F(TouchSelectionControllerTest
, SelectionDragged
) {
582 base::TimeTicks event_time
= base::TimeTicks::Now();
583 controller().OnLongPressEvent();
585 // The touch sequence should not be handled if selection is not active.
586 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
587 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
589 float line_height
= 10.f
;
590 gfx::RectF
start_rect(0, 0, 0, line_height
);
591 gfx::RectF
end_rect(50, 0, 0, line_height
);
593 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
594 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
595 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
597 // The touch sequence should be handled only if the drawable reports a hit.
598 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
599 SetDraggingEnabled(true);
600 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
601 EXPECT_FALSE(GetAndResetSelectionMoved());
603 // The SelectBetweenCoordinates() result should reflect the movement. Note
604 // that the start coordinate will always reflect the "fixed" handle's
605 // position, in this case the position from |end_rect|.
606 // Note that the reported position is offset from the center of the
607 // input rects (i.e., the middle of the corresponding text line).
608 gfx::PointF fixed_offset
= end_rect
.CenterPoint();
609 gfx::PointF start_offset
= start_rect
.CenterPoint();
610 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 5);
611 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
612 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
613 EXPECT_TRUE(GetAndResetSelectionMoved());
614 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
615 EXPECT_EQ(start_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
617 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 5, 5);
618 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
619 EXPECT_TRUE(GetAndResetSelectionMoved());
620 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
621 EXPECT_EQ(start_offset
+ gfx::Vector2dF(5, 5), GetLastSelectionEnd());
623 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 10, 5);
624 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
625 EXPECT_TRUE(GetAndResetSelectionMoved());
626 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
627 EXPECT_EQ(start_offset
+ gfx::Vector2dF(10, 5), GetLastSelectionEnd());
629 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
630 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
631 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
632 EXPECT_FALSE(GetAndResetSelectionMoved());
634 // Once the drag is complete, no more touch events should be consumed until
635 // the next ACTION_DOWN.
636 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
639 TEST_F(TouchSelectionControllerTest
, SelectionDraggedWithOverlap
) {
640 base::TimeTicks event_time
= base::TimeTicks::Now();
641 controller().OnLongPressEvent();
643 float line_height
= 10.f
;
644 gfx::RectF
start_rect(0, 0, 0, line_height
);
645 gfx::RectF
end_rect(50, 0, 0, line_height
);
647 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
648 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
649 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
651 // The ACTION_DOWN should lock to the closest handle.
652 gfx::PointF end_offset
= end_rect
.CenterPoint();
653 gfx::PointF fixed_offset
= start_rect
.CenterPoint();
654 float touch_down_x
= (end_offset
.x() + fixed_offset
.x()) / 2 + 1.f
;
655 MockMotionEvent
event(
656 MockMotionEvent::ACTION_DOWN
, event_time
, touch_down_x
, 0);
657 SetDraggingEnabled(true);
658 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
659 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
660 EXPECT_FALSE(GetAndResetSelectionMoved());
662 // Even though the ACTION_MOVE is over the start handle, it should continue
663 // targetting the end handle that consumed the ACTION_DOWN.
664 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 0);
665 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
666 EXPECT_TRUE(GetAndResetSelectionMoved());
667 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
668 EXPECT_EQ(end_offset
- gfx::Vector2dF(touch_down_x
, 0),
669 GetLastSelectionEnd());
671 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
672 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
673 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
674 EXPECT_FALSE(GetAndResetSelectionMoved());
677 TEST_F(TouchSelectionControllerTest
, SelectionDraggedToSwitchBaseAndExtent
) {
678 base::TimeTicks event_time
= base::TimeTicks::Now();
679 controller().OnLongPressEvent();
681 float line_height
= 10.f
;
682 gfx::RectF
start_rect(50, line_height
, 0, line_height
);
683 gfx::RectF
end_rect(100, line_height
, 0, line_height
);
685 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
686 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
687 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
689 SetDraggingEnabled(true);
691 // Move the extent, not triggering a swap of points.
692 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
,
693 end_rect
.x(), end_rect
.bottom());
694 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
695 EXPECT_FALSE(GetAndResetSelectionMoved());
696 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
698 gfx::PointF base_offset
= start_rect
.CenterPoint();
699 gfx::PointF extent_offset
= end_rect
.CenterPoint();
700 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
701 end_rect
.x(), end_rect
.bottom() + 5);
702 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
703 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
704 EXPECT_TRUE(GetAndResetSelectionMoved());
705 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
706 EXPECT_EQ(base_offset
, GetLastSelectionStart());
707 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
709 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
710 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
711 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
712 EXPECT_FALSE(GetAndResetSelectionMoved());
714 end_rect
+= gfx::Vector2dF(0, 5);
715 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
717 // Move the base, triggering a swap of points.
718 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
719 start_rect
.x(), start_rect
.bottom());
720 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
721 EXPECT_FALSE(GetAndResetSelectionMoved());
722 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
724 base_offset
= end_rect
.CenterPoint();
725 extent_offset
= start_rect
.CenterPoint();
726 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
727 start_rect
.x(), start_rect
.bottom() + 5);
728 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
729 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
730 EXPECT_TRUE(GetAndResetSelectionMoved());
731 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
732 EXPECT_EQ(base_offset
, GetLastSelectionStart());
733 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
735 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
736 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
737 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
738 EXPECT_FALSE(GetAndResetSelectionMoved());
740 start_rect
+= gfx::Vector2dF(0, 5);
741 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
743 // Move the same point again, not triggering a swap of points.
744 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
745 start_rect
.x(), start_rect
.bottom());
746 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
747 EXPECT_FALSE(GetAndResetSelectionMoved());
748 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
750 base_offset
= end_rect
.CenterPoint();
751 extent_offset
= start_rect
.CenterPoint();
752 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
753 start_rect
.x(), start_rect
.bottom() + 5);
754 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
755 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
756 EXPECT_TRUE(GetAndResetSelectionMoved());
757 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
758 EXPECT_EQ(base_offset
, GetLastSelectionStart());
759 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
761 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
762 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
763 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
764 EXPECT_FALSE(GetAndResetSelectionMoved());
766 start_rect
+= gfx::Vector2dF(0, 5);
767 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
769 // Move the base, triggering a swap of points.
770 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
771 end_rect
.x(), end_rect
.bottom());
772 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
773 EXPECT_FALSE(GetAndResetSelectionMoved());
774 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
776 base_offset
= start_rect
.CenterPoint();
777 extent_offset
= end_rect
.CenterPoint();
778 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
779 end_rect
.x(), end_rect
.bottom() + 5);
780 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
781 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
782 EXPECT_TRUE(GetAndResetSelectionMoved());
783 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
784 EXPECT_EQ(base_offset
, GetLastSelectionStart());
785 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
787 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
788 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
789 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STOPPED
));
790 EXPECT_FALSE(GetAndResetSelectionMoved());
793 TEST_F(TouchSelectionControllerTest
, SelectionDragExtremeLineSize
) {
794 base::TimeTicks event_time
= base::TimeTicks::Now();
795 controller().OnLongPressEvent();
797 float small_line_height
= 1.f
;
798 float large_line_height
= 50.f
;
799 gfx::RectF
small_line_rect(0, 0, 0, small_line_height
);
800 gfx::RectF
large_line_rect(50, 50, 0, large_line_height
);
802 ChangeSelection(small_line_rect
, visible
, large_line_rect
, visible
);
803 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
804 EXPECT_EQ(small_line_rect
.bottom_left(), GetLastEventAnchor());
806 // Start dragging the handle on the small line.
807 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
,
808 small_line_rect
.x(), small_line_rect
.y());
809 SetDraggingEnabled(true);
810 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
811 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_DRAG_STARTED
));
812 // The drag coordinate for large lines should be capped to a reasonable
813 // offset, allowing seamless transition to neighboring lines with different
814 // sizes. The drag coordinate for small lines should have an offset
815 // commensurate with the small line size.
816 EXPECT_EQ(large_line_rect
.bottom_left() - gfx::Vector2dF(0, 5.f
),
817 GetLastSelectionStart());
818 EXPECT_EQ(small_line_rect
.CenterPoint(), GetLastSelectionEnd());
820 small_line_rect
+= gfx::Vector2dF(25.f
, 0);
821 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
822 small_line_rect
.x(), small_line_rect
.y());
823 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
824 EXPECT_TRUE(GetAndResetSelectionMoved());
825 EXPECT_EQ(small_line_rect
.CenterPoint(), GetLastSelectionEnd());
828 TEST_F(TouchSelectionControllerTest
, Animation
) {
829 controller().OnTapEvent();
830 controller().OnSelectionEditable(true);
832 gfx::RectF
insertion_rect(5, 5, 0, 10);
835 ChangeInsertion(insertion_rect
, visible
);
836 EXPECT_FALSE(GetAndResetNeedsAnimate());
839 ChangeInsertion(insertion_rect
, visible
);
840 EXPECT_TRUE(GetAndResetNeedsAnimate());
843 ChangeInsertion(insertion_rect
, visible
);
844 EXPECT_TRUE(GetAndResetNeedsAnimate());
846 // If the handles are explicity hidden, no animation should be triggered.
847 controller().HideAndDisallowShowingAutomatically();
848 EXPECT_FALSE(GetAndResetNeedsAnimate());
850 // If the client doesn't support animation, no animation should be triggered.
851 SetAnimationEnabled(false);
852 controller().OnTapEvent();
854 ChangeInsertion(insertion_rect
, visible
);
855 EXPECT_FALSE(GetAndResetNeedsAnimate());
858 TEST_F(TouchSelectionControllerTest
, TemporarilyHidden
) {
859 controller().OnTapEvent();
860 controller().OnSelectionEditable(true);
862 gfx::RectF
insertion_rect(5, 5, 0, 10);
865 ChangeInsertion(insertion_rect
, visible
);
866 EXPECT_FALSE(GetAndResetNeedsAnimate());
868 controller().SetTemporarilyHidden(true);
869 EXPECT_TRUE(GetAndResetNeedsAnimate());
872 ChangeInsertion(insertion_rect
, visible
);
873 EXPECT_FALSE(GetAndResetNeedsAnimate());
876 ChangeInsertion(insertion_rect
, visible
);
877 EXPECT_FALSE(GetAndResetNeedsAnimate());
879 controller().SetTemporarilyHidden(false);
880 EXPECT_TRUE(GetAndResetNeedsAnimate());
883 TEST_F(TouchSelectionControllerTest
, SelectionClearOnTap
) {
884 gfx::RectF
start_rect(5, 5, 0, 10);
885 gfx::RectF
end_rect(50, 5, 0, 10);
888 controller().OnLongPressEvent();
889 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
890 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
892 // Selection should not be cleared if the selection bounds have not changed.
893 controller().OnTapEvent();
894 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
895 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
897 controller().OnTapEvent();
899 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
));
900 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
903 TEST_F(TouchSelectionControllerTest
, AllowShowingFromCurrentSelection
) {
904 gfx::RectF
start_rect(5, 5, 0, 10);
905 gfx::RectF
end_rect(50, 5, 0, 10);
908 // The selection should not be activated, as it wasn't yet allowed.
909 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
910 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
912 // Now explicitly allow showing from the previously supplied bounds.
913 controller().AllowShowingFromCurrentSelection();
914 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_SHOWN
));
915 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
917 // Repeated calls to show from the current selection should be ignored.
918 controller().AllowShowingFromCurrentSelection();
919 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
920 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
922 // Trying to show from an empty selection will have no result.
924 EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_CLEARED
));
925 controller().AllowShowingFromCurrentSelection();
926 EXPECT_THAT(GetAndResetEvents(), IsEmpty());
928 // Showing the insertion handle should also be supported.
929 controller().OnSelectionEditable(true);
930 controller().OnSelectionEmpty(false);
931 controller().HideAndDisallowShowingAutomatically();
932 gfx::RectF
insertion_rect(5, 5, 0, 10);
933 ChangeInsertion(insertion_rect
, visible
);
934 controller().AllowShowingFromCurrentSelection();
935 EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_SHOWN
));
936 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());