Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / events / gesture_detection / gesture_detector.cc
blob5dc61938b1ae4fff85ff6d8f30a836dd74075f96
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 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
8 #include "ui/events/gesture_detection/gesture_detector.h"
10 #include <cmath>
12 #include "base/timer/timer.h"
13 #include "ui/events/gesture_detection/motion_event.h"
15 namespace ui {
16 namespace {
18 // Using a small epsilon when comparing slop distances allows pixel perfect
19 // slop determination when using fractional DIP coordinates (assuming the slop
20 // region and DPI scale are reasonably proportioned).
21 const float kSlopEpsilon = .05f;
23 // Minimum distance a scroll must have traveled from the last scroll/focal point
24 // to trigger an |OnScroll| callback.
25 const float kScrollEpsilon = .1f;
27 const float kDegreesToRadians = static_cast<float>(M_PI) / 180.0f;
29 // Constants used by TimeoutGestureHandler.
30 enum TimeoutEvent {
31 SHOW_PRESS = 0,
32 LONG_PRESS,
33 TAP,
34 TIMEOUT_EVENT_COUNT
37 } // namespace
39 // Note: These constants were taken directly from the default (unscaled)
40 // versions found in Android's ViewConfiguration.
41 GestureDetector::Config::Config()
42 : longpress_timeout(base::TimeDelta::FromMilliseconds(500)),
43 showpress_timeout(base::TimeDelta::FromMilliseconds(180)),
44 double_tap_timeout(base::TimeDelta::FromMilliseconds(300)),
45 double_tap_min_time(base::TimeDelta::FromMilliseconds(40)),
46 touch_slop(8),
47 double_tap_slop(100),
48 minimum_fling_velocity(50),
49 maximum_fling_velocity(8000),
50 swipe_enabled(false),
51 minimum_swipe_velocity(20),
52 maximum_swipe_deviation_angle(20.f),
53 two_finger_tap_enabled(false),
54 two_finger_tap_max_separation(300),
55 two_finger_tap_timeout(base::TimeDelta::FromMilliseconds(700)) {
58 GestureDetector::Config::~Config() {}
60 bool GestureDetector::SimpleGestureListener::OnDown(const MotionEvent& e) {
61 return false;
64 void GestureDetector::SimpleGestureListener::OnShowPress(const MotionEvent& e) {
67 bool GestureDetector::SimpleGestureListener::OnSingleTapUp(
68 const MotionEvent& e) {
69 return false;
72 void GestureDetector::SimpleGestureListener::OnLongPress(const MotionEvent& e) {
75 bool GestureDetector::SimpleGestureListener::OnScroll(const MotionEvent& e1,
76 const MotionEvent& e2,
77 float distance_x,
78 float distance_y) {
79 return false;
82 bool GestureDetector::SimpleGestureListener::OnFling(const MotionEvent& e1,
83 const MotionEvent& e2,
84 float velocity_x,
85 float velocity_y) {
86 return false;
89 bool GestureDetector::SimpleGestureListener::OnSwipe(const MotionEvent& e1,
90 const MotionEvent& e2,
91 float velocity_x,
92 float velocity_y) {
93 return false;
96 bool GestureDetector::SimpleGestureListener::OnTwoFingerTap(
97 const MotionEvent& e1,
98 const MotionEvent& e2) {
99 return false;
102 bool GestureDetector::SimpleGestureListener::OnSingleTapConfirmed(
103 const MotionEvent& e) {
104 return false;
107 bool GestureDetector::SimpleGestureListener::OnDoubleTap(const MotionEvent& e) {
108 return false;
111 bool GestureDetector::SimpleGestureListener::OnDoubleTapEvent(
112 const MotionEvent& e) {
113 return false;
116 class GestureDetector::TimeoutGestureHandler {
117 public:
118 TimeoutGestureHandler(const Config& config, GestureDetector* gesture_detector)
119 : gesture_detector_(gesture_detector) {
120 DCHECK(config.showpress_timeout <= config.longpress_timeout);
122 timeout_callbacks_[SHOW_PRESS] = &GestureDetector::OnShowPressTimeout;
123 timeout_delays_[SHOW_PRESS] = config.showpress_timeout;
125 timeout_callbacks_[LONG_PRESS] = &GestureDetector::OnLongPressTimeout;
126 timeout_delays_[LONG_PRESS] =
127 config.longpress_timeout + config.showpress_timeout;
129 timeout_callbacks_[TAP] = &GestureDetector::OnTapTimeout;
130 timeout_delays_[TAP] = config.double_tap_timeout;
133 ~TimeoutGestureHandler() {
134 Stop();
137 void StartTimeout(TimeoutEvent event) {
138 timeout_timers_[event].Start(FROM_HERE,
139 timeout_delays_[event],
140 gesture_detector_,
141 timeout_callbacks_[event]);
144 void StopTimeout(TimeoutEvent event) { timeout_timers_[event].Stop(); }
146 void Stop() {
147 for (size_t i = SHOW_PRESS; i < TIMEOUT_EVENT_COUNT; ++i)
148 timeout_timers_[i].Stop();
151 bool HasTimeout(TimeoutEvent event) const {
152 return timeout_timers_[event].IsRunning();
155 private:
156 typedef void (GestureDetector::*ReceiverMethod)();
158 GestureDetector* const gesture_detector_;
159 base::OneShotTimer<GestureDetector> timeout_timers_[TIMEOUT_EVENT_COUNT];
160 ReceiverMethod timeout_callbacks_[TIMEOUT_EVENT_COUNT];
161 base::TimeDelta timeout_delays_[TIMEOUT_EVENT_COUNT];
164 GestureDetector::GestureDetector(
165 const Config& config,
166 GestureListener* listener,
167 DoubleTapListener* optional_double_tap_listener)
168 : timeout_handler_(new TimeoutGestureHandler(config, this)),
169 listener_(listener),
170 double_tap_listener_(optional_double_tap_listener),
171 touch_slop_square_(0),
172 double_tap_touch_slop_square_(0),
173 double_tap_slop_square_(0),
174 two_finger_tap_distance_square_(0),
175 min_fling_velocity_(1),
176 max_fling_velocity_(1),
177 min_swipe_velocity_(0),
178 min_swipe_direction_component_ratio_(0),
179 still_down_(false),
180 defer_confirm_single_tap_(false),
181 always_in_tap_region_(false),
182 always_in_bigger_tap_region_(false),
183 two_finger_tap_allowed_for_gesture_(false),
184 is_double_tapping_(false),
185 last_focus_x_(0),
186 last_focus_y_(0),
187 down_focus_x_(0),
188 down_focus_y_(0),
189 longpress_enabled_(true),
190 swipe_enabled_(false),
191 two_finger_tap_enabled_(false) {
192 DCHECK(listener_);
193 Init(config);
196 GestureDetector::~GestureDetector() {}
198 bool GestureDetector::OnTouchEvent(const MotionEvent& ev) {
199 const MotionEvent::Action action = ev.GetAction();
201 velocity_tracker_.AddMovement(ev);
203 const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
204 const int skip_index = pointer_up ? ev.GetActionIndex() : -1;
206 // Determine focal point.
207 float sum_x = 0, sum_y = 0;
208 const int count = static_cast<int>(ev.GetPointerCount());
209 for (int i = 0; i < count; i++) {
210 if (skip_index == i)
211 continue;
212 sum_x += ev.GetX(i);
213 sum_y += ev.GetY(i);
215 const int div = pointer_up ? count - 1 : count;
216 const float focus_x = sum_x / div;
217 const float focus_y = sum_y / div;
219 bool handled = false;
221 switch (action) {
222 case MotionEvent::ACTION_POINTER_DOWN: {
223 down_focus_x_ = last_focus_x_ = focus_x;
224 down_focus_y_ = last_focus_y_ = focus_y;
225 // Cancel long press and taps.
226 CancelTaps();
228 if (!two_finger_tap_allowed_for_gesture_)
229 break;
231 const int action_index = ev.GetActionIndex();
232 const float dx = ev.GetX(action_index) - current_down_event_->GetX();
233 const float dy = ev.GetY(action_index) - current_down_event_->GetY();
235 if (ev.GetPointerCount() == 2 &&
236 dx * dx + dy * dy < two_finger_tap_distance_square_) {
237 secondary_pointer_down_event_ = ev.Clone();
238 } else {
239 two_finger_tap_allowed_for_gesture_ = false;
241 } break;
243 case MotionEvent::ACTION_POINTER_UP: {
244 down_focus_x_ = last_focus_x_ = focus_x;
245 down_focus_y_ = last_focus_y_ = focus_y;
247 // Check the dot product of current velocities.
248 // If the pointer that left was opposing another velocity vector, clear.
249 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
250 const int up_index = ev.GetActionIndex();
251 const int id1 = ev.GetPointerId(up_index);
252 const float vx1 = velocity_tracker_.GetXVelocity(id1);
253 const float vy1 = velocity_tracker_.GetYVelocity(id1);
254 float vx_total = vx1;
255 float vy_total = vy1;
256 for (int i = 0; i < count; i++) {
257 if (i == up_index)
258 continue;
260 const int id2 = ev.GetPointerId(i);
261 const float vx2 = velocity_tracker_.GetXVelocity(id2);
262 const float vy2 = velocity_tracker_.GetYVelocity(id2);
263 const float dot = vx1 * vx2 + vy1 * vy2;
264 if (dot < 0) {
265 vx_total = 0;
266 vy_total = 0;
267 velocity_tracker_.Clear();
268 break;
270 vx_total += vx2;
271 vy_total += vy2;
274 handled = HandleSwipeIfNeeded(ev, vx_total / count, vy_total / count);
276 if (two_finger_tap_allowed_for_gesture_ && ev.GetPointerCount() == 2 &&
277 (ev.GetEventTime() - secondary_pointer_down_event_->GetEventTime() <=
278 two_finger_tap_timeout_)) {
279 handled = listener_->OnTwoFingerTap(*current_down_event_, ev);
281 two_finger_tap_allowed_for_gesture_ = false;
282 } break;
284 case MotionEvent::ACTION_DOWN:
285 if (double_tap_listener_) {
286 bool had_tap_message = timeout_handler_->HasTimeout(TAP);
287 if (had_tap_message)
288 timeout_handler_->StopTimeout(TAP);
289 if (current_down_event_ && previous_up_event_ && had_tap_message &&
290 IsConsideredDoubleTap(
291 *current_down_event_, *previous_up_event_, ev)) {
292 // This is a second tap.
293 is_double_tapping_ = true;
294 // Give a callback with the first tap of the double-tap.
295 handled |= double_tap_listener_->OnDoubleTap(*current_down_event_);
296 // Give a callback with down event of the double-tap.
297 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
298 } else {
299 // This is a first tap.
300 DCHECK(double_tap_timeout_ > base::TimeDelta());
301 timeout_handler_->StartTimeout(TAP);
305 down_focus_x_ = last_focus_x_ = focus_x;
306 down_focus_y_ = last_focus_y_ = focus_y;
307 current_down_event_ = ev.Clone();
309 secondary_pointer_down_event_.reset();
310 always_in_tap_region_ = true;
311 always_in_bigger_tap_region_ = true;
312 still_down_ = true;
313 defer_confirm_single_tap_ = false;
314 two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_;
316 // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure
317 // proper timeout ordering.
318 timeout_handler_->StartTimeout(SHOW_PRESS);
319 if (longpress_enabled_)
320 timeout_handler_->StartTimeout(LONG_PRESS);
321 handled |= listener_->OnDown(ev);
322 break;
324 case MotionEvent::ACTION_MOVE:
326 const float scroll_x = last_focus_x_ - focus_x;
327 const float scroll_y = last_focus_y_ - focus_y;
328 if (is_double_tapping_) {
329 // Give the move events of the double-tap.
330 DCHECK(double_tap_listener_);
331 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
332 } else if (always_in_tap_region_) {
333 const float delta_x = focus_x - down_focus_x_;
334 const float delta_y = focus_y - down_focus_y_;
335 const float distance_square = delta_x * delta_x + delta_y * delta_y;
336 if (distance_square > touch_slop_square_) {
337 handled = listener_->OnScroll(
338 *current_down_event_, ev, scroll_x, scroll_y);
339 last_focus_x_ = focus_x;
340 last_focus_y_ = focus_y;
341 always_in_tap_region_ = false;
342 timeout_handler_->Stop();
344 if (distance_square > double_tap_touch_slop_square_)
345 always_in_bigger_tap_region_ = false;
346 } else if (std::abs(scroll_x) > kScrollEpsilon ||
347 std::abs(scroll_y) > kScrollEpsilon) {
348 handled =
349 listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y);
350 last_focus_x_ = focus_x;
351 last_focus_y_ = focus_y;
354 if (!two_finger_tap_allowed_for_gesture_)
355 break;
357 // Two-finger tap should be prevented if either pointer exceeds its
358 // (independent) slop region.
359 const int id0 = current_down_event_->GetPointerId(0);
360 const int ev_idx0 = ev.GetPointerId(0) == id0 ? 0 : 1;
362 // Check if the primary pointer exceeded the slop region.
363 float dx = current_down_event_->GetX() - ev.GetX(ev_idx0);
364 float dy = current_down_event_->GetY() - ev.GetY(ev_idx0);
365 if (dx * dx + dy * dy > touch_slop_square_) {
366 two_finger_tap_allowed_for_gesture_ = false;
367 break;
369 if (ev.GetPointerCount() == 2) {
370 // Check if the secondary pointer exceeded the slop region.
371 const int ev_idx1 = ev_idx0 == 0 ? 1 : 0;
372 const int idx1 = secondary_pointer_down_event_->GetActionIndex();
373 dx = secondary_pointer_down_event_->GetX(idx1) - ev.GetX(ev_idx1);
374 dy = secondary_pointer_down_event_->GetY(idx1) - ev.GetY(ev_idx1);
375 if (dx * dx + dy * dy > touch_slop_square_)
376 two_finger_tap_allowed_for_gesture_ = false;
379 break;
381 case MotionEvent::ACTION_UP:
382 still_down_ = false;
384 if (is_double_tapping_) {
385 // Finally, give the up event of the double-tap.
386 DCHECK(double_tap_listener_);
387 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
388 } else if (always_in_tap_region_) {
389 handled = listener_->OnSingleTapUp(ev);
390 if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) {
391 double_tap_listener_->OnSingleTapConfirmed(ev);
393 } else {
395 // A fling must travel the minimum tap distance.
396 const int pointer_id = ev.GetPointerId(0);
397 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
398 const float velocity_y = velocity_tracker_.GetYVelocity(pointer_id);
399 const float velocity_x = velocity_tracker_.GetXVelocity(pointer_id);
401 if ((std::abs(velocity_y) > min_fling_velocity_) ||
402 (std::abs(velocity_x) > min_fling_velocity_)) {
403 handled = listener_->OnFling(
404 *current_down_event_, ev, velocity_x, velocity_y);
407 handled |= HandleSwipeIfNeeded(ev, velocity_x, velocity_y);
410 previous_up_event_ = ev.Clone();
412 velocity_tracker_.Clear();
413 is_double_tapping_ = false;
414 defer_confirm_single_tap_ = false;
415 timeout_handler_->StopTimeout(SHOW_PRESS);
416 timeout_handler_->StopTimeout(LONG_PRESS);
418 break;
420 case MotionEvent::ACTION_CANCEL:
421 Cancel();
422 break;
425 return handled;
428 void GestureDetector::SetDoubleTapListener(
429 DoubleTapListener* double_tap_listener) {
430 if (double_tap_listener == double_tap_listener_)
431 return;
433 DCHECK(!is_double_tapping_);
435 // Null'ing the double-tap listener should flush an active tap timeout.
436 if (!double_tap_listener) {
437 if (timeout_handler_->HasTimeout(TAP)) {
438 timeout_handler_->StopTimeout(TAP);
439 OnTapTimeout();
443 double_tap_listener_ = double_tap_listener;
446 void GestureDetector::Init(const Config& config) {
447 DCHECK(listener_);
449 const float touch_slop = config.touch_slop + kSlopEpsilon;
450 const float double_tap_touch_slop = touch_slop;
451 const float double_tap_slop = config.double_tap_slop + kSlopEpsilon;
452 touch_slop_square_ = touch_slop * touch_slop;
453 double_tap_touch_slop_square_ = double_tap_touch_slop * double_tap_touch_slop;
454 double_tap_slop_square_ = double_tap_slop * double_tap_slop;
455 double_tap_timeout_ = config.double_tap_timeout;
456 double_tap_min_time_ = config.double_tap_min_time;
457 DCHECK(double_tap_min_time_ < double_tap_timeout_);
458 min_fling_velocity_ = config.minimum_fling_velocity;
459 max_fling_velocity_ = config.maximum_fling_velocity;
461 swipe_enabled_ = config.swipe_enabled;
462 min_swipe_velocity_ = config.minimum_swipe_velocity;
463 DCHECK_GT(config.maximum_swipe_deviation_angle, 0);
464 DCHECK_LE(config.maximum_swipe_deviation_angle, 45);
465 const float maximum_swipe_deviation_angle =
466 std::min(45.f, std::max(0.001f, config.maximum_swipe_deviation_angle));
467 min_swipe_direction_component_ratio_ =
468 1.f / tan(maximum_swipe_deviation_angle * kDegreesToRadians);
470 two_finger_tap_enabled_ = config.two_finger_tap_enabled;
471 two_finger_tap_distance_square_ = config.two_finger_tap_max_separation *
472 config.two_finger_tap_max_separation;
473 two_finger_tap_timeout_ = config.two_finger_tap_timeout;
476 void GestureDetector::OnShowPressTimeout() {
477 listener_->OnShowPress(*current_down_event_);
480 void GestureDetector::OnLongPressTimeout() {
481 timeout_handler_->StopTimeout(TAP);
482 defer_confirm_single_tap_ = false;
483 listener_->OnLongPress(*current_down_event_);
486 void GestureDetector::OnTapTimeout() {
487 if (!double_tap_listener_)
488 return;
489 if (!still_down_)
490 double_tap_listener_->OnSingleTapConfirmed(*current_down_event_);
491 else
492 defer_confirm_single_tap_ = true;
495 void GestureDetector::Cancel() {
496 CancelTaps();
497 velocity_tracker_.Clear();
498 still_down_ = false;
501 void GestureDetector::CancelTaps() {
502 timeout_handler_->Stop();
503 is_double_tapping_ = false;
504 always_in_tap_region_ = false;
505 always_in_bigger_tap_region_ = false;
506 defer_confirm_single_tap_ = false;
509 bool GestureDetector::IsConsideredDoubleTap(
510 const MotionEvent& first_down,
511 const MotionEvent& first_up,
512 const MotionEvent& second_down) const {
513 if (!always_in_bigger_tap_region_)
514 return false;
516 const base::TimeDelta delta_time =
517 second_down.GetEventTime() - first_up.GetEventTime();
518 if (delta_time < double_tap_min_time_ || delta_time > double_tap_timeout_)
519 return false;
521 const float delta_x = first_down.GetX() - second_down.GetX();
522 const float delta_y = first_down.GetY() - second_down.GetY();
523 return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_);
526 bool GestureDetector::HandleSwipeIfNeeded(const MotionEvent& up,
527 float vx,
528 float vy) {
529 if (!swipe_enabled_ || (!vx && !vy))
530 return false;
531 float vx_abs = std::abs(vx);
532 float vy_abs = std::abs(vy);
534 if (vx_abs < min_swipe_velocity_)
535 vx_abs = vx = 0;
536 if (vy_abs < min_swipe_velocity_)
537 vy_abs = vy = 0;
539 // Note that the ratio will be 0 if both velocites are below the min.
540 float ratio = vx_abs > vy_abs ? vx_abs / std::max(vy_abs, 0.001f)
541 : vy_abs / std::max(vx_abs, 0.001f);
543 if (ratio < min_swipe_direction_component_ratio_)
544 return false;
546 if (vx_abs > vy_abs)
547 vy = 0;
548 else
549 vx = 0;
550 return listener_->OnSwipe(*current_down_event_, up, vx, vy);
553 } // namespace ui