Add iOS bots to 10% CQ experiment
[chromium-blink-merge.git] / ui / touch_selection / touch_selection_controller_unittest.cc
blob2281aeeac1053768b8d8660b92e8005b0794aa92
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;
12 namespace ui {
13 namespace {
15 const int kDefaultTapTimeoutMs = 200;
16 const float kDefaulTapSlop = 10.f;
18 class MockTouchHandleDrawable : public TouchHandleDrawable {
19 public:
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);
32 private:
33 bool* intersects_rect_;
36 } // namespace
38 class TouchSelectionControllerTest : public testing::Test,
39 public TouchSelectionControllerClient {
40 public:
41 TouchSelectionControllerTest()
42 : last_event_(SELECTION_CLEARED),
43 caret_moved_(false),
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(
60 this,
61 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs),
62 kDefaulTapSlop,
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 {
75 caret_moved_ = true;
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 {
95 last_event_ = event;
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(
106 this,
107 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs),
108 kDefaulTapSlop,
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(),
117 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,
131 bool start_visible,
132 const gfx::RectF& end_rect,
133 bool end_visible) {
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);
144 void Animate() {
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_;
154 Animate();
155 return needs_animate;
158 bool GetAndResetCaretMoved() {
159 bool moved = caret_moved_;
160 caret_moved_ = false;
161 return moved;
164 bool GetAndResetSelectionMoved() {
165 bool moved = selection_moved_;
166 selection_moved_ = false;
167 return moved;
170 bool GetAndResetSelectionPointsSwapped() {
171 bool swapped = selection_points_swapped_;
172 selection_points_swapped_ = false;
173 return swapped;
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_; }
184 private:
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_;
190 bool caret_moved_;
191 bool selection_moved_;
192 bool selection_points_swapped_;
193 bool needs_animate_;
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);
201 bool visible = true;
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());
228 ClearInsertion();
229 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType());
232 TEST_F(TouchSelectionControllerTest, InsertionClearedWhenNoLongerEditable) {
233 gfx::RectF insertion_rect(5, 5, 0, 10);
234 bool visible = true;
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);
248 bool visible = true;
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);
286 bool visible = true;
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);
300 bool visible = true;
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);
329 bool visible = true;
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);
363 bool visible = true;
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);
408 bool visible = true;
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.
422 ClearInsertion();
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.
438 ClearInsertion();
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.
453 ClearInsertion();
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);
473 bool visible = true;
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);
512 bool visible = true;
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());
528 ClearSelection();
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);
536 bool visible = true;
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);
563 bool visible = true;
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);
617 bool visible = true;
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);
655 bool visible = true;
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);
772 bool visible = true;
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);
805 bool visible = true;
806 ChangeInsertion(insertion_rect, visible);
807 EXPECT_FALSE(GetAndResetNeedsAnimate());
809 visible = false;
810 ChangeInsertion(insertion_rect, visible);
811 EXPECT_TRUE(GetAndResetNeedsAnimate());
813 visible = true;
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();
824 visible = true;
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);
835 bool visible = true;
836 ChangeInsertion(insertion_rect, visible);
837 EXPECT_FALSE(GetAndResetNeedsAnimate());
839 controller().SetTemporarilyHidden(true);
840 EXPECT_TRUE(GetAndResetNeedsAnimate());
842 visible = false;
843 ChangeInsertion(insertion_rect, visible);
844 EXPECT_FALSE(GetAndResetNeedsAnimate());
846 visible = true;
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);
857 bool visible = true;
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();
868 ClearSelection();
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);
876 bool visible = true;
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.
893 ClearSelection();
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());
909 } // namespace ui