MD Downloads: prevent search text from overlapping with the cancel search (X)
[chromium-blink-merge.git] / ui / touch_selection / touch_handle_unittest.cc
blob6298b3a9b4d2a4ef7273cbb88116b2d8639e1508
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_handle.h"
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "ui/events/test/motion_event_test_utils.h"
9 #include "ui/gfx/geometry/rect_f.h"
10 #include "ui/touch_selection/touch_handle_orientation.h"
12 using ui::test::MockMotionEvent;
14 namespace ui {
15 namespace {
17 const int kDefaultTapDurationMs = 200;
18 const double kDefaultTapSlop = 10.;
19 const float kDefaultDrawableSize = 10.f;
21 struct MockDrawableData {
22 MockDrawableData()
23 : orientation(TouchHandleOrientation::UNDEFINED),
24 alpha(0.f),
25 enabled(false),
26 visible(false),
27 rect(0, 0, kDefaultDrawableSize, kDefaultDrawableSize) {}
28 TouchHandleOrientation orientation;
29 float alpha;
30 bool enabled;
31 bool visible;
32 gfx::RectF rect;
35 class MockTouchHandleDrawable : public TouchHandleDrawable {
36 public:
37 explicit MockTouchHandleDrawable(MockDrawableData* data) : data_(data) {}
38 ~MockTouchHandleDrawable() override {}
40 void SetEnabled(bool enabled) override { data_->enabled = enabled; }
42 void SetOrientation(TouchHandleOrientation orientation) override {
43 data_->orientation = orientation;
46 void SetAlpha(float alpha) override {
47 data_->alpha = alpha;
48 data_->visible = alpha > 0;
51 void SetFocus(const gfx::PointF& position) override {
52 // Anchor focus to the top left of the rect (regardless of orientation).
53 data_->rect.set_origin(position);
56 gfx::RectF GetVisibleBounds() const override {
57 return data_->rect;
60 private:
61 MockDrawableData* data_;
64 } // namespace
66 class TouchHandleTest : public testing::Test, public TouchHandleClient {
67 public:
68 TouchHandleTest()
69 : dragging_(false),
70 dragged_(false),
71 tapped_(false),
72 needs_animate_(false) {}
74 ~TouchHandleTest() override {}
76 // TouchHandleClient implementation.
77 void OnDragBegin(const TouchSelectionDraggable& handler,
78 const gfx::PointF& drag_position) override {
79 dragging_ = true;
82 void OnDragUpdate(const TouchSelectionDraggable& handler,
83 const gfx::PointF& drag_position) override {
84 dragged_ = true;
85 drag_position_ = drag_position;
88 void OnDragEnd(const TouchSelectionDraggable& handler) override {
89 dragging_ = false;
92 bool IsWithinTapSlop(const gfx::Vector2dF& delta) const override {
93 return delta.LengthSquared() < (kDefaultTapSlop * kDefaultTapSlop);
96 void OnHandleTapped(const TouchHandle& handle) override { tapped_ = true; }
98 void SetNeedsAnimate() override { needs_animate_ = true; }
100 scoped_ptr<TouchHandleDrawable> CreateDrawable() override {
101 return make_scoped_ptr(new MockTouchHandleDrawable(&drawable_data_));
104 base::TimeDelta GetMaxTapDuration() const override {
105 return base::TimeDelta::FromMilliseconds(kDefaultTapDurationMs);
108 void Animate(TouchHandle& handle) {
109 needs_animate_ = false;
110 base::TimeTicks now = base::TimeTicks::Now();
111 while (handle.Animate(now))
112 now += base::TimeDelta::FromMilliseconds(16);
115 bool GetAndResetHandleDragged() {
116 bool dragged = dragged_;
117 dragged_ = false;
118 return dragged;
121 bool GetAndResetHandleTapped() {
122 bool tapped = tapped_;
123 tapped_ = false;
124 return tapped;
127 bool GetAndResetNeedsAnimate() {
128 bool needs_animate = needs_animate_;
129 needs_animate_ = false;
130 return needs_animate;
133 bool IsDragging() const { return dragging_; }
134 const gfx::PointF& DragPosition() const { return drag_position_; }
135 bool NeedsAnimate() const { return needs_animate_; }
137 const MockDrawableData& drawable() { return drawable_data_; }
139 private:
140 gfx::PointF drag_position_;
141 bool dragging_;
142 bool dragged_;
143 bool tapped_;
144 bool needs_animate_;
146 MockDrawableData drawable_data_;
149 TEST_F(TouchHandleTest, Visibility) {
150 TouchHandle handle(this, TouchHandleOrientation::CENTER);
151 EXPECT_FALSE(drawable().visible);
153 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
154 EXPECT_TRUE(drawable().visible);
155 EXPECT_EQ(1.f, drawable().alpha);
157 handle.SetVisible(false, TouchHandle::ANIMATION_NONE);
158 EXPECT_FALSE(drawable().visible);
160 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
161 EXPECT_TRUE(drawable().visible);
162 EXPECT_EQ(1.f, drawable().alpha);
165 TEST_F(TouchHandleTest, VisibilityAnimation) {
166 TouchHandle handle(this, TouchHandleOrientation::CENTER);
167 ASSERT_FALSE(NeedsAnimate());
168 ASSERT_FALSE(drawable().visible);
169 ASSERT_EQ(0.f, drawable().alpha);
171 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
172 EXPECT_TRUE(NeedsAnimate());
173 EXPECT_FALSE(drawable().visible);
174 EXPECT_EQ(0.f, drawable().alpha);
176 Animate(handle);
177 EXPECT_TRUE(drawable().visible);
178 EXPECT_EQ(1.f, drawable().alpha);
180 ASSERT_FALSE(NeedsAnimate());
181 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
182 EXPECT_TRUE(NeedsAnimate());
183 EXPECT_TRUE(drawable().visible);
184 EXPECT_EQ(1.f, drawable().alpha);
186 Animate(handle);
187 EXPECT_FALSE(drawable().visible);
188 EXPECT_EQ(0.f, drawable().alpha);
190 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
191 EXPECT_EQ(1.f, drawable().alpha);
192 EXPECT_FALSE(GetAndResetNeedsAnimate());
193 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
194 EXPECT_EQ(1.f, drawable().alpha);
195 EXPECT_TRUE(GetAndResetNeedsAnimate());
196 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
197 EXPECT_EQ(1.f, drawable().alpha);
198 EXPECT_FALSE(GetAndResetNeedsAnimate());
201 TEST_F(TouchHandleTest, Orientation) {
202 TouchHandle handle(this, TouchHandleOrientation::CENTER);
203 EXPECT_EQ(TouchHandleOrientation::CENTER, drawable().orientation);
205 handle.SetOrientation(TouchHandleOrientation::LEFT);
206 EXPECT_EQ(TouchHandleOrientation::LEFT, drawable().orientation);
208 handle.SetOrientation(TouchHandleOrientation::RIGHT);
209 EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation);
211 handle.SetOrientation(TouchHandleOrientation::CENTER);
212 EXPECT_EQ(TouchHandleOrientation::CENTER, drawable().orientation);
215 TEST_F(TouchHandleTest, Position) {
216 TouchHandle handle(this, TouchHandleOrientation::CENTER);
217 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
219 gfx::PointF position;
220 EXPECT_EQ(gfx::PointF(), drawable().rect.origin());
222 position = gfx::PointF(7.3f, -3.7f);
223 handle.SetPosition(position);
224 EXPECT_EQ(position, drawable().rect.origin());
226 position = gfx::PointF(-7.3f, 3.7f);
227 handle.SetPosition(position);
228 EXPECT_EQ(position, drawable().rect.origin());
231 TEST_F(TouchHandleTest, PositionNotUpdatedWhileFadingOrInvisible) {
232 TouchHandle handle(this, TouchHandleOrientation::CENTER);
234 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
235 ASSERT_TRUE(drawable().visible);
236 ASSERT_FALSE(NeedsAnimate());
238 gfx::PointF old_position(7.3f, -3.7f);
239 handle.SetPosition(old_position);
240 ASSERT_EQ(old_position, drawable().rect.origin());
242 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
243 ASSERT_TRUE(NeedsAnimate());
245 gfx::PointF new_position(3.7f, -3.7f);
246 handle.SetPosition(new_position);
247 EXPECT_EQ(old_position, drawable().rect.origin());
248 EXPECT_TRUE(NeedsAnimate());
250 // While the handle is fading, the new position should not take affect.
251 base::TimeTicks now = base::TimeTicks::Now();
252 while (handle.Animate(now)) {
253 EXPECT_EQ(old_position, drawable().rect.origin());
254 now += base::TimeDelta::FromMilliseconds(16);
257 // Even after the animation terminates, the new position will not be pushed.
258 EXPECT_EQ(old_position, drawable().rect.origin());
260 // As soon as the handle becomes visible, the new position will be pushed.
261 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
262 EXPECT_EQ(new_position, drawable().rect.origin());
265 TEST_F(TouchHandleTest, Enabled) {
266 // A newly created handle defaults to enabled.
267 TouchHandle handle(this, TouchHandleOrientation::CENTER);
268 EXPECT_TRUE(drawable().enabled);
270 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
271 EXPECT_TRUE(GetAndResetNeedsAnimate());
272 EXPECT_EQ(0.f, drawable().alpha);
273 handle.SetEnabled(false);
274 EXPECT_FALSE(drawable().enabled);
276 // Dragging should not be allowed while the handle is disabled.
277 base::TimeTicks event_time = base::TimeTicks::Now();
278 const float kOffset = kDefaultDrawableSize / 2.f;
279 MockMotionEvent event(
280 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
281 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
283 // Disabling mid-animation should cancel the animation.
284 handle.SetEnabled(true);
285 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
286 EXPECT_TRUE(drawable().visible);
287 EXPECT_TRUE(GetAndResetNeedsAnimate());
288 handle.SetEnabled(false);
289 EXPECT_FALSE(drawable().enabled);
290 EXPECT_FALSE(drawable().visible);
291 EXPECT_FALSE(handle.Animate(base::TimeTicks::Now()));
293 // Disabling mid-drag should cancel the drag.
294 handle.SetEnabled(true);
295 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
296 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
297 EXPECT_TRUE(IsDragging());
298 handle.SetEnabled(false);
299 EXPECT_FALSE(IsDragging());
300 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
303 TEST_F(TouchHandleTest, Drag) {
304 TouchHandle handle(this, TouchHandleOrientation::CENTER);
306 base::TimeTicks event_time = base::TimeTicks::Now();
307 const float kOffset = kDefaultDrawableSize / 2.f;
309 // The handle must be visible to trigger drag.
310 MockMotionEvent event(
311 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
312 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
313 EXPECT_FALSE(IsDragging());
314 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
316 // ACTION_DOWN must fall within the drawable region to trigger drag.
317 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 50, 50);
318 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
319 EXPECT_FALSE(IsDragging());
321 // Only ACTION_DOWN will trigger drag.
322 event = MockMotionEvent(
323 MockMotionEvent::ACTION_MOVE, event_time, kOffset, kOffset);
324 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
325 EXPECT_FALSE(IsDragging());
327 // Start the drag.
328 event = MockMotionEvent(
329 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
330 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
331 EXPECT_TRUE(IsDragging());
333 event = MockMotionEvent(
334 MockMotionEvent::ACTION_MOVE, event_time, kOffset + 10, kOffset + 15);
335 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
336 EXPECT_TRUE(GetAndResetHandleDragged());
337 EXPECT_TRUE(IsDragging());
338 EXPECT_EQ(gfx::PointF(10, 15), DragPosition());
340 event = MockMotionEvent(
341 MockMotionEvent::ACTION_MOVE, event_time, kOffset - 10, kOffset - 15);
342 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
343 EXPECT_TRUE(GetAndResetHandleDragged());
344 EXPECT_TRUE(IsDragging());
345 EXPECT_EQ(gfx::PointF(-10, -15), DragPosition());
347 event = MockMotionEvent(MockMotionEvent::ACTION_UP);
348 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
349 EXPECT_FALSE(GetAndResetHandleDragged());
350 EXPECT_FALSE(IsDragging());
352 // Non-ACTION_DOWN events after the drag has terminated should not be handled.
353 event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL);
354 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
357 TEST_F(TouchHandleTest, DragDefersOrientationChange) {
358 TouchHandle handle(this, TouchHandleOrientation::RIGHT);
359 ASSERT_EQ(drawable().orientation, TouchHandleOrientation::RIGHT);
360 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
362 MockMotionEvent event(MockMotionEvent::ACTION_DOWN);
363 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
364 EXPECT_TRUE(IsDragging());
366 // Orientation changes will be deferred until the drag ends.
367 handle.SetOrientation(TouchHandleOrientation::LEFT);
368 EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation);
370 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE);
371 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
372 EXPECT_TRUE(GetAndResetHandleDragged());
373 EXPECT_TRUE(IsDragging());
374 EXPECT_EQ(TouchHandleOrientation::RIGHT, drawable().orientation);
376 event = MockMotionEvent(MockMotionEvent::ACTION_UP);
377 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
378 EXPECT_FALSE(GetAndResetHandleDragged());
379 EXPECT_FALSE(IsDragging());
380 EXPECT_EQ(TouchHandleOrientation::LEFT, drawable().orientation);
383 TEST_F(TouchHandleTest, DragDefersFade) {
384 TouchHandle handle(this, TouchHandleOrientation::CENTER);
385 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
387 MockMotionEvent event(MockMotionEvent::ACTION_DOWN);
388 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
389 EXPECT_TRUE(IsDragging());
391 // Fade will be deferred until the drag ends.
392 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
393 EXPECT_FALSE(NeedsAnimate());
394 EXPECT_TRUE(drawable().visible);
395 EXPECT_EQ(1.f, drawable().alpha);
397 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE);
398 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
399 EXPECT_FALSE(NeedsAnimate());
400 EXPECT_TRUE(drawable().visible);
402 event = MockMotionEvent(MockMotionEvent::ACTION_UP);
403 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
404 EXPECT_FALSE(IsDragging());
405 EXPECT_TRUE(NeedsAnimate());
407 Animate(handle);
408 EXPECT_FALSE(drawable().visible);
409 EXPECT_EQ(0.f, drawable().alpha);
412 TEST_F(TouchHandleTest, DragTargettingUsesTouchSize) {
413 TouchHandle handle(this, TouchHandleOrientation::CENTER);
414 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
416 base::TimeTicks event_time = base::TimeTicks::Now();
417 const float kTouchSize = 24.f;
418 const float kOffset = kDefaultDrawableSize + kTouchSize / 2.001f;
420 MockMotionEvent event(
421 MockMotionEvent::ACTION_DOWN, event_time, kOffset, 0);
422 event.SetTouchMajor(0.f);
423 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
424 EXPECT_FALSE(IsDragging());
426 event.SetTouchMajor(kTouchSize / 2.f);
427 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
428 EXPECT_FALSE(IsDragging());
430 event.SetTouchMajor(kTouchSize);
431 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
432 EXPECT_TRUE(IsDragging());
434 event.SetTouchMajor(kTouchSize * 2.f);
435 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
436 EXPECT_TRUE(IsDragging());
438 // The touch hit test region should be circular.
439 event = MockMotionEvent(
440 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
441 event.SetTouchMajor(kTouchSize);
442 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
443 EXPECT_FALSE(IsDragging());
445 event.SetTouchMajor(kTouchSize * std::sqrt(2.f) - 0.1f);
446 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
447 EXPECT_FALSE(IsDragging());
449 event.SetTouchMajor(kTouchSize * std::sqrt(2.f) + 0.1f);
450 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
451 EXPECT_TRUE(IsDragging());
453 // Ensure a touch size of 0 can still register a hit.
454 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN,
455 event_time,
456 kDefaultDrawableSize / 2.f,
457 kDefaultDrawableSize / 2.f);
458 event.SetTouchMajor(0);
459 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
460 EXPECT_TRUE(IsDragging());
462 // Touches centered above the handle region should never register a hit, even
463 // if the touch region intersects the handle region.
464 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time,
465 kDefaultDrawableSize / 2.f, -kTouchSize / 3.f);
466 event.SetTouchMajor(kTouchSize);
467 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
468 EXPECT_FALSE(IsDragging());
471 TEST_F(TouchHandleTest, Tap) {
472 TouchHandle handle(this, TouchHandleOrientation::CENTER);
473 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
475 base::TimeTicks event_time = base::TimeTicks::Now();
477 // ACTION_CANCEL shouldn't trigger a tap.
478 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
479 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
480 event_time += base::TimeDelta::FromMilliseconds(50);
481 event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0);
482 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
483 EXPECT_FALSE(GetAndResetHandleTapped());
485 // Long press shouldn't trigger a tap.
486 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
487 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
488 event_time += 2 * GetMaxTapDuration();
489 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
490 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
491 EXPECT_FALSE(GetAndResetHandleTapped());
493 // Only a brief tap within the slop region should trigger a tap.
494 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
495 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
496 event_time += GetMaxTapDuration() / 2;
497 event = MockMotionEvent(
498 MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop / 2.f, 0);
499 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
500 event = MockMotionEvent(
501 MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop / 2.f, 0);
502 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
503 EXPECT_TRUE(GetAndResetHandleTapped());
505 // Moving beyond the slop region shouldn't trigger a tap.
506 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
507 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
508 event_time += GetMaxTapDuration() / 2;
509 event = MockMotionEvent(
510 MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop * 2.f, 0);
511 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
512 event = MockMotionEvent(
513 MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop * 2.f, 0);
514 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
515 EXPECT_FALSE(GetAndResetHandleTapped());
518 } // namespace ui