Cleanup: Only do a Local State file lookup when needed.
[chromium-blink-merge.git] / ui / touch_selection / touch_selection_controller_unittest.cc
blob79c9823d24941019b267083618d835ceb560f426
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.
53 void SetUp() override {
54 controller_.reset(new TouchSelectionController(
55 this,
56 base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs),
57 kDefaulTapSlop));
60 void TearDown() override { controller_.reset(); }
62 // TouchSelectionControllerClient implementation.
64 bool SupportsAnimation() const override { return animation_enabled_; }
66 void SetNeedsAnimate() override { needs_animate_ = true; }
68 void MoveCaret(const gfx::PointF& position) override {
69 caret_moved_ = true;
70 caret_position_ = position;
73 void SelectBetweenCoordinates(const gfx::PointF& base,
74 const gfx::PointF& extent) override {
75 if (base == selection_end_ && extent == selection_start_)
76 selection_points_swapped_ = true;
78 selection_start_ = base;
79 selection_end_ = extent;
82 void MoveRangeSelectionExtent(const gfx::PointF& extent) override {
83 selection_moved_ = true;
84 selection_end_ = extent;
87 void OnSelectionEvent(SelectionEventType event,
88 const gfx::PointF& end_position) override {
89 last_event_ = event;
90 last_event_start_ = end_position;
93 scoped_ptr<TouchHandleDrawable> CreateDrawable() override {
94 return scoped_ptr<TouchHandleDrawable>(
95 new MockTouchHandleDrawable(&dragging_enabled_));
98 void SetAnimationEnabled(bool enabled) { animation_enabled_ = enabled; }
99 void SetDraggingEnabled(bool enabled) { dragging_enabled_ = enabled; }
101 void ClearSelection() {
102 controller_->OnSelectionBoundsChanged(SelectionBound(),
103 SelectionBound());
106 void ClearInsertion() { ClearSelection(); }
108 void ChangeInsertion(const gfx::RectF& rect, bool visible) {
109 SelectionBound bound;
110 bound.set_type(SelectionBound::CENTER);
111 bound.SetEdge(rect.origin(), rect.bottom_left());
112 bound.set_visible(visible);
113 controller_->OnSelectionBoundsChanged(bound, bound);
116 void ChangeSelection(const gfx::RectF& start_rect,
117 bool start_visible,
118 const gfx::RectF& end_rect,
119 bool end_visible) {
120 SelectionBound start_bound, end_bound;
121 start_bound.set_type(SelectionBound::LEFT);
122 end_bound.set_type(SelectionBound::RIGHT);
123 start_bound.SetEdge(start_rect.origin(), start_rect.bottom_left());
124 end_bound.SetEdge(end_rect.origin(), end_rect.bottom_left());
125 start_bound.set_visible(start_visible);
126 end_bound.set_visible(end_visible);
127 controller_->OnSelectionBoundsChanged(start_bound, end_bound);
130 void Animate() {
131 base::TimeTicks now = base::TimeTicks::Now();
132 while (needs_animate_) {
133 needs_animate_ = controller_->Animate(now);
134 now += base::TimeDelta::FromMilliseconds(16);
138 bool GetAndResetNeedsAnimate() {
139 bool needs_animate = needs_animate_;
140 Animate();
141 return needs_animate;
144 bool GetAndResetCaretMoved() {
145 bool moved = caret_moved_;
146 caret_moved_ = false;
147 return moved;
150 bool GetAndResetSelectionMoved() {
151 bool moved = selection_moved_;
152 selection_moved_ = false;
153 return moved;
156 bool GetAndResetSelectionPointsSwapped() {
157 bool swapped = selection_points_swapped_;
158 selection_points_swapped_ = false;
159 return swapped;
162 const gfx::PointF& GetLastCaretPosition() const { return caret_position_; }
163 const gfx::PointF& GetLastSelectionStart() const { return selection_start_; }
164 const gfx::PointF& GetLastSelectionEnd() const { return selection_end_; }
165 SelectionEventType GetLastEventType() const { return last_event_; }
166 const gfx::PointF& GetLastEventAnchor() const { return last_event_start_; }
168 TouchSelectionController& controller() { return *controller_; }
170 private:
171 gfx::PointF last_event_start_;
172 gfx::PointF caret_position_;
173 gfx::PointF selection_start_;
174 gfx::PointF selection_end_;
175 SelectionEventType last_event_;
176 bool caret_moved_;
177 bool selection_moved_;
178 bool selection_points_swapped_;
179 bool needs_animate_;
180 bool animation_enabled_;
181 bool dragging_enabled_;
182 scoped_ptr<TouchSelectionController> controller_;
185 TEST_F(TouchSelectionControllerTest, InsertionBasic) {
186 gfx::RectF insertion_rect(5, 5, 0, 10);
187 bool visible = true;
189 // Insertion events are ignored until automatic showing is enabled.
190 ChangeInsertion(insertion_rect, visible);
191 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
192 controller().OnTapEvent();
194 // Insertion events are ignored until the selection region is marked editable.
195 ChangeInsertion(insertion_rect, visible);
196 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
198 controller().OnTapEvent();
199 controller().OnSelectionEditable(true);
200 ChangeInsertion(insertion_rect, visible);
201 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
202 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
204 insertion_rect.Offset(1, 0);
205 ChangeInsertion(insertion_rect, visible);
206 EXPECT_EQ(INSERTION_MOVED, GetLastEventType());
207 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
209 insertion_rect.Offset(0, 1);
210 ChangeInsertion(insertion_rect, visible);
211 EXPECT_EQ(INSERTION_MOVED, GetLastEventType());
212 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
214 ClearInsertion();
215 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType());
218 TEST_F(TouchSelectionControllerTest, InsertionClearedWhenNoLongerEditable) {
219 gfx::RectF insertion_rect(5, 5, 0, 10);
220 bool visible = true;
221 controller().OnTapEvent();
222 controller().OnSelectionEditable(true);
224 ChangeInsertion(insertion_rect, visible);
225 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
226 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
228 controller().OnSelectionEditable(false);
229 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType());
232 TEST_F(TouchSelectionControllerTest, InsertionStaysHiddenIfEmptyRegionTapped) {
233 gfx::RectF insertion_rect(5, 5, 0, 10);
234 bool visible = true;
235 controller().OnSelectionEditable(true);
237 // Taps should be ignored if they're in an empty editable region.
238 controller().OnTapEvent();
239 controller().OnSelectionEmpty(true);
240 ChangeInsertion(insertion_rect, visible);
241 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
243 // Once the region becomes editable, taps should show the insertion handle.
244 controller().OnTapEvent();
245 controller().OnSelectionEmpty(false);
246 ChangeInsertion(insertion_rect, visible);
247 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
248 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
250 // Reset the selection.
251 controller().HideAndDisallowShowingAutomatically();
252 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType());
254 // Long-pressing should show the handle even if the editable region is empty.
255 insertion_rect.Offset(2, -2);
256 controller().OnLongPressEvent();
257 controller().OnSelectionEmpty(true);
258 ChangeInsertion(insertion_rect, visible);
259 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
260 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
262 // Single Tap on an empty edit field should clear insertion handle.
263 controller().OnTapEvent();
264 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType());
267 TEST_F(TouchSelectionControllerTest, InsertionAppearsAfterTapFollowingTyping) {
268 gfx::RectF insertion_rect(5, 5, 0, 10);
269 bool visible = true;
271 // Simulate the user tapping an empty text field.
272 controller().OnTapEvent();
273 controller().OnSelectionEditable(true);
274 controller().OnSelectionEmpty(true);
275 ChangeInsertion(insertion_rect, visible);
276 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
278 // Simulate the cursor moving while a user is typing.
279 insertion_rect.Offset(10, 0);
280 controller().OnSelectionEmpty(false);
281 ChangeInsertion(insertion_rect, visible);
282 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
284 // If the user taps the *same* position as the cursor at the end of the text
285 // entry, the handle should appear.
286 controller().OnTapEvent();
287 ChangeInsertion(insertion_rect, visible);
288 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
289 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
292 TEST_F(TouchSelectionControllerTest, InsertionToSelectionTransition) {
293 controller().OnLongPressEvent();
294 controller().OnSelectionEditable(true);
296 gfx::RectF start_rect(5, 5, 0, 10);
297 gfx::RectF end_rect(50, 5, 0, 10);
298 bool visible = true;
300 ChangeInsertion(start_rect, visible);
301 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
302 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
304 ChangeSelection(start_rect, visible, end_rect, visible);
305 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
306 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
308 ChangeInsertion(end_rect, visible);
309 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
310 EXPECT_EQ(end_rect.bottom_left(), GetLastEventAnchor());
312 controller().HideAndDisallowShowingAutomatically();
313 EXPECT_EQ(INSERTION_CLEARED, GetLastEventType());
315 controller().OnTapEvent();
316 ChangeInsertion(end_rect, visible);
317 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
318 EXPECT_EQ(end_rect.bottom_left(), GetLastEventAnchor());
321 TEST_F(TouchSelectionControllerTest, InsertionDragged) {
322 base::TimeTicks event_time = base::TimeTicks::Now();
323 controller().OnTapEvent();
324 controller().OnSelectionEditable(true);
326 // The touch sequence should not be handled if insertion is not active.
327 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
328 EXPECT_FALSE(controller().WillHandleTouchEvent(event));
330 float line_height = 10.f;
331 gfx::RectF start_rect(10, 0, 0, line_height);
332 bool visible = true;
333 ChangeInsertion(start_rect, visible);
334 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
335 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
337 // The touch sequence should be handled only if the drawable reports a hit.
338 EXPECT_FALSE(controller().WillHandleTouchEvent(event));
339 SetDraggingEnabled(true);
340 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
341 EXPECT_FALSE(GetAndResetCaretMoved());
343 // The MoveCaret() result should reflect the movement.
344 // The reported position is offset from the center of |start_rect|.
345 gfx::PointF start_offset = start_rect.CenterPoint();
346 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5);
347 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
348 EXPECT_TRUE(GetAndResetCaretMoved());
349 EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastCaretPosition());
351 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5);
352 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
353 EXPECT_TRUE(GetAndResetCaretMoved());
354 EXPECT_EQ(start_offset + gfx::Vector2dF(5, 5), GetLastCaretPosition());
356 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 10);
357 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
358 EXPECT_TRUE(GetAndResetCaretMoved());
359 EXPECT_EQ(start_offset + gfx::Vector2dF(10, 10), GetLastCaretPosition());
361 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
362 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
363 EXPECT_FALSE(GetAndResetCaretMoved());
365 // Once the drag is complete, no more touch events should be consumed until
366 // the next ACTION_DOWN.
367 EXPECT_FALSE(controller().WillHandleTouchEvent(event));
370 TEST_F(TouchSelectionControllerTest, InsertionTapped) {
371 base::TimeTicks event_time = base::TimeTicks::Now();
372 controller().OnTapEvent();
373 controller().OnSelectionEditable(true);
374 SetDraggingEnabled(true);
376 gfx::RectF start_rect(10, 0, 0, 10);
377 bool visible = true;
378 ChangeInsertion(start_rect, visible);
379 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
381 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
382 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
383 //TODO(AKV): this test case has to be modified once crbug.com/394093 is fixed.
384 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType());
386 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
387 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
388 EXPECT_EQ(INSERTION_TAPPED, GetLastEventType());
390 // Reset the insertion.
391 ClearInsertion();
392 controller().OnTapEvent();
393 ChangeInsertion(start_rect, visible);
394 ASSERT_EQ(INSERTION_SHOWN, GetLastEventType());
396 // No tap should be signalled if the time between DOWN and UP was too long.
397 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
398 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
399 event = MockMotionEvent(MockMotionEvent::ACTION_UP,
400 event_time + base::TimeDelta::FromSeconds(1),
403 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
404 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType());
406 // Reset the insertion.
407 ClearInsertion();
408 controller().OnTapEvent();
409 ChangeInsertion(start_rect, visible);
410 ASSERT_EQ(INSERTION_SHOWN, GetLastEventType());
412 // No tap should be signalled if the drag was too long.
413 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
414 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
415 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 100, 0);
416 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
417 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 100, 0);
418 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
419 EXPECT_EQ(INSERTION_DRAG_STARTED, 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 touch sequence is cancelled.
428 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
429 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
430 event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0);
431 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
432 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType());
435 TEST_F(TouchSelectionControllerTest, InsertionNotResetByRepeatedTapOrPress) {
436 base::TimeTicks event_time = base::TimeTicks::Now();
437 controller().OnTapEvent();
438 controller().OnSelectionEditable(true);
439 SetDraggingEnabled(true);
441 gfx::RectF anchor_rect(10, 0, 0, 10);
442 bool visible = true;
443 ChangeInsertion(anchor_rect, visible);
444 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
445 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor());
447 // Tapping again shouldn't reset the active insertion point.
448 controller().OnTapEvent();
449 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
450 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
451 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType());
452 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor());
454 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
455 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
456 EXPECT_EQ(INSERTION_TAPPED, GetLastEventType());
457 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor());
459 anchor_rect.Offset(5, 15);
460 ChangeInsertion(anchor_rect, visible);
461 EXPECT_EQ(INSERTION_MOVED, GetLastEventType());
462 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor());
464 // Pressing shouldn't reset the active insertion point.
465 controller().OnLongPressEvent();
466 controller().OnSelectionEmpty(true);
467 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
468 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
469 EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType());
470 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor());
472 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
473 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
474 EXPECT_EQ(INSERTION_TAPPED, GetLastEventType());
475 EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor());
478 TEST_F(TouchSelectionControllerTest, SelectionBasic) {
479 gfx::RectF start_rect(5, 5, 0, 10);
480 gfx::RectF end_rect(50, 5, 0, 10);
481 bool visible = true;
483 // Selection events are ignored until automatic showing is enabled.
484 ChangeSelection(start_rect, visible, end_rect, visible);
485 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
487 controller().OnLongPressEvent();
488 ChangeSelection(start_rect, visible, end_rect, visible);
489 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
490 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
492 start_rect.Offset(1, 0);
493 ChangeSelection(start_rect, visible, end_rect, visible);
494 // Selection movement does not currently trigger a separate event.
495 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
497 ClearSelection();
498 EXPECT_EQ(SELECTION_CLEARED, GetLastEventType());
499 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
502 TEST_F(TouchSelectionControllerTest, SelectionRepeatedLongPress) {
503 gfx::RectF start_rect(5, 5, 0, 10);
504 gfx::RectF end_rect(50, 5, 0, 10);
505 bool visible = true;
507 controller().OnLongPressEvent();
508 ChangeSelection(start_rect, visible, end_rect, visible);
509 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
510 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
512 // A long press triggering a new selection should re-send the SELECTION_SHOWN
513 // event notification.
514 start_rect.Offset(10, 10);
515 controller().OnLongPressEvent();
516 ChangeSelection(start_rect, visible, end_rect, visible);
517 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
518 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
521 TEST_F(TouchSelectionControllerTest, SelectionDragged) {
522 base::TimeTicks event_time = base::TimeTicks::Now();
523 controller().OnLongPressEvent();
525 // The touch sequence should not be handled if selection is not active.
526 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
527 EXPECT_FALSE(controller().WillHandleTouchEvent(event));
529 float line_height = 10.f;
530 gfx::RectF start_rect(0, 0, 0, line_height);
531 gfx::RectF end_rect(50, 0, 0, line_height);
532 bool visible = true;
533 ChangeSelection(start_rect, visible, end_rect, visible);
534 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
535 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
537 // The touch sequence should be handled only if the drawable reports a hit.
538 EXPECT_FALSE(controller().WillHandleTouchEvent(event));
539 SetDraggingEnabled(true);
540 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
541 EXPECT_FALSE(GetAndResetSelectionMoved());
543 // The SelectBetweenCoordinates() result should reflect the movement. Note
544 // that the start coordinate will always reflect the "fixed" handle's
545 // position, in this case the position from |end_rect|.
546 // Note that the reported position is offset from the center of the
547 // input rects (i.e., the middle of the corresponding text line).
548 gfx::PointF fixed_offset = end_rect.CenterPoint();
549 gfx::PointF start_offset = start_rect.CenterPoint();
550 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5);
551 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
552 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType());
553 EXPECT_TRUE(GetAndResetSelectionMoved());
554 EXPECT_EQ(fixed_offset, GetLastSelectionStart());
555 EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
557 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 5, 5);
558 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
559 EXPECT_TRUE(GetAndResetSelectionMoved());
560 EXPECT_EQ(fixed_offset, GetLastSelectionStart());
561 EXPECT_EQ(start_offset + gfx::Vector2dF(5, 5), GetLastSelectionEnd());
563 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 10, 5);
564 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
565 EXPECT_TRUE(GetAndResetSelectionMoved());
566 EXPECT_EQ(fixed_offset, GetLastSelectionStart());
567 EXPECT_EQ(start_offset + gfx::Vector2dF(10, 5), GetLastSelectionEnd());
569 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
570 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
571 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType());
572 EXPECT_FALSE(GetAndResetSelectionMoved());
574 // Once the drag is complete, no more touch events should be consumed until
575 // the next ACTION_DOWN.
576 EXPECT_FALSE(controller().WillHandleTouchEvent(event));
579 TEST_F(TouchSelectionControllerTest, SelectionDraggedWithOverlap) {
580 base::TimeTicks event_time = base::TimeTicks::Now();
581 controller().OnLongPressEvent();
583 float line_height = 10.f;
584 gfx::RectF start_rect(0, 0, 0, line_height);
585 gfx::RectF end_rect(50, 0, 0, line_height);
586 bool visible = true;
587 ChangeSelection(start_rect, visible, end_rect, visible);
588 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
589 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
591 // The ACTION_DOWN should lock to the closest handle.
592 gfx::PointF end_offset = end_rect.CenterPoint();
593 gfx::PointF fixed_offset = start_rect.CenterPoint();
594 float touch_down_x = (end_offset.x() + fixed_offset.x()) / 2 + 1.f;
595 MockMotionEvent event(
596 MockMotionEvent::ACTION_DOWN, event_time, touch_down_x, 0);
597 SetDraggingEnabled(true);
598 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
599 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType());
600 EXPECT_FALSE(GetAndResetSelectionMoved());
602 // Even though the ACTION_MOVE is over the start handle, it should continue
603 // targetting the end handle that consumed the ACTION_DOWN.
604 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 0);
605 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
606 EXPECT_TRUE(GetAndResetSelectionMoved());
607 EXPECT_EQ(fixed_offset, GetLastSelectionStart());
608 EXPECT_EQ(end_offset - gfx::Vector2dF(touch_down_x, 0),
609 GetLastSelectionEnd());
611 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
612 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
613 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType());
614 EXPECT_FALSE(GetAndResetSelectionMoved());
617 TEST_F(TouchSelectionControllerTest, SelectionDraggedToSwitchBaseAndExtent) {
618 base::TimeTicks event_time = base::TimeTicks::Now();
619 controller().OnLongPressEvent();
621 float line_height = 10.f;
622 gfx::RectF start_rect(50, line_height, 0, line_height);
623 gfx::RectF end_rect(100, line_height, 0, line_height);
624 bool visible = true;
625 ChangeSelection(start_rect, visible, end_rect, visible);
626 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
627 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
629 SetDraggingEnabled(true);
631 // Move the extent, not triggering a swap of points.
632 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time,
633 end_rect.x(), end_rect.bottom());
634 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
635 EXPECT_FALSE(GetAndResetSelectionMoved());
636 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
638 gfx::PointF base_offset = start_rect.CenterPoint();
639 gfx::PointF extent_offset = end_rect.CenterPoint();
640 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
641 end_rect.x(), end_rect.bottom() + 5);
642 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
643 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType());
644 EXPECT_TRUE(GetAndResetSelectionMoved());
645 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
646 EXPECT_EQ(base_offset, GetLastSelectionStart());
647 EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
649 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
650 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
651 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType());
652 EXPECT_FALSE(GetAndResetSelectionMoved());
654 end_rect += gfx::Vector2dF(0, 5);
655 ChangeSelection(start_rect, visible, end_rect, visible);
657 // Move the base, triggering a swap of points.
658 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
659 start_rect.x(), start_rect.bottom());
660 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
661 EXPECT_FALSE(GetAndResetSelectionMoved());
662 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
664 base_offset = end_rect.CenterPoint();
665 extent_offset = start_rect.CenterPoint();
666 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
667 start_rect.x(), start_rect.bottom() + 5);
668 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
669 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType());
670 EXPECT_TRUE(GetAndResetSelectionMoved());
671 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
672 EXPECT_EQ(base_offset, GetLastSelectionStart());
673 EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
675 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
676 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
677 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType());
678 EXPECT_FALSE(GetAndResetSelectionMoved());
680 start_rect += gfx::Vector2dF(0, 5);
681 ChangeSelection(start_rect, visible, end_rect, visible);
683 // Move the same point again, not triggering a swap of points.
684 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
685 start_rect.x(), start_rect.bottom());
686 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
687 EXPECT_FALSE(GetAndResetSelectionMoved());
688 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
690 base_offset = end_rect.CenterPoint();
691 extent_offset = start_rect.CenterPoint();
692 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
693 start_rect.x(), start_rect.bottom() + 5);
694 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
695 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType());
696 EXPECT_TRUE(GetAndResetSelectionMoved());
697 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
698 EXPECT_EQ(base_offset, GetLastSelectionStart());
699 EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
701 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
702 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
703 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType());
704 EXPECT_FALSE(GetAndResetSelectionMoved());
706 start_rect += gfx::Vector2dF(0, 5);
707 ChangeSelection(start_rect, visible, end_rect, visible);
709 // Move the base, triggering a swap of points.
710 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
711 end_rect.x(), end_rect.bottom());
712 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
713 EXPECT_FALSE(GetAndResetSelectionMoved());
714 EXPECT_TRUE(GetAndResetSelectionPointsSwapped());
716 base_offset = start_rect.CenterPoint();
717 extent_offset = end_rect.CenterPoint();
718 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
719 end_rect.x(), end_rect.bottom() + 5);
720 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
721 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType());
722 EXPECT_TRUE(GetAndResetSelectionMoved());
723 EXPECT_FALSE(GetAndResetSelectionPointsSwapped());
724 EXPECT_EQ(base_offset, GetLastSelectionStart());
725 EXPECT_EQ(extent_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd());
727 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5);
728 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
729 EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType());
730 EXPECT_FALSE(GetAndResetSelectionMoved());
733 TEST_F(TouchSelectionControllerTest, SelectionDragExtremeLineSize) {
734 base::TimeTicks event_time = base::TimeTicks::Now();
735 controller().OnLongPressEvent();
737 float small_line_height = 1.f;
738 float large_line_height = 50.f;
739 gfx::RectF small_line_rect(0, 0, 0, small_line_height);
740 gfx::RectF large_line_rect(50, 50, 0, large_line_height);
741 bool visible = true;
742 ChangeSelection(small_line_rect, visible, large_line_rect, visible);
743 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
744 EXPECT_EQ(small_line_rect.bottom_left(), GetLastEventAnchor());
746 // Start dragging the handle on the small line.
747 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time,
748 small_line_rect.x(), small_line_rect.y());
749 SetDraggingEnabled(true);
750 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
751 EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType());
752 // The drag coordinate for large lines should be capped to a reasonable
753 // offset, allowing seamless transition to neighboring lines with different
754 // sizes. The drag coordinate for small lines should have an offset
755 // commensurate with the small line size.
756 EXPECT_EQ(large_line_rect.bottom_left() - gfx::Vector2dF(0, 5.f),
757 GetLastSelectionStart());
758 EXPECT_EQ(small_line_rect.CenterPoint(), GetLastSelectionEnd());
760 small_line_rect += gfx::Vector2dF(25.f, 0);
761 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time,
762 small_line_rect.x(), small_line_rect.y());
763 EXPECT_TRUE(controller().WillHandleTouchEvent(event));
764 EXPECT_TRUE(GetAndResetSelectionMoved());
765 EXPECT_EQ(small_line_rect.CenterPoint(), GetLastSelectionEnd());
768 TEST_F(TouchSelectionControllerTest, Animation) {
769 controller().OnTapEvent();
770 controller().OnSelectionEditable(true);
772 gfx::RectF insertion_rect(5, 5, 0, 10);
774 bool visible = true;
775 ChangeInsertion(insertion_rect, visible);
776 EXPECT_FALSE(GetAndResetNeedsAnimate());
778 visible = false;
779 ChangeInsertion(insertion_rect, visible);
780 EXPECT_TRUE(GetAndResetNeedsAnimate());
782 visible = true;
783 ChangeInsertion(insertion_rect, visible);
784 EXPECT_TRUE(GetAndResetNeedsAnimate());
786 // If the handles are explicity hidden, no animation should be triggered.
787 controller().HideAndDisallowShowingAutomatically();
788 EXPECT_FALSE(GetAndResetNeedsAnimate());
790 // If the client doesn't support animation, no animation should be triggered.
791 SetAnimationEnabled(false);
792 controller().OnTapEvent();
793 visible = true;
794 ChangeInsertion(insertion_rect, visible);
795 EXPECT_FALSE(GetAndResetNeedsAnimate());
798 TEST_F(TouchSelectionControllerTest, TemporarilyHidden) {
799 controller().OnTapEvent();
800 controller().OnSelectionEditable(true);
802 gfx::RectF insertion_rect(5, 5, 0, 10);
804 bool visible = true;
805 ChangeInsertion(insertion_rect, visible);
806 EXPECT_FALSE(GetAndResetNeedsAnimate());
808 controller().SetTemporarilyHidden(true);
809 EXPECT_TRUE(GetAndResetNeedsAnimate());
811 visible = false;
812 ChangeInsertion(insertion_rect, visible);
813 EXPECT_FALSE(GetAndResetNeedsAnimate());
815 visible = true;
816 ChangeInsertion(insertion_rect, visible);
817 EXPECT_FALSE(GetAndResetNeedsAnimate());
819 controller().SetTemporarilyHidden(false);
820 EXPECT_TRUE(GetAndResetNeedsAnimate());
823 TEST_F(TouchSelectionControllerTest, SelectionClearOnTap) {
824 gfx::RectF start_rect(5, 5, 0, 10);
825 gfx::RectF end_rect(50, 5, 0, 10);
826 bool visible = true;
828 controller().OnLongPressEvent();
829 ChangeSelection(start_rect, visible, end_rect, visible);
831 // Selection should not be cleared if the selection bounds have not changed.
832 controller().OnTapEvent();
833 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
834 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
836 controller().OnTapEvent();
837 ClearSelection();
838 EXPECT_EQ(SELECTION_CLEARED, GetLastEventType());
839 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
842 TEST_F(TouchSelectionControllerTest, AllowShowingFromCurrentSelection) {
843 gfx::RectF start_rect(5, 5, 0, 10);
844 gfx::RectF end_rect(50, 5, 0, 10);
845 bool visible = true;
847 // The selection should not have be activated, as it wasn't yet allowed.
848 ChangeSelection(start_rect, visible, end_rect, visible);
849 EXPECT_EQ(gfx::PointF(), GetLastEventAnchor());
851 // Now explicitly allow showing from the previously supplied bounds.
852 controller().AllowShowingFromCurrentSelection();
853 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
854 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
856 // Repeated calls to show from the current selection should be ignored.
857 controller().AllowShowingFromCurrentSelection();
858 EXPECT_EQ(SELECTION_SHOWN, GetLastEventType());
859 EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor());
861 // Trying to show from an empty selection will have no result.
862 ClearSelection();
863 EXPECT_EQ(SELECTION_CLEARED, GetLastEventType());
864 controller().AllowShowingFromCurrentSelection();
865 EXPECT_EQ(SELECTION_CLEARED, GetLastEventType());
867 // Showing the insertion handle should also be supported.
868 controller().OnSelectionEditable(true);
869 controller().OnSelectionEmpty(false);
870 controller().HideAndDisallowShowingAutomatically();
871 gfx::RectF insertion_rect(5, 5, 0, 10);
872 ChangeInsertion(insertion_rect, visible);
873 controller().AllowShowingFromCurrentSelection();
874 EXPECT_EQ(INSERTION_SHOWN, GetLastEventType());
875 EXPECT_EQ(insertion_rect.bottom_left(), GetLastEventAnchor());
878 } // namespace ui