Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: ui/events/gesture_detection/gesture_detector.cc

Issue 243403002: Add multi-finger swipe detection to GestureDetector (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix M_PI Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
7
5 #include "ui/events/gesture_detection/gesture_detector.h" 8 #include "ui/events/gesture_detection/gesture_detector.h"
6 9
7 #include <cmath> 10 #include <cmath>
8 11
9 #include "base/timer/timer.h" 12 #include "base/timer/timer.h"
10 #include "ui/events/gesture_detection/motion_event.h" 13 #include "ui/events/gesture_detection/motion_event.h"
11 14
12 namespace ui { 15 namespace ui {
13 namespace { 16 namespace {
14 17
15 // Using a small epsilon when comparing slop distances allows pixel perfect 18 // Using a small epsilon when comparing slop distances allows pixel perfect
16 // slop determination when using fractional DIP coordinates (assuming the slop 19 // slop determination when using fractional DIP coordinates (assuming the slop
17 // region and DPI scale are reasonably proportioned). 20 // region and DPI scale are reasonably proportioned).
18 const float kSlopEpsilon = .05f; 21 const float kSlopEpsilon = .05f;
19 22
20 // Minimum distance a scroll must have traveled from the last scroll/focal point 23 // Minimum distance a scroll must have traveled from the last scroll/focal point
21 // to trigger an |OnScroll| callback. 24 // to trigger an |OnScroll| callback.
22 const float kScrollEpsilon = .1f; 25 const float kScrollEpsilon = .1f;
23 26
27 const float kDegreesToRadians = static_cast<float>(M_PI) / 180.0f;
28
24 // Constants used by TimeoutGestureHandler. 29 // Constants used by TimeoutGestureHandler.
25 enum TimeoutEvent { 30 enum TimeoutEvent {
26 SHOW_PRESS = 0, 31 SHOW_PRESS = 0,
27 LONG_PRESS, 32 LONG_PRESS,
28 TAP, 33 TAP,
29 TIMEOUT_EVENT_COUNT 34 TIMEOUT_EVENT_COUNT
30 }; 35 };
31 36
32 } // namespace 37 } // namespace
33 38
34 // Note: These constants were taken directly from the default (unscaled) 39 // Note: These constants were taken directly from the default (unscaled)
35 // versions found in Android's ViewConfiguration. 40 // versions found in Android's ViewConfiguration.
36 GestureDetector::Config::Config() 41 GestureDetector::Config::Config()
37 : longpress_timeout(base::TimeDelta::FromMilliseconds(500)), 42 : longpress_timeout(base::TimeDelta::FromMilliseconds(500)),
38 showpress_timeout(base::TimeDelta::FromMilliseconds(180)), 43 showpress_timeout(base::TimeDelta::FromMilliseconds(180)),
39 double_tap_timeout(base::TimeDelta::FromMilliseconds(300)), 44 double_tap_timeout(base::TimeDelta::FromMilliseconds(300)),
40 touch_slop(8), 45 touch_slop(8),
41 double_tap_slop(100), 46 double_tap_slop(100),
42 minimum_fling_velocity(50), 47 minimum_fling_velocity(50),
43 maximum_fling_velocity(8000) {} 48 maximum_fling_velocity(8000),
49 swipe_enabled(false),
50 minimum_swipe_velocity(20),
51 maximum_swipe_deviation_angle(20.f) {
52 }
44 53
45 GestureDetector::Config::~Config() {} 54 GestureDetector::Config::~Config() {}
46 55
47 bool GestureDetector::SimpleGestureListener::OnDown(const MotionEvent& e) { 56 bool GestureDetector::SimpleGestureListener::OnDown(const MotionEvent& e) {
48 return false; 57 return false;
49 } 58 }
50 59
51 void GestureDetector::SimpleGestureListener::OnShowPress(const MotionEvent& e) { 60 void GestureDetector::SimpleGestureListener::OnShowPress(const MotionEvent& e) {
52 } 61 }
53 62
(...skipping 13 matching lines...) Expand all
67 return false; 76 return false;
68 } 77 }
69 78
70 bool GestureDetector::SimpleGestureListener::OnFling(const MotionEvent& e1, 79 bool GestureDetector::SimpleGestureListener::OnFling(const MotionEvent& e1,
71 const MotionEvent& e2, 80 const MotionEvent& e2,
72 float velocity_x, 81 float velocity_x,
73 float velocity_y) { 82 float velocity_y) {
74 return false; 83 return false;
75 } 84 }
76 85
86 bool GestureDetector::SimpleGestureListener::OnSwipe(const MotionEvent& e1,
87 const MotionEvent& e2,
88 float velocity_x,
89 float velocity_y) {
90 return false;
91 }
92
77 bool GestureDetector::SimpleGestureListener::OnSingleTapConfirmed( 93 bool GestureDetector::SimpleGestureListener::OnSingleTapConfirmed(
78 const MotionEvent& e) { 94 const MotionEvent& e) {
79 return false; 95 return false;
80 } 96 }
81 97
82 bool GestureDetector::SimpleGestureListener::OnDoubleTap(const MotionEvent& e) { 98 bool GestureDetector::SimpleGestureListener::OnDoubleTap(const MotionEvent& e) {
83 return false; 99 return false;
84 } 100 }
85 101
86 bool GestureDetector::SimpleGestureListener::OnDoubleTapEvent( 102 bool GestureDetector::SimpleGestureListener::OnDoubleTapEvent(
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 still_down_(false), 167 still_down_(false),
152 defer_confirm_single_tap_(false), 168 defer_confirm_single_tap_(false),
153 in_longpress_(false), 169 in_longpress_(false),
154 always_in_tap_region_(false), 170 always_in_tap_region_(false),
155 always_in_bigger_tap_region_(false), 171 always_in_bigger_tap_region_(false),
156 is_double_tapping_(false), 172 is_double_tapping_(false),
157 last_focus_x_(0), 173 last_focus_x_(0),
158 last_focus_y_(0), 174 last_focus_y_(0),
159 down_focus_x_(0), 175 down_focus_x_(0),
160 down_focus_y_(0), 176 down_focus_y_(0),
161 is_longpress_enabled_(true) { 177 longpress_enabled_(true) {
162 DCHECK(listener_); 178 DCHECK(listener_);
163 Init(config); 179 Init(config);
164 } 180 }
165 181
166 GestureDetector::~GestureDetector() {} 182 GestureDetector::~GestureDetector() {}
167 183
168 bool GestureDetector::OnTouchEvent(const MotionEvent& ev) { 184 bool GestureDetector::OnTouchEvent(const MotionEvent& ev) {
169 const MotionEvent::Action action = ev.GetAction(); 185 const MotionEvent::Action action = ev.GetAction();
170 186
171 velocity_tracker_.AddMovement(ev); 187 velocity_tracker_.AddMovement(ev);
(...skipping 27 matching lines...) Expand all
199 case MotionEvent::ACTION_POINTER_UP: 215 case MotionEvent::ACTION_POINTER_UP:
200 down_focus_x_ = last_focus_x_ = focus_x; 216 down_focus_x_ = last_focus_x_ = focus_x;
201 down_focus_y_ = last_focus_y_ = focus_y; 217 down_focus_y_ = last_focus_y_ = focus_y;
202 218
203 // Check the dot product of current velocities. 219 // Check the dot product of current velocities.
204 // If the pointer that left was opposing another velocity vector, clear. 220 // If the pointer that left was opposing another velocity vector, clear.
205 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_); 221 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
206 { 222 {
207 const int up_index = ev.GetActionIndex(); 223 const int up_index = ev.GetActionIndex();
208 const int id1 = ev.GetPointerId(up_index); 224 const int id1 = ev.GetPointerId(up_index);
209 const float x1 = velocity_tracker_.GetXVelocity(id1); 225 const float vx1 = velocity_tracker_.GetXVelocity(id1);
210 const float y1 = velocity_tracker_.GetYVelocity(id1); 226 const float vy1 = velocity_tracker_.GetYVelocity(id1);
227 float vx_total = vx1;
228 float vy_total = vy1;
211 for (int i = 0; i < count; i++) { 229 for (int i = 0; i < count; i++) {
212 if (i == up_index) 230 if (i == up_index)
213 continue; 231 continue;
214 232
215 const int id2 = ev.GetPointerId(i); 233 const int id2 = ev.GetPointerId(i);
216 const float x = x1 * velocity_tracker_.GetXVelocity(id2); 234 const float vx2 = velocity_tracker_.GetXVelocity(id2);
217 const float y = y1 * velocity_tracker_.GetYVelocity(id2); 235 const float vy2 = velocity_tracker_.GetYVelocity(id2);
218 236 const float dot = vx1 * vx2 + vy1 * vy2;
219 const float dot = x + y;
220 if (dot < 0) { 237 if (dot < 0) {
238 vx_total = 0;
239 vy_total = 0;
221 velocity_tracker_.Clear(); 240 velocity_tracker_.Clear();
222 break; 241 break;
223 } 242 }
243 vx_total += vx2;
244 vy_total += vy2;
245 }
246
247 if (swipe_enabled_ && (vx_total || vy_total)) {
248 float vx = vx_total / count;
249 float vy = vy_total / count;
250 float vx_abs = std::abs(vx);
251 float vy_abs = std::abs(vy);
252
253 if (vx_abs < min_swipe_velocity_)
254 vx_abs = vx = 0;
255 if (vy_abs < min_swipe_velocity_)
256 vy_abs = vy = 0;
257
258 // Note that the ratio will be 0 if both velocites are below the min.
259 float ratio = vx_abs > vy_abs ? vx_abs / std::max(vy_abs, 0.001f)
260 : vy_abs / std::max(vx_abs, 0.001f);
261 if (ratio > min_swipe_direction_component_ratio_) {
262 if (vx_abs > vy_abs)
263 vy = 0;
264 else
265 vx = 0;
266
267 handled = listener_->OnSwipe(*current_down_event_, ev, vx, vy);
268 }
224 } 269 }
225 } 270 }
226 break; 271 break;
227 272
228 case MotionEvent::ACTION_DOWN: 273 case MotionEvent::ACTION_DOWN:
229 if (double_tap_listener_) { 274 if (double_tap_listener_) {
230 bool had_tap_message = timeout_handler_->HasTimeout(TAP); 275 bool had_tap_message = timeout_handler_->HasTimeout(TAP);
231 if (had_tap_message) 276 if (had_tap_message)
232 timeout_handler_->StopTimeout(TAP); 277 timeout_handler_->StopTimeout(TAP);
233 if (current_down_event_ && previous_up_event_ && had_tap_message && 278 if (current_down_event_ && previous_up_event_ && had_tap_message &&
(...skipping 18 matching lines...) Expand all
252 297
253 always_in_tap_region_ = true; 298 always_in_tap_region_ = true;
254 always_in_bigger_tap_region_ = true; 299 always_in_bigger_tap_region_ = true;
255 still_down_ = true; 300 still_down_ = true;
256 in_longpress_ = false; 301 in_longpress_ = false;
257 defer_confirm_single_tap_ = false; 302 defer_confirm_single_tap_ = false;
258 303
259 // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure 304 // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure
260 // proper timeout ordering. 305 // proper timeout ordering.
261 timeout_handler_->StartTimeout(SHOW_PRESS); 306 timeout_handler_->StartTimeout(SHOW_PRESS);
262 if (is_longpress_enabled_) 307 if (longpress_enabled_)
263 timeout_handler_->StartTimeout(LONG_PRESS); 308 timeout_handler_->StartTimeout(LONG_PRESS);
264 handled |= listener_->OnDown(ev); 309 handled |= listener_->OnDown(ev);
265 break; 310 break;
266 311
267 case MotionEvent::ACTION_MOVE: 312 case MotionEvent::ACTION_MOVE:
268 if (in_longpress_) 313 if (in_longpress_)
269 break; 314 break;
270 315
271 { 316 {
272 const float scroll_x = last_focus_x_ - focus_x; 317 const float scroll_x = last_focus_x_ - focus_x;
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 415
371 const float touch_slop = config.touch_slop + kSlopEpsilon; 416 const float touch_slop = config.touch_slop + kSlopEpsilon;
372 const float double_tap_touch_slop = touch_slop; 417 const float double_tap_touch_slop = touch_slop;
373 const float double_tap_slop = config.double_tap_slop + kSlopEpsilon; 418 const float double_tap_slop = config.double_tap_slop + kSlopEpsilon;
374 touch_slop_square_ = touch_slop * touch_slop; 419 touch_slop_square_ = touch_slop * touch_slop;
375 double_tap_touch_slop_square_ = double_tap_touch_slop * double_tap_touch_slop; 420 double_tap_touch_slop_square_ = double_tap_touch_slop * double_tap_touch_slop;
376 double_tap_slop_square_ = double_tap_slop * double_tap_slop; 421 double_tap_slop_square_ = double_tap_slop * double_tap_slop;
377 double_tap_timeout_ = config.double_tap_timeout; 422 double_tap_timeout_ = config.double_tap_timeout;
378 min_fling_velocity_ = config.minimum_fling_velocity; 423 min_fling_velocity_ = config.minimum_fling_velocity;
379 max_fling_velocity_ = config.maximum_fling_velocity; 424 max_fling_velocity_ = config.maximum_fling_velocity;
425
426 swipe_enabled_ = config.swipe_enabled;
427 min_swipe_velocity_ = config.minimum_swipe_velocity;
428 DCHECK_GT(config.maximum_swipe_deviation_angle, 0);
429 DCHECK_LE(config.maximum_swipe_deviation_angle, 45);
430 const float maximum_swipe_deviation_angle =
431 std::min(45.f, std::max(0.001f, config.maximum_swipe_deviation_angle));
432 min_swipe_direction_component_ratio_ =
433 1.f / tan(maximum_swipe_deviation_angle * kDegreesToRadians);
380 } 434 }
381 435
382 void GestureDetector::OnShowPressTimeout() { 436 void GestureDetector::OnShowPressTimeout() {
383 listener_->OnShowPress(*current_down_event_); 437 listener_->OnShowPress(*current_down_event_);
384 } 438 }
385 439
386 void GestureDetector::OnLongPressTimeout() { 440 void GestureDetector::OnLongPressTimeout() {
387 timeout_handler_->StopTimeout(TAP); 441 timeout_handler_->StopTimeout(TAP);
388 defer_confirm_single_tap_ = false; 442 defer_confirm_single_tap_ = false;
389 in_longpress_ = listener_->OnLongPress(*current_down_event_); 443 in_longpress_ = listener_->OnLongPress(*current_down_event_);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 if (second_down.GetEventTime() - first_up.GetEventTime() > 482 if (second_down.GetEventTime() - first_up.GetEventTime() >
429 double_tap_timeout_) 483 double_tap_timeout_)
430 return false; 484 return false;
431 485
432 const float delta_x = first_down.GetX() - second_down.GetX(); 486 const float delta_x = first_down.GetX() - second_down.GetX();
433 const float delta_y = first_down.GetY() - second_down.GetY(); 487 const float delta_y = first_down.GetY() - second_down.GetY();
434 return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_); 488 return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_);
435 } 489 }
436 490
437 } // namespace ui 491 } // namespace ui
OLDNEW
« no previous file with comments | « ui/events/gesture_detection/gesture_detector.h ('k') | ui/events/gesture_detection/gesture_provider.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698