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

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

Powered by Google App Engine
This is Rietveld 408576698