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"
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 int kDefaultTapTimeoutMs
= 200;
16 const float kDefaulTapSlop
= 10.f
;
18 class MockTouchHandleDrawable
: public TouchHandleDrawable
{
20 explicit MockTouchHandleDrawable(bool* contains_point
)
21 : intersects_rect_(contains_point
) {}
22 ~MockTouchHandleDrawable() override
{}
23 void SetEnabled(bool enabled
) override
{}
24 void SetOrientation(TouchHandleOrientation orientation
) override
{}
25 void SetAlpha(float alpha
) override
{}
26 void SetFocus(const gfx::PointF
& position
) override
{}
27 gfx::RectF
GetVisibleBounds() const override
{
28 return *intersects_rect_
? gfx::RectF(-1000, -1000, 2000, 2000)
29 : gfx::RectF(-1000, -1000, 0, 0);
33 bool* intersects_rect_
;
38 class TouchSelectionControllerTest
: public testing::Test
,
39 public TouchSelectionControllerClient
{
41 TouchSelectionControllerTest()
42 : last_event_(SELECTION_CLEARED
),
44 selection_moved_(false),
45 selection_points_swapped_(false),
46 needs_animate_(false),
47 animation_enabled_(true),
48 dragging_enabled_(false) {}
50 ~TouchSelectionControllerTest() override
{}
52 // testing::Test implementation.
54 void SetUp() override
{
55 // Default touch selection controller is created with
56 // |show_on_tap_for_empty_editable| flag set to false. Use
57 // |AllowShowingOnTapForEmptyEditable()| function to override it.
58 bool show_on_tap_for_empty_editable
= false;
59 controller_
.reset(new TouchSelectionController(
61 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs
),
63 show_on_tap_for_empty_editable
));
66 void TearDown() override
{ controller_
.reset(); }
68 // TouchSelectionControllerClient implementation.
70 bool SupportsAnimation() const override
{ return animation_enabled_
; }
72 void SetNeedsAnimate() override
{ needs_animate_
= true; }
74 void MoveCaret(const gfx::PointF
& position
) override
{
76 caret_position_
= position
;
79 void SelectBetweenCoordinates(const gfx::PointF
& base
,
80 const gfx::PointF
& extent
) override
{
81 if (base
== selection_end_
&& extent
== selection_start_
)
82 selection_points_swapped_
= true;
84 selection_start_
= base
;
85 selection_end_
= extent
;
88 void MoveRangeSelectionExtent(const gfx::PointF
& extent
) override
{
89 selection_moved_
= true;
90 selection_end_
= extent
;
93 void OnSelectionEvent(SelectionEventType event
,
94 const gfx::PointF
& end_position
) override
{
96 last_event_start_
= end_position
;
99 scoped_ptr
<TouchHandleDrawable
> CreateDrawable() override
{
100 return make_scoped_ptr(new MockTouchHandleDrawable(&dragging_enabled_
));
103 void AllowShowingOnTapForEmptyEditable() {
104 bool show_on_tap_for_empty_editable
= true;
105 controller_
.reset(new TouchSelectionController(
107 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs
),
109 show_on_tap_for_empty_editable
));
112 void SetAnimationEnabled(bool enabled
) { animation_enabled_
= enabled
; }
113 void SetDraggingEnabled(bool enabled
) { dragging_enabled_
= enabled
; }
115 void ClearSelection() {
116 controller_
->OnSelectionBoundsChanged(SelectionBound(),
120 void ClearInsertion() { ClearSelection(); }
122 void ChangeInsertion(const gfx::RectF
& rect
, bool visible
) {
123 SelectionBound bound
;
124 bound
.set_type(SelectionBound::CENTER
);
125 bound
.SetEdge(rect
.origin(), rect
.bottom_left());
126 bound
.set_visible(visible
);
127 controller_
->OnSelectionBoundsChanged(bound
, bound
);
130 void ChangeSelection(const gfx::RectF
& start_rect
,
132 const gfx::RectF
& end_rect
,
134 SelectionBound start_bound
, end_bound
;
135 start_bound
.set_type(SelectionBound::LEFT
);
136 end_bound
.set_type(SelectionBound::RIGHT
);
137 start_bound
.SetEdge(start_rect
.origin(), start_rect
.bottom_left());
138 end_bound
.SetEdge(end_rect
.origin(), end_rect
.bottom_left());
139 start_bound
.set_visible(start_visible
);
140 end_bound
.set_visible(end_visible
);
141 controller_
->OnSelectionBoundsChanged(start_bound
, end_bound
);
145 base::TimeTicks now
= base::TimeTicks::Now();
146 while (needs_animate_
) {
147 needs_animate_
= controller_
->Animate(now
);
148 now
+= base::TimeDelta::FromMilliseconds(16);
152 bool GetAndResetNeedsAnimate() {
153 bool needs_animate
= needs_animate_
;
155 return needs_animate
;
158 bool GetAndResetCaretMoved() {
159 bool moved
= caret_moved_
;
160 caret_moved_
= false;
164 bool GetAndResetSelectionMoved() {
165 bool moved
= selection_moved_
;
166 selection_moved_
= false;
170 bool GetAndResetSelectionPointsSwapped() {
171 bool swapped
= selection_points_swapped_
;
172 selection_points_swapped_
= false;
176 const gfx::PointF
& GetLastCaretPosition() const { return caret_position_
; }
177 const gfx::PointF
& GetLastSelectionStart() const { return selection_start_
; }
178 const gfx::PointF
& GetLastSelectionEnd() const { return selection_end_
; }
179 SelectionEventType
GetLastEventType() const { return last_event_
; }
180 const gfx::PointF
& GetLastEventAnchor() const { return last_event_start_
; }
182 TouchSelectionController
& controller() { return *controller_
; }
185 gfx::PointF last_event_start_
;
186 gfx::PointF caret_position_
;
187 gfx::PointF selection_start_
;
188 gfx::PointF selection_end_
;
189 SelectionEventType last_event_
;
191 bool selection_moved_
;
192 bool selection_points_swapped_
;
194 bool animation_enabled_
;
195 bool dragging_enabled_
;
196 scoped_ptr
<TouchSelectionController
> controller_
;
199 TEST_F(TouchSelectionControllerTest
, InsertionBasic
) {
200 gfx::RectF
insertion_rect(5, 5, 0, 10);
203 // Insertion events are ignored until automatic showing is enabled.
204 ChangeInsertion(insertion_rect
, visible
);
205 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
206 controller().OnTapEvent();
208 // Insertion events are ignored until the selection region is marked editable.
209 ChangeInsertion(insertion_rect
, visible
);
210 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
212 controller().OnTapEvent();
213 controller().OnSelectionEditable(true);
214 ChangeInsertion(insertion_rect
, visible
);
215 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
216 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
218 insertion_rect
.Offset(1, 0);
219 ChangeInsertion(insertion_rect
, visible
);
220 EXPECT_EQ(INSERTION_MOVED
, GetLastEventType());
221 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
223 insertion_rect
.Offset(0, 1);
224 ChangeInsertion(insertion_rect
, visible
);
225 EXPECT_EQ(INSERTION_MOVED
, GetLastEventType());
226 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
229 EXPECT_EQ(INSERTION_CLEARED
, GetLastEventType());
232 TEST_F(TouchSelectionControllerTest
, InsertionClearedWhenNoLongerEditable
) {
233 gfx::RectF
insertion_rect(5, 5, 0, 10);
235 controller().OnTapEvent();
236 controller().OnSelectionEditable(true);
238 ChangeInsertion(insertion_rect
, visible
);
239 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
240 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
242 controller().OnSelectionEditable(false);
243 EXPECT_EQ(INSERTION_CLEARED
, GetLastEventType());
246 TEST_F(TouchSelectionControllerTest
, InsertionWithNoShowOnTapForEmptyEditable
) {
247 gfx::RectF
insertion_rect(5, 5, 0, 10);
249 controller().OnSelectionEditable(true);
251 // Taps on an empty editable region should be ignored if the controller is
252 // created with |show_on_tap_for_empty_editable| set to false.
253 controller().OnTapEvent();
254 controller().OnSelectionEmpty(true);
255 ChangeInsertion(insertion_rect
, visible
);
256 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
258 // Once the region becomes non-empty, taps should show the insertion handle.
259 controller().OnTapEvent();
260 controller().OnSelectionEmpty(false);
261 ChangeInsertion(insertion_rect
, visible
);
262 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
263 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
265 // Reset the selection.
266 controller().HideAndDisallowShowingAutomatically();
267 EXPECT_EQ(INSERTION_CLEARED
, GetLastEventType());
269 // Long-pressing should show the handle even if the editable region is empty.
270 insertion_rect
.Offset(2, -2);
271 controller().OnLongPressEvent();
272 controller().OnSelectionEmpty(true);
273 ChangeInsertion(insertion_rect
, visible
);
274 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
275 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
277 // Single Tap on an empty edit field should clear insertion handle.
278 controller().OnTapEvent();
279 EXPECT_EQ(INSERTION_CLEARED
, GetLastEventType());
282 TEST_F(TouchSelectionControllerTest
, InsertionWithShowOnTapForEmptyEditable
) {
283 AllowShowingOnTapForEmptyEditable();
285 gfx::RectF
insertion_rect(5, 5, 0, 10);
287 controller().OnSelectionEditable(true);
289 // Taps on an empty editable region should show the insertion handle if the
290 // controller is created with |show_on_tap_for_empty_editable| set to true.
291 controller().OnTapEvent();
292 controller().OnSelectionEmpty(true);
293 ChangeInsertion(insertion_rect
, visible
);
294 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
295 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
298 TEST_F(TouchSelectionControllerTest
, InsertionAppearsAfterTapFollowingTyping
) {
299 gfx::RectF
insertion_rect(5, 5, 0, 10);
302 // Simulate the user tapping an empty text field.
303 controller().OnTapEvent();
304 controller().OnSelectionEditable(true);
305 controller().OnSelectionEmpty(true);
306 ChangeInsertion(insertion_rect
, visible
);
307 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
309 // Simulate the cursor moving while a user is typing.
310 insertion_rect
.Offset(10, 0);
311 controller().OnSelectionEmpty(false);
312 ChangeInsertion(insertion_rect
, visible
);
313 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
315 // If the user taps the *same* position as the cursor at the end of the text
316 // entry, the handle should appear.
317 controller().OnTapEvent();
318 ChangeInsertion(insertion_rect
, visible
);
319 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
320 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());
323 TEST_F(TouchSelectionControllerTest
, InsertionToSelectionTransition
) {
324 controller().OnLongPressEvent();
325 controller().OnSelectionEditable(true);
327 gfx::RectF
start_rect(5, 5, 0, 10);
328 gfx::RectF
end_rect(50, 5, 0, 10);
331 ChangeInsertion(start_rect
, visible
);
332 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
333 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
335 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
336 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
337 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
339 ChangeInsertion(end_rect
, visible
);
340 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
341 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventAnchor());
343 controller().HideAndDisallowShowingAutomatically();
344 EXPECT_EQ(INSERTION_CLEARED
, GetLastEventType());
346 controller().OnTapEvent();
347 ChangeInsertion(end_rect
, visible
);
348 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
349 EXPECT_EQ(end_rect
.bottom_left(), GetLastEventAnchor());
352 TEST_F(TouchSelectionControllerTest
, InsertionDragged
) {
353 base::TimeTicks event_time
= base::TimeTicks::Now();
354 controller().OnTapEvent();
355 controller().OnSelectionEditable(true);
357 // The touch sequence should not be handled if insertion is not active.
358 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
359 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
361 float line_height
= 10.f
;
362 gfx::RectF
start_rect(10, 0, 0, line_height
);
364 ChangeInsertion(start_rect
, visible
);
365 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
366 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
368 // The touch sequence should be handled only if the drawable reports a hit.
369 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
370 SetDraggingEnabled(true);
371 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
372 EXPECT_FALSE(GetAndResetCaretMoved());
374 // The MoveCaret() result should reflect the movement.
375 // The reported position is offset from the center of |start_rect|.
376 gfx::PointF start_offset
= start_rect
.CenterPoint();
377 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 5);
378 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
379 EXPECT_TRUE(GetAndResetCaretMoved());
380 EXPECT_EQ(start_offset
+ gfx::Vector2dF(0, 5), GetLastCaretPosition());
382 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 5, 5);
383 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
384 EXPECT_TRUE(GetAndResetCaretMoved());
385 EXPECT_EQ(start_offset
+ gfx::Vector2dF(5, 5), GetLastCaretPosition());
387 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 10, 10);
388 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
389 EXPECT_TRUE(GetAndResetCaretMoved());
390 EXPECT_EQ(start_offset
+ gfx::Vector2dF(10, 10), GetLastCaretPosition());
392 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
393 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
394 EXPECT_FALSE(GetAndResetCaretMoved());
396 // Once the drag is complete, no more touch events should be consumed until
397 // the next ACTION_DOWN.
398 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
401 TEST_F(TouchSelectionControllerTest
, InsertionTapped
) {
402 base::TimeTicks event_time
= base::TimeTicks::Now();
403 controller().OnTapEvent();
404 controller().OnSelectionEditable(true);
405 SetDraggingEnabled(true);
407 gfx::RectF
start_rect(10, 0, 0, 10);
409 ChangeInsertion(start_rect
, visible
);
410 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
412 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
413 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
414 //TODO(AKV): this test case has to be modified once crbug.com/394093 is fixed.
415 EXPECT_EQ(INSERTION_DRAG_STARTED
, GetLastEventType());
417 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
418 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
419 EXPECT_EQ(INSERTION_TAPPED
, GetLastEventType());
421 // Reset the insertion.
423 controller().OnTapEvent();
424 ChangeInsertion(start_rect
, visible
);
425 ASSERT_EQ(INSERTION_SHOWN
, GetLastEventType());
427 // No tap should be signalled if the time between DOWN and UP was too long.
428 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
429 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
430 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
,
431 event_time
+ base::TimeDelta::FromSeconds(1),
434 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
435 EXPECT_EQ(INSERTION_DRAG_STARTED
, GetLastEventType());
437 // Reset the insertion.
439 controller().OnTapEvent();
440 ChangeInsertion(start_rect
, visible
);
441 ASSERT_EQ(INSERTION_SHOWN
, GetLastEventType());
443 // No tap should be signalled if the drag was too long.
444 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
445 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
446 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 100, 0);
447 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
448 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 100, 0);
449 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
450 EXPECT_EQ(INSERTION_DRAG_STARTED
, GetLastEventType());
452 // Reset the insertion.
454 controller().OnTapEvent();
455 ChangeInsertion(start_rect
, visible
);
456 ASSERT_EQ(INSERTION_SHOWN
, GetLastEventType());
458 // No tap should be signalled if the touch sequence is cancelled.
459 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
460 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
461 event
= MockMotionEvent(MockMotionEvent::ACTION_CANCEL
, event_time
, 0, 0);
462 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
463 EXPECT_EQ(INSERTION_DRAG_STARTED
, GetLastEventType());
466 TEST_F(TouchSelectionControllerTest
, InsertionNotResetByRepeatedTapOrPress
) {
467 base::TimeTicks event_time
= base::TimeTicks::Now();
468 controller().OnTapEvent();
469 controller().OnSelectionEditable(true);
470 SetDraggingEnabled(true);
472 gfx::RectF
anchor_rect(10, 0, 0, 10);
474 ChangeInsertion(anchor_rect
, visible
);
475 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
476 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
478 // Tapping again shouldn't reset the active insertion point.
479 controller().OnTapEvent();
480 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
481 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
482 EXPECT_EQ(INSERTION_DRAG_STARTED
, GetLastEventType());
483 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
485 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
486 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
487 EXPECT_EQ(INSERTION_TAPPED
, GetLastEventType());
488 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
490 anchor_rect
.Offset(5, 15);
491 ChangeInsertion(anchor_rect
, visible
);
492 EXPECT_EQ(INSERTION_MOVED
, GetLastEventType());
493 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
495 // Pressing shouldn't reset the active insertion point.
496 controller().OnLongPressEvent();
497 controller().OnSelectionEmpty(true);
498 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
499 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
500 EXPECT_EQ(INSERTION_DRAG_STARTED
, GetLastEventType());
501 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
503 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
504 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
505 EXPECT_EQ(INSERTION_TAPPED
, GetLastEventType());
506 EXPECT_EQ(anchor_rect
.bottom_left(), GetLastEventAnchor());
509 TEST_F(TouchSelectionControllerTest
, SelectionBasic
) {
510 gfx::RectF
start_rect(5, 5, 0, 10);
511 gfx::RectF
end_rect(50, 5, 0, 10);
514 // Selection events are ignored until automatic showing is enabled.
515 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
516 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
518 controller().OnLongPressEvent();
519 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
520 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
521 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
523 start_rect
.Offset(1, 0);
524 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
525 // Selection movement does not currently trigger a separate event.
526 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
529 EXPECT_EQ(SELECTION_CLEARED
, GetLastEventType());
530 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
533 TEST_F(TouchSelectionControllerTest
, SelectionRepeatedLongPress
) {
534 gfx::RectF
start_rect(5, 5, 0, 10);
535 gfx::RectF
end_rect(50, 5, 0, 10);
538 controller().OnLongPressEvent();
539 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
540 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
541 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
543 // A long press triggering a new selection should re-send the SELECTION_SHOWN
544 // event notification.
545 start_rect
.Offset(10, 10);
546 controller().OnLongPressEvent();
547 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
548 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
549 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
552 TEST_F(TouchSelectionControllerTest
, SelectionDragged
) {
553 base::TimeTicks event_time
= base::TimeTicks::Now();
554 controller().OnLongPressEvent();
556 // The touch sequence should not be handled if selection is not active.
557 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
, 0, 0);
558 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
560 float line_height
= 10.f
;
561 gfx::RectF
start_rect(0, 0, 0, line_height
);
562 gfx::RectF
end_rect(50, 0, 0, line_height
);
564 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
565 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
566 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
568 // The touch sequence should be handled only if the drawable reports a hit.
569 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
570 SetDraggingEnabled(true);
571 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
572 EXPECT_FALSE(GetAndResetSelectionMoved());
574 // The SelectBetweenCoordinates() result should reflect the movement. Note
575 // that the start coordinate will always reflect the "fixed" handle's
576 // position, in this case the position from |end_rect|.
577 // Note that the reported position is offset from the center of the
578 // input rects (i.e., the middle of the corresponding text line).
579 gfx::PointF fixed_offset
= end_rect
.CenterPoint();
580 gfx::PointF start_offset
= start_rect
.CenterPoint();
581 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 5);
582 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
583 EXPECT_EQ(SELECTION_DRAG_STARTED
, GetLastEventType());
584 EXPECT_TRUE(GetAndResetSelectionMoved());
585 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
586 EXPECT_EQ(start_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
588 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 5, 5);
589 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
590 EXPECT_TRUE(GetAndResetSelectionMoved());
591 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
592 EXPECT_EQ(start_offset
+ gfx::Vector2dF(5, 5), GetLastSelectionEnd());
594 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 10, 5);
595 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
596 EXPECT_TRUE(GetAndResetSelectionMoved());
597 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
598 EXPECT_EQ(start_offset
+ gfx::Vector2dF(10, 5), GetLastSelectionEnd());
600 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
601 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
602 EXPECT_EQ(SELECTION_DRAG_STOPPED
, GetLastEventType());
603 EXPECT_FALSE(GetAndResetSelectionMoved());
605 // Once the drag is complete, no more touch events should be consumed until
606 // the next ACTION_DOWN.
607 EXPECT_FALSE(controller().WillHandleTouchEvent(event
));
610 TEST_F(TouchSelectionControllerTest
, SelectionDraggedWithOverlap
) {
611 base::TimeTicks event_time
= base::TimeTicks::Now();
612 controller().OnLongPressEvent();
614 float line_height
= 10.f
;
615 gfx::RectF
start_rect(0, 0, 0, line_height
);
616 gfx::RectF
end_rect(50, 0, 0, line_height
);
618 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
619 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
620 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
622 // The ACTION_DOWN should lock to the closest handle.
623 gfx::PointF end_offset
= end_rect
.CenterPoint();
624 gfx::PointF fixed_offset
= start_rect
.CenterPoint();
625 float touch_down_x
= (end_offset
.x() + fixed_offset
.x()) / 2 + 1.f
;
626 MockMotionEvent
event(
627 MockMotionEvent::ACTION_DOWN
, event_time
, touch_down_x
, 0);
628 SetDraggingEnabled(true);
629 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
630 EXPECT_EQ(SELECTION_DRAG_STARTED
, GetLastEventType());
631 EXPECT_FALSE(GetAndResetSelectionMoved());
633 // Even though the ACTION_MOVE is over the start handle, it should continue
634 // targetting the end handle that consumed the ACTION_DOWN.
635 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
, 0, 0);
636 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
637 EXPECT_TRUE(GetAndResetSelectionMoved());
638 EXPECT_EQ(fixed_offset
, GetLastSelectionStart());
639 EXPECT_EQ(end_offset
- gfx::Vector2dF(touch_down_x
, 0),
640 GetLastSelectionEnd());
642 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 0, 0);
643 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
644 EXPECT_EQ(SELECTION_DRAG_STOPPED
, GetLastEventType());
645 EXPECT_FALSE(GetAndResetSelectionMoved());
648 TEST_F(TouchSelectionControllerTest
, SelectionDraggedToSwitchBaseAndExtent
) {
649 base::TimeTicks event_time
= base::TimeTicks::Now();
650 controller().OnLongPressEvent();
652 float line_height
= 10.f
;
653 gfx::RectF
start_rect(50, line_height
, 0, line_height
);
654 gfx::RectF
end_rect(100, line_height
, 0, line_height
);
656 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
657 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
658 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
660 SetDraggingEnabled(true);
662 // Move the extent, not triggering a swap of points.
663 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
,
664 end_rect
.x(), end_rect
.bottom());
665 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
666 EXPECT_FALSE(GetAndResetSelectionMoved());
667 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
669 gfx::PointF base_offset
= start_rect
.CenterPoint();
670 gfx::PointF extent_offset
= end_rect
.CenterPoint();
671 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
672 end_rect
.x(), end_rect
.bottom() + 5);
673 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
674 EXPECT_EQ(SELECTION_DRAG_STARTED
, GetLastEventType());
675 EXPECT_TRUE(GetAndResetSelectionMoved());
676 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
677 EXPECT_EQ(base_offset
, GetLastSelectionStart());
678 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
680 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
681 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
682 EXPECT_EQ(SELECTION_DRAG_STOPPED
, GetLastEventType());
683 EXPECT_FALSE(GetAndResetSelectionMoved());
685 end_rect
+= gfx::Vector2dF(0, 5);
686 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
688 // Move the base, triggering a swap of points.
689 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
690 start_rect
.x(), start_rect
.bottom());
691 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
692 EXPECT_FALSE(GetAndResetSelectionMoved());
693 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
695 base_offset
= end_rect
.CenterPoint();
696 extent_offset
= start_rect
.CenterPoint();
697 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
698 start_rect
.x(), start_rect
.bottom() + 5);
699 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
700 EXPECT_EQ(SELECTION_DRAG_STARTED
, GetLastEventType());
701 EXPECT_TRUE(GetAndResetSelectionMoved());
702 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
703 EXPECT_EQ(base_offset
, GetLastSelectionStart());
704 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
706 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
707 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
708 EXPECT_EQ(SELECTION_DRAG_STOPPED
, GetLastEventType());
709 EXPECT_FALSE(GetAndResetSelectionMoved());
711 start_rect
+= gfx::Vector2dF(0, 5);
712 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
714 // Move the same point again, not triggering a swap of points.
715 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
716 start_rect
.x(), start_rect
.bottom());
717 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
718 EXPECT_FALSE(GetAndResetSelectionMoved());
719 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
721 base_offset
= end_rect
.CenterPoint();
722 extent_offset
= start_rect
.CenterPoint();
723 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
724 start_rect
.x(), start_rect
.bottom() + 5);
725 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
726 EXPECT_EQ(SELECTION_DRAG_STARTED
, GetLastEventType());
727 EXPECT_TRUE(GetAndResetSelectionMoved());
728 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
729 EXPECT_EQ(base_offset
, GetLastSelectionStart());
730 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
732 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
733 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
734 EXPECT_EQ(SELECTION_DRAG_STOPPED
, GetLastEventType());
735 EXPECT_FALSE(GetAndResetSelectionMoved());
737 start_rect
+= gfx::Vector2dF(0, 5);
738 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
740 // Move the base, triggering a swap of points.
741 event
= MockMotionEvent(MockMotionEvent::ACTION_DOWN
, event_time
,
742 end_rect
.x(), end_rect
.bottom());
743 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
744 EXPECT_FALSE(GetAndResetSelectionMoved());
745 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
747 base_offset
= start_rect
.CenterPoint();
748 extent_offset
= end_rect
.CenterPoint();
749 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
750 end_rect
.x(), end_rect
.bottom() + 5);
751 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
752 EXPECT_EQ(SELECTION_DRAG_STARTED
, GetLastEventType());
753 EXPECT_TRUE(GetAndResetSelectionMoved());
754 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
755 EXPECT_EQ(base_offset
, GetLastSelectionStart());
756 EXPECT_EQ(extent_offset
+ gfx::Vector2dF(0, 5), GetLastSelectionEnd());
758 event
= MockMotionEvent(MockMotionEvent::ACTION_UP
, event_time
, 10, 5);
759 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
760 EXPECT_EQ(SELECTION_DRAG_STOPPED
, GetLastEventType());
761 EXPECT_FALSE(GetAndResetSelectionMoved());
764 TEST_F(TouchSelectionControllerTest
, SelectionDragExtremeLineSize
) {
765 base::TimeTicks event_time
= base::TimeTicks::Now();
766 controller().OnLongPressEvent();
768 float small_line_height
= 1.f
;
769 float large_line_height
= 50.f
;
770 gfx::RectF
small_line_rect(0, 0, 0, small_line_height
);
771 gfx::RectF
large_line_rect(50, 50, 0, large_line_height
);
773 ChangeSelection(small_line_rect
, visible
, large_line_rect
, visible
);
774 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
775 EXPECT_EQ(small_line_rect
.bottom_left(), GetLastEventAnchor());
777 // Start dragging the handle on the small line.
778 MockMotionEvent
event(MockMotionEvent::ACTION_DOWN
, event_time
,
779 small_line_rect
.x(), small_line_rect
.y());
780 SetDraggingEnabled(true);
781 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
782 EXPECT_EQ(SELECTION_DRAG_STARTED
, GetLastEventType());
783 // The drag coordinate for large lines should be capped to a reasonable
784 // offset, allowing seamless transition to neighboring lines with different
785 // sizes. The drag coordinate for small lines should have an offset
786 // commensurate with the small line size.
787 EXPECT_EQ(large_line_rect
.bottom_left() - gfx::Vector2dF(0, 5.f
),
788 GetLastSelectionStart());
789 EXPECT_EQ(small_line_rect
.CenterPoint(), GetLastSelectionEnd());
791 small_line_rect
+= gfx::Vector2dF(25.f
, 0);
792 event
= MockMotionEvent(MockMotionEvent::ACTION_MOVE
, event_time
,
793 small_line_rect
.x(), small_line_rect
.y());
794 EXPECT_TRUE(controller().WillHandleTouchEvent(event
));
795 EXPECT_TRUE(GetAndResetSelectionMoved());
796 EXPECT_EQ(small_line_rect
.CenterPoint(), GetLastSelectionEnd());
799 TEST_F(TouchSelectionControllerTest
, Animation
) {
800 controller().OnTapEvent();
801 controller().OnSelectionEditable(true);
803 gfx::RectF
insertion_rect(5, 5, 0, 10);
806 ChangeInsertion(insertion_rect
, visible
);
807 EXPECT_FALSE(GetAndResetNeedsAnimate());
810 ChangeInsertion(insertion_rect
, visible
);
811 EXPECT_TRUE(GetAndResetNeedsAnimate());
814 ChangeInsertion(insertion_rect
, visible
);
815 EXPECT_TRUE(GetAndResetNeedsAnimate());
817 // If the handles are explicity hidden, no animation should be triggered.
818 controller().HideAndDisallowShowingAutomatically();
819 EXPECT_FALSE(GetAndResetNeedsAnimate());
821 // If the client doesn't support animation, no animation should be triggered.
822 SetAnimationEnabled(false);
823 controller().OnTapEvent();
825 ChangeInsertion(insertion_rect
, visible
);
826 EXPECT_FALSE(GetAndResetNeedsAnimate());
829 TEST_F(TouchSelectionControllerTest
, TemporarilyHidden
) {
830 controller().OnTapEvent();
831 controller().OnSelectionEditable(true);
833 gfx::RectF
insertion_rect(5, 5, 0, 10);
836 ChangeInsertion(insertion_rect
, visible
);
837 EXPECT_FALSE(GetAndResetNeedsAnimate());
839 controller().SetTemporarilyHidden(true);
840 EXPECT_TRUE(GetAndResetNeedsAnimate());
843 ChangeInsertion(insertion_rect
, visible
);
844 EXPECT_FALSE(GetAndResetNeedsAnimate());
847 ChangeInsertion(insertion_rect
, visible
);
848 EXPECT_FALSE(GetAndResetNeedsAnimate());
850 controller().SetTemporarilyHidden(false);
851 EXPECT_TRUE(GetAndResetNeedsAnimate());
854 TEST_F(TouchSelectionControllerTest
, SelectionClearOnTap
) {
855 gfx::RectF
start_rect(5, 5, 0, 10);
856 gfx::RectF
end_rect(50, 5, 0, 10);
859 controller().OnLongPressEvent();
860 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
862 // Selection should not be cleared if the selection bounds have not changed.
863 controller().OnTapEvent();
864 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
865 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
867 controller().OnTapEvent();
869 EXPECT_EQ(SELECTION_CLEARED
, GetLastEventType());
870 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
873 TEST_F(TouchSelectionControllerTest
, AllowShowingFromCurrentSelection
) {
874 gfx::RectF
start_rect(5, 5, 0, 10);
875 gfx::RectF
end_rect(50, 5, 0, 10);
878 // The selection should not have be activated, as it wasn't yet allowed.
879 ChangeSelection(start_rect
, visible
, end_rect
, visible
);
880 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
882 // Now explicitly allow showing from the previously supplied bounds.
883 controller().AllowShowingFromCurrentSelection();
884 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
885 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
887 // Repeated calls to show from the current selection should be ignored.
888 controller().AllowShowingFromCurrentSelection();
889 EXPECT_EQ(SELECTION_SHOWN
, GetLastEventType());
890 EXPECT_EQ(start_rect
.bottom_left(), GetLastEventAnchor());
892 // Trying to show from an empty selection will have no result.
894 EXPECT_EQ(SELECTION_CLEARED
, GetLastEventType());
895 controller().AllowShowingFromCurrentSelection();
896 EXPECT_EQ(SELECTION_CLEARED
, GetLastEventType());
898 // Showing the insertion handle should also be supported.
899 controller().OnSelectionEditable(true);
900 controller().OnSelectionEmpty(false);
901 controller().HideAndDisallowShowingAutomatically();
902 gfx::RectF
insertion_rect(5, 5, 0, 10);
903 ChangeInsertion(insertion_rect
, visible
);
904 controller().AllowShowingFromCurrentSelection();
905 EXPECT_EQ(INSERTION_SHOWN
, GetLastEventType());
906 EXPECT_EQ(insertion_rect
.bottom_left(), GetLastEventAnchor());