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 "content/browser/renderer_host/input/touch_handle.h"
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "ui/events/test/mock_motion_event.h"
9 #include "ui/gfx/geometry/rect_f.h"
11 using ui::test::MockMotionEvent;
16 const int kDefaultTapTimeoutMs = 200;
17 const float kDefaultTapSlop = 10.f;
18 const float kDefaultDrawableSize = 10.f;
20 struct MockDrawableData {
22 : orientation(TOUCH_HANDLE_ORIENTATION_UNDEFINED),
26 rect(0, 0, kDefaultDrawableSize, kDefaultDrawableSize) {}
27 TouchHandleOrientation orientation;
34 class MockTouchHandleDrawable : public TouchHandleDrawable {
36 explicit MockTouchHandleDrawable(MockDrawableData* data) : data_(data) {}
37 virtual ~MockTouchHandleDrawable() {}
39 virtual void SetEnabled(bool enabled) OVERRIDE { data_->enabled = enabled; }
41 virtual void SetOrientation(TouchHandleOrientation orientation) OVERRIDE {
42 data_->orientation = orientation;
45 virtual void SetAlpha(float alpha) OVERRIDE { data_->alpha = alpha; }
47 virtual void SetFocus(const gfx::PointF& position) OVERRIDE {
48 // Anchor focus to the top left of the rect (regardless of orientation).
49 data_->rect.set_origin(position);
52 virtual void SetVisible(bool visible) OVERRIDE { data_->visible = visible; }
54 virtual bool IntersectsWith(const gfx::RectF& rect) const OVERRIDE {
55 return data_->rect.Intersects(rect);
59 MockDrawableData* data_;
64 class TouchHandleTest : public testing::Test, public TouchHandleClient {
70 needs_animate_(false) {}
72 virtual ~TouchHandleTest() {}
74 // TouchHandleClient implementation.
75 virtual void OnHandleDragBegin(const TouchHandle& handle) OVERRIDE {
79 virtual void OnHandleDragUpdate(const TouchHandle& handle,
80 const gfx::PointF& new_position) OVERRIDE {
82 drag_position_ = new_position;
85 virtual void OnHandleDragEnd(const TouchHandle& handle) OVERRIDE {
89 virtual void OnHandleTapped(const TouchHandle& handle) OVERRIDE {
93 virtual void SetNeedsAnimate() OVERRIDE { needs_animate_ = true; }
95 virtual scoped_ptr<TouchHandleDrawable> CreateDrawable() OVERRIDE {
96 return scoped_ptr<TouchHandleDrawable>(
97 new MockTouchHandleDrawable(&drawable_data_));
100 virtual base::TimeDelta GetTapTimeout() const OVERRIDE {
101 return base::TimeDelta::FromMilliseconds(kDefaultTapTimeoutMs);
104 virtual float GetTapSlop() const OVERRIDE { return kDefaultTapSlop; }
106 void Animate(TouchHandle& handle) {
107 needs_animate_ = false;
108 base::TimeTicks now = base::TimeTicks::Now();
109 while (handle.Animate(now))
110 now += base::TimeDelta::FromMilliseconds(16);
113 bool GetAndResetHandleDragged() {
114 bool dragged = dragged_;
119 bool GetAndResetHandleTapped() {
120 bool tapped = tapped_;
125 bool GetAndResetNeedsAnimate() {
126 bool needs_animate = needs_animate_;
127 needs_animate_ = false;
128 return needs_animate;
131 bool IsDragging() const { return dragging_; }
132 const gfx::PointF& DragPosition() const { return drag_position_; }
133 bool NeedsAnimate() const { return needs_animate_; }
135 const MockDrawableData& drawable() { return drawable_data_; }
138 gfx::PointF drag_position_;
144 MockDrawableData drawable_data_;
147 TEST_F(TouchHandleTest, Visibility) {
148 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
149 EXPECT_FALSE(drawable().visible);
151 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
152 EXPECT_TRUE(drawable().visible);
153 EXPECT_EQ(1.f, drawable().alpha);
155 handle.SetVisible(false, TouchHandle::ANIMATION_NONE);
156 EXPECT_FALSE(drawable().visible);
158 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
159 EXPECT_TRUE(drawable().visible);
160 EXPECT_EQ(1.f, drawable().alpha);
163 TEST_F(TouchHandleTest, VisibilityAnimation) {
164 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
165 ASSERT_FALSE(NeedsAnimate());
166 ASSERT_FALSE(drawable().visible);
167 ASSERT_EQ(0.f, drawable().alpha);
169 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
170 EXPECT_TRUE(NeedsAnimate());
171 EXPECT_TRUE(drawable().visible);
172 EXPECT_EQ(0.f, drawable().alpha);
175 EXPECT_TRUE(drawable().visible);
176 EXPECT_EQ(1.f, drawable().alpha);
178 ASSERT_FALSE(NeedsAnimate());
179 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
180 EXPECT_TRUE(NeedsAnimate());
181 EXPECT_TRUE(drawable().visible);
182 EXPECT_EQ(1.f, drawable().alpha);
185 EXPECT_FALSE(drawable().visible);
186 EXPECT_EQ(0.f, drawable().alpha);
188 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
189 EXPECT_EQ(1.f, drawable().alpha);
190 EXPECT_FALSE(GetAndResetNeedsAnimate());
191 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
192 EXPECT_EQ(1.f, drawable().alpha);
193 EXPECT_TRUE(GetAndResetNeedsAnimate());
194 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
195 EXPECT_EQ(1.f, drawable().alpha);
196 EXPECT_FALSE(GetAndResetNeedsAnimate());
199 TEST_F(TouchHandleTest, Orientation) {
200 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
201 EXPECT_EQ(TOUCH_HANDLE_CENTER, drawable().orientation);
203 handle.SetOrientation(TOUCH_HANDLE_LEFT);
204 EXPECT_EQ(TOUCH_HANDLE_LEFT, drawable().orientation);
206 handle.SetOrientation(TOUCH_HANDLE_RIGHT);
207 EXPECT_EQ(TOUCH_HANDLE_RIGHT, drawable().orientation);
209 handle.SetOrientation(TOUCH_HANDLE_CENTER);
210 EXPECT_EQ(TOUCH_HANDLE_CENTER, drawable().orientation);
213 TEST_F(TouchHandleTest, Position) {
214 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
215 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
217 gfx::PointF position;
218 EXPECT_EQ(gfx::PointF(), drawable().rect.origin());
220 position = gfx::PointF(7.3f, -3.7f);
221 handle.SetPosition(position);
222 EXPECT_EQ(position, drawable().rect.origin());
224 position = gfx::PointF(-7.3f, 3.7f);
225 handle.SetPosition(position);
226 EXPECT_EQ(position, drawable().rect.origin());
229 TEST_F(TouchHandleTest, PositionNotUpdatedWhileFadingOrInvisible) {
230 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
232 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
233 ASSERT_TRUE(drawable().visible);
234 ASSERT_FALSE(NeedsAnimate());
236 gfx::PointF old_position(7.3f, -3.7f);
237 handle.SetPosition(old_position);
238 ASSERT_EQ(old_position, drawable().rect.origin());
240 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
241 ASSERT_TRUE(NeedsAnimate());
243 gfx::PointF new_position(3.7f, -3.7f);
244 handle.SetPosition(new_position);
245 EXPECT_EQ(old_position, drawable().rect.origin());
246 EXPECT_TRUE(NeedsAnimate());
248 // While the handle is fading, the new position should not take affect.
249 base::TimeTicks now = base::TimeTicks::Now();
250 while (handle.Animate(now)) {
251 EXPECT_EQ(old_position, drawable().rect.origin());
252 now += base::TimeDelta::FromMilliseconds(16);
255 // Even after the animation terminates, the new position will not be pushed.
256 EXPECT_EQ(old_position, drawable().rect.origin());
258 // As soon as the handle becomes visible, the new position will be pushed.
259 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
260 EXPECT_EQ(new_position, drawable().rect.origin());
263 TEST_F(TouchHandleTest, Enabled) {
264 // A newly created handle defaults to enabled.
265 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
266 EXPECT_TRUE(drawable().enabled);
268 handle.SetVisible(true, TouchHandle::ANIMATION_SMOOTH);
269 EXPECT_TRUE(GetAndResetNeedsAnimate());
270 EXPECT_EQ(0.f, drawable().alpha);
271 handle.SetEnabled(false);
272 EXPECT_FALSE(drawable().enabled);
274 // Dragging should not be allowed while the handle is disabled.
275 base::TimeTicks event_time = base::TimeTicks::Now();
276 const float kOffset = kDefaultDrawableSize / 2.f;
277 MockMotionEvent event(
278 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
279 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
281 // Disabling mid-animation should cancel the animation.
282 handle.SetEnabled(true);
283 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
284 EXPECT_TRUE(drawable().visible);
285 EXPECT_TRUE(GetAndResetNeedsAnimate());
286 handle.SetEnabled(false);
287 EXPECT_FALSE(drawable().enabled);
288 EXPECT_FALSE(drawable().visible);
289 EXPECT_FALSE(handle.Animate(base::TimeTicks::Now()));
291 // Disabling mid-drag should cancel the drag.
292 handle.SetEnabled(true);
293 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
294 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
295 EXPECT_TRUE(IsDragging());
296 handle.SetEnabled(false);
297 EXPECT_FALSE(IsDragging());
298 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
301 TEST_F(TouchHandleTest, Drag) {
302 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
304 base::TimeTicks event_time = base::TimeTicks::Now();
305 const float kOffset = kDefaultDrawableSize / 2.f;
307 // The handle must be visible to trigger drag.
308 MockMotionEvent event(
309 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
310 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
311 EXPECT_FALSE(IsDragging());
312 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
314 // ACTION_DOWN must fall within the drawable region to trigger drag.
315 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 50, 50);
316 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
317 EXPECT_FALSE(IsDragging());
319 // Only ACTION_DOWN will trigger drag.
320 event = MockMotionEvent(
321 MockMotionEvent::ACTION_MOVE, event_time, kOffset, kOffset);
322 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
323 EXPECT_FALSE(IsDragging());
326 event = MockMotionEvent(
327 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
328 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
329 EXPECT_TRUE(IsDragging());
331 event = MockMotionEvent(
332 MockMotionEvent::ACTION_MOVE, event_time, kOffset + 10, kOffset + 15);
333 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
334 EXPECT_TRUE(GetAndResetHandleDragged());
335 EXPECT_TRUE(IsDragging());
336 EXPECT_EQ(gfx::PointF(10, 15), DragPosition());
338 event = MockMotionEvent(
339 MockMotionEvent::ACTION_MOVE, event_time, kOffset - 10, kOffset - 15);
340 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
341 EXPECT_TRUE(GetAndResetHandleDragged());
342 EXPECT_TRUE(IsDragging());
343 EXPECT_EQ(gfx::PointF(-10, -15), DragPosition());
345 event = MockMotionEvent(MockMotionEvent::ACTION_UP);
346 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
347 EXPECT_FALSE(GetAndResetHandleDragged());
348 EXPECT_FALSE(IsDragging());
350 // Non-ACTION_DOWN events after the drag has terminated should not be handled.
351 event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL);
352 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
355 TEST_F(TouchHandleTest, DragDefersOrientationChange) {
356 TouchHandle handle(this, TOUCH_HANDLE_RIGHT);
357 ASSERT_EQ(drawable().orientation, TOUCH_HANDLE_RIGHT);
358 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
360 MockMotionEvent event(MockMotionEvent::ACTION_DOWN);
361 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
362 EXPECT_TRUE(IsDragging());
364 // Orientation changes will be deferred until the drag ends.
365 handle.SetOrientation(TOUCH_HANDLE_LEFT);
366 EXPECT_EQ(TOUCH_HANDLE_RIGHT, drawable().orientation);
368 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE);
369 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
370 EXPECT_TRUE(GetAndResetHandleDragged());
371 EXPECT_TRUE(IsDragging());
372 EXPECT_EQ(TOUCH_HANDLE_RIGHT, drawable().orientation);
374 event = MockMotionEvent(MockMotionEvent::ACTION_UP);
375 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
376 EXPECT_FALSE(GetAndResetHandleDragged());
377 EXPECT_FALSE(IsDragging());
378 EXPECT_EQ(TOUCH_HANDLE_LEFT, drawable().orientation);
381 TEST_F(TouchHandleTest, DragDefersFade) {
382 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
383 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
385 MockMotionEvent event(MockMotionEvent::ACTION_DOWN);
386 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
387 EXPECT_TRUE(IsDragging());
389 // Fade will be deferred until the drag ends.
390 handle.SetVisible(false, TouchHandle::ANIMATION_SMOOTH);
391 EXPECT_FALSE(NeedsAnimate());
392 EXPECT_TRUE(drawable().visible);
393 EXPECT_EQ(1.f, drawable().alpha);
395 event = MockMotionEvent(MockMotionEvent::ACTION_MOVE);
396 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
397 EXPECT_FALSE(NeedsAnimate());
398 EXPECT_TRUE(drawable().visible);
400 event = MockMotionEvent(MockMotionEvent::ACTION_UP);
401 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
402 EXPECT_FALSE(IsDragging());
403 EXPECT_TRUE(NeedsAnimate());
406 EXPECT_FALSE(drawable().visible);
407 EXPECT_EQ(0.f, drawable().alpha);
410 TEST_F(TouchHandleTest, DragTargettingUsesTouchSize) {
411 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
412 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
414 base::TimeTicks event_time = base::TimeTicks::Now();
415 const float kTouchSize = 24.f;
416 const float kOffset = kDefaultDrawableSize + kTouchSize / 2.001f;
418 MockMotionEvent event(
419 MockMotionEvent::ACTION_DOWN, event_time, kOffset, kOffset);
420 event.SetTouchMajor(0.f);
421 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
422 EXPECT_FALSE(IsDragging());
424 event.SetTouchMajor(kTouchSize / 2.f);
425 EXPECT_FALSE(handle.WillHandleTouchEvent(event));
426 EXPECT_FALSE(IsDragging());
428 event.SetTouchMajor(kTouchSize);
429 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
430 EXPECT_TRUE(IsDragging());
432 event.SetTouchMajor(kTouchSize * 2.f);
433 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
434 EXPECT_TRUE(IsDragging());
436 // Ensure a touch size of 0 can still register a hit.
437 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN,
439 kDefaultDrawableSize / 2.f,
440 kDefaultDrawableSize / 2.f);
441 event.SetTouchMajor(0);
442 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
443 EXPECT_TRUE(IsDragging());
446 TEST_F(TouchHandleTest, Tap) {
447 TouchHandle handle(this, TOUCH_HANDLE_CENTER);
448 handle.SetVisible(true, TouchHandle::ANIMATION_NONE);
450 base::TimeTicks event_time = base::TimeTicks::Now();
452 // ACTION_CANCEL shouldn't trigger a tap.
453 MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
454 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
455 event_time += base::TimeDelta::FromMilliseconds(50);
456 event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0);
457 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
458 EXPECT_FALSE(GetAndResetHandleTapped());
460 // Long press shouldn't trigger a tap.
461 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
462 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
463 event_time += 2 * GetTapTimeout();
464 event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0);
465 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
466 EXPECT_FALSE(GetAndResetHandleTapped());
468 // Only a brief tap within the slop region should trigger a tap.
469 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
470 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
471 event_time += GetTapTimeout() / 2;
472 event = MockMotionEvent(
473 MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop / 2.f, 0);
474 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
475 event = MockMotionEvent(
476 MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop / 2.f, 0);
477 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
478 EXPECT_TRUE(GetAndResetHandleTapped());
480 // Moving beyond the slop region shouldn't trigger a tap.
481 event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0);
482 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
483 event_time += GetTapTimeout() / 2;
484 event = MockMotionEvent(
485 MockMotionEvent::ACTION_MOVE, event_time, kDefaultTapSlop * 2.f, 0);
486 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
487 event = MockMotionEvent(
488 MockMotionEvent::ACTION_UP, event_time, kDefaultTapSlop * 2.f, 0);
489 EXPECT_TRUE(handle.WillHandleTouchEvent(event));
490 EXPECT_FALSE(GetAndResetHandleTapped());
493 } // namespace content