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

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

Issue 2058723003: Slop region check for multi-finger scroll (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 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. 5 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES 6 #define _USE_MATH_DEFINES
7 7
8 #include "ui/events/gesture_detection/gesture_detector.h" 8 #include "ui/events/gesture_detection/gesture_detector.h"
9 9
10 #include <stddef.h> 10 #include <stddef.h>
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 touch_slop_square_(0), 123 touch_slop_square_(0),
124 double_tap_touch_slop_square_(0), 124 double_tap_touch_slop_square_(0),
125 double_tap_slop_square_(0), 125 double_tap_slop_square_(0),
126 two_finger_tap_distance_square_(0), 126 two_finger_tap_distance_square_(0),
127 min_fling_velocity_(1), 127 min_fling_velocity_(1),
128 max_fling_velocity_(1), 128 max_fling_velocity_(1),
129 min_swipe_velocity_(0), 129 min_swipe_velocity_(0),
130 min_swipe_direction_component_ratio_(0), 130 min_swipe_direction_component_ratio_(0),
131 still_down_(false), 131 still_down_(false),
132 defer_confirm_single_tap_(false), 132 defer_confirm_single_tap_(false),
133 always_in_tap_region_(false), 133 all_pointers_always_in_tap_region_(false),
134 always_in_bigger_tap_region_(false), 134 always_in_bigger_tap_region_(false),
135 two_finger_tap_allowed_for_gesture_(false), 135 two_finger_tap_allowed_for_gesture_(false),
136 is_double_tapping_(false), 136 is_double_tapping_(false),
137 is_down_candidate_for_repeated_single_tap_(false), 137 is_down_candidate_for_repeated_single_tap_(false),
138 maximum_pointer_count_(0),
138 current_single_tap_repeat_count_(0), 139 current_single_tap_repeat_count_(0),
139 single_tap_repeat_interval_(1), 140 single_tap_repeat_interval_(1),
140 last_focus_x_(0), 141 last_focus_x_(0),
141 last_focus_y_(0), 142 last_focus_y_(0),
142 down_focus_x_(0), 143 down_focus_x_(0),
143 down_focus_y_(0), 144 down_focus_y_(0),
144 longpress_enabled_(true), 145 longpress_enabled_(true),
145 showpress_enabled_(true), 146 showpress_enabled_(true),
146 swipe_enabled_(false), 147 swipe_enabled_(false),
147 two_finger_tap_enabled_(false), 148 two_finger_tap_enabled_(false),
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
179 case MotionEvent::ACTION_NONE: 180 case MotionEvent::ACTION_NONE:
180 NOTREACHED(); 181 NOTREACHED();
181 return handled; 182 return handled;
182 183
183 case MotionEvent::ACTION_POINTER_DOWN: { 184 case MotionEvent::ACTION_POINTER_DOWN: {
184 down_focus_x_ = last_focus_x_ = focus_x; 185 down_focus_x_ = last_focus_x_ = focus_x;
185 down_focus_y_ = last_focus_y_ = focus_y; 186 down_focus_y_ = last_focus_y_ = focus_y;
186 // Cancel long press and taps. 187 // Cancel long press and taps.
187 CancelTaps(); 188 CancelTaps();
188 189
190 //Update maximum pointer count
tdresser 2016/06/10 19:34:57 // Update maximum pointer count. Note the space b
sahel 2016/06/23 22:34:44 Done.
191 int current_pointer_count = ev.GetPointerCount();
192 if(current_pointer_count > maximum_pointer_count_)
193 maximum_pointer_count_ = current_pointer_count;
tdresser 2016/06/10 19:34:57 maximum_pointer_count_ = std::max(maximum_pointer_
sahel 2016/06/23 22:34:43 Done.
194
195 // Even when two_finger_tap_allowed_for_gesture_ is false,
196 // second pointer down information must be stored to check
197 // the slop region in multi-finger scrolls.
198 if(ev.GetPointerCount() == 2)
199 secondary_pointer_down_event_ = ev.Clone();
200
189 if (!two_finger_tap_allowed_for_gesture_) 201 if (!two_finger_tap_allowed_for_gesture_)
190 break; 202 break;
191 203
192 const int action_index = ev.GetActionIndex(); 204 const int action_index = ev.GetActionIndex();
193 const float dx = ev.GetX(action_index) - current_down_event_->GetX(); 205 const float dx = ev.GetX(action_index) - current_down_event_->GetX();
194 const float dy = ev.GetY(action_index) - current_down_event_->GetY(); 206 const float dy = ev.GetY(action_index) - current_down_event_->GetY();
195 207
196 if (ev.GetPointerCount() == 2 && 208 if (ev.GetPointerCount() != 2 || maximum_pointer_count_ > 2 ||
tdresser 2016/06/10 19:34:57 Is the first part of this clause necessary? Couldn
sahel 2016/06/23 22:34:43 You are right, if the current count is not two, th
197 dx * dx + dy * dy < two_finger_tap_distance_square_) { 209 dx * dx + dy * dy >= two_finger_tap_distance_square_)
198 secondary_pointer_down_event_ = ev.Clone();
199 } else {
200 two_finger_tap_allowed_for_gesture_ = false; 210 two_finger_tap_allowed_for_gesture_ = false;
201 }
202 } break; 211 } break;
203 212
204 case MotionEvent::ACTION_POINTER_UP: { 213 case MotionEvent::ACTION_POINTER_UP: {
205 down_focus_x_ = last_focus_x_ = focus_x; 214 down_focus_x_ = last_focus_x_ = focus_x;
206 down_focus_y_ = last_focus_y_ = focus_y; 215 down_focus_y_ = last_focus_y_ = focus_y;
207 216
208 // Check the dot product of current velocities. 217 // Check the dot product of current velocities.
209 // If the pointer that left was opposing another velocity vector, clear. 218 // If the pointer that left was opposing another velocity vector, clear.
210 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_); 219 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
211 const int up_index = ev.GetActionIndex(); 220 const int up_index = ev.GetActionIndex();
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 } 274 }
266 } else { 275 } else {
267 is_down_candidate_for_repeated_single_tap_ = is_repeated_tap; 276 is_down_candidate_for_repeated_single_tap_ = is_repeated_tap;
268 } 277 }
269 278
270 down_focus_x_ = last_focus_x_ = focus_x; 279 down_focus_x_ = last_focus_x_ = focus_x;
271 down_focus_y_ = last_focus_y_ = focus_y; 280 down_focus_y_ = last_focus_y_ = focus_y;
272 current_down_event_ = ev.Clone(); 281 current_down_event_ = ev.Clone();
273 282
274 secondary_pointer_down_event_.reset(); 283 secondary_pointer_down_event_.reset();
275 always_in_tap_region_ = true; 284 all_pointers_always_in_tap_region_ = true;
276 always_in_bigger_tap_region_ = true; 285 always_in_bigger_tap_region_ = true;
277 still_down_ = true; 286 still_down_ = true;
278 defer_confirm_single_tap_ = false; 287 defer_confirm_single_tap_ = false;
279 two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_; 288 two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_;
289 maximum_pointer_count_ = 1;
280 290
281 // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure 291 // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure
282 // proper timeout ordering. 292 // proper timeout ordering.
283 if (showpress_enabled_) 293 if (showpress_enabled_)
284 timeout_handler_->StartTimeout(SHOW_PRESS); 294 timeout_handler_->StartTimeout(SHOW_PRESS);
285 if (longpress_enabled_) 295 if (longpress_enabled_)
286 timeout_handler_->StartTimeout(LONG_PRESS); 296 timeout_handler_->StartTimeout(LONG_PRESS);
287 handled |= listener_->OnDown(ev); 297 handled |= listener_->OnDown(ev);
288 } break; 298 } break;
289 299
290 case MotionEvent::ACTION_MOVE: 300 case MotionEvent::ACTION_MOVE:
291 { 301 {
292 const float scroll_x = last_focus_x_ - focus_x; 302 const float scroll_x = last_focus_x_ - focus_x;
293 const float scroll_y = last_focus_y_ - focus_y; 303 const float scroll_y = last_focus_y_ - focus_y;
294 if (is_double_tapping_) { 304 if (is_double_tapping_) {
295 // Give the move events of the double-tap. 305 // Give the move events of the double-tap.
296 DCHECK(double_tap_listener_); 306 DCHECK(double_tap_listener_);
297 handled |= double_tap_listener_->OnDoubleTapEvent(ev); 307 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
298 } else if (always_in_tap_region_) { 308 } else if (all_pointers_always_in_tap_region_) {
299 const float delta_x = focus_x - down_focus_x_; 309 if (!IsWithinTouchSlop(ev)) {
300 const float delta_y = focus_y - down_focus_y_;
301 const float distance_square = delta_x * delta_x + delta_y * delta_y;
302 if (distance_square > touch_slop_square_) {
303 handled = listener_->OnScroll( 310 handled = listener_->OnScroll(
304 *current_down_event_, ev, scroll_x, scroll_y); 311 *current_down_event_, ev, scroll_x, scroll_y);
305 last_focus_x_ = focus_x; 312 last_focus_x_ = focus_x;
306 last_focus_y_ = focus_y; 313 last_focus_y_ = focus_y;
307 always_in_tap_region_ = false; 314 all_pointers_always_in_tap_region_ = false;
308 timeout_handler_->Stop(); 315 timeout_handler_->Stop();
309 } 316 }
317
318 const float delta_x = focus_x - down_focus_x_;
319 const float delta_y = focus_y - down_focus_y_;
320 const float distance_square = delta_x * delta_x + delta_y * delta_y;
310 if (distance_square > double_tap_touch_slop_square_) 321 if (distance_square > double_tap_touch_slop_square_)
311 always_in_bigger_tap_region_ = false; 322 always_in_bigger_tap_region_ = false;
312 } else if (std::abs(scroll_x) > kScrollEpsilon || 323 } else if (std::abs(scroll_x) > kScrollEpsilon ||
313 std::abs(scroll_y) > kScrollEpsilon) { 324 std::abs(scroll_y) > kScrollEpsilon) {
314 // We should eventually apply touch slop for multi-finger
315 // scrolls as well as single finger scrolls. See
316 // crbug.com/492185 for details.
317 handled = 325 handled =
318 listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y); 326 listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y);
319 last_focus_x_ = focus_x; 327 last_focus_x_ = focus_x;
320 last_focus_y_ = focus_y; 328 last_focus_y_ = focus_y;
321 } 329 }
322 330
323 if (!two_finger_tap_allowed_for_gesture_) 331 if (!two_finger_tap_allowed_for_gesture_)
324 break; 332 break;
325 333
326 // Two-finger tap should be prevented if either pointer exceeds its 334 // Two-finger tap should be prevented if either pointer exceeds its
327 // (independent) slop region. 335 // (independent) slop region.
328 const int id0 = current_down_event_->GetPointerId(0); 336 // If the event has had more than two pointers down at any time,
329 const int ev_idx0 = ev.GetPointerId(0) == id0 ? 0 : 1; 337 // two finger tap should be prevented.
330 338 if (maximum_pointer_count_ > 2 || !IsWithinTouchSlop(ev)) {
331 // Check if the primary pointer exceeded the slop region.
332 float dx = current_down_event_->GetX() - ev.GetX(ev_idx0);
333 float dy = current_down_event_->GetY() - ev.GetY(ev_idx0);
334 if (dx * dx + dy * dy > touch_slop_square_) {
335 two_finger_tap_allowed_for_gesture_ = false; 339 two_finger_tap_allowed_for_gesture_ = false;
336 break;
337 }
338 if (ev.GetPointerCount() == 2) {
339 // Check if the secondary pointer exceeded the slop region.
340 const int ev_idx1 = ev_idx0 == 0 ? 1 : 0;
341 const int idx1 = secondary_pointer_down_event_->GetActionIndex();
342 dx = secondary_pointer_down_event_->GetX(idx1) - ev.GetX(ev_idx1);
343 dy = secondary_pointer_down_event_->GetY(idx1) - ev.GetY(ev_idx1);
344 if (dx * dx + dy * dy > touch_slop_square_)
345 two_finger_tap_allowed_for_gesture_ = false;
346 } 340 }
347 } 341 }
348 break; 342 break;
349 343
350 case MotionEvent::ACTION_UP: 344 case MotionEvent::ACTION_UP:
351 still_down_ = false; 345 still_down_ = false;
352 { 346 {
353 if (is_double_tapping_) { 347 if (is_double_tapping_) {
354 // Finally, give the up event of the double-tap. 348 // Finally, give the up event of the double-tap.
355 DCHECK(double_tap_listener_); 349 DCHECK(double_tap_listener_);
356 handled |= double_tap_listener_->OnDoubleTapEvent(ev); 350 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
357 } else if (always_in_tap_region_) { 351 } else if (all_pointers_always_in_tap_region_ &&
352 maximum_pointer_count_ == 1) {
358 if (is_down_candidate_for_repeated_single_tap_) { 353 if (is_down_candidate_for_repeated_single_tap_) {
359 current_single_tap_repeat_count_ = 354 current_single_tap_repeat_count_ =
360 (1 + current_single_tap_repeat_count_) % 355 (1 + current_single_tap_repeat_count_) %
361 single_tap_repeat_interval_; 356 single_tap_repeat_interval_;
362 } else { 357 } else {
363 current_single_tap_repeat_count_ = 0; 358 current_single_tap_repeat_count_ = 0;
364 } 359 }
365 handled = listener_->OnSingleTapUp( 360 handled = listener_->OnSingleTapUp(
366 ev, 1 + current_single_tap_repeat_count_); 361 ev, 1 + current_single_tap_repeat_count_);
367 if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) { 362 if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) {
(...skipping 17 matching lines...) Expand all
385 } 380 }
386 381
387 previous_up_event_ = ev.Clone(); 382 previous_up_event_ = ev.Clone();
388 383
389 velocity_tracker_.Clear(); 384 velocity_tracker_.Clear();
390 is_double_tapping_ = false; 385 is_double_tapping_ = false;
391 defer_confirm_single_tap_ = false; 386 defer_confirm_single_tap_ = false;
392 timeout_handler_->StopTimeout(SHOW_PRESS); 387 timeout_handler_->StopTimeout(SHOW_PRESS);
393 timeout_handler_->StopTimeout(LONG_PRESS); 388 timeout_handler_->StopTimeout(LONG_PRESS);
394 } 389 }
390 maximum_pointer_count_ = 0;
395 break; 391 break;
396 392
397 case MotionEvent::ACTION_CANCEL: 393 case MotionEvent::ACTION_CANCEL:
398 Cancel(); 394 Cancel();
399 break; 395 break;
400 } 396 }
401 397
402 return handled; 398 return handled;
403 } 399 }
404 400
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
470 CHECK(previous_up_event_); 466 CHECK(previous_up_event_);
471 double_tap_listener_->OnSingleTapConfirmed(*previous_up_event_); 467 double_tap_listener_->OnSingleTapConfirmed(*previous_up_event_);
472 } else { 468 } else {
473 defer_confirm_single_tap_ = true; 469 defer_confirm_single_tap_ = true;
474 } 470 }
475 } 471 }
476 472
477 void GestureDetector::Cancel() { 473 void GestureDetector::Cancel() {
478 CancelTaps(); 474 CancelTaps();
479 velocity_tracker_.Clear(); 475 velocity_tracker_.Clear();
476 all_pointers_always_in_tap_region_ = false;
480 still_down_ = false; 477 still_down_ = false;
481 } 478 }
482 479
483 void GestureDetector::CancelTaps() { 480 void GestureDetector::CancelTaps() {
484 timeout_handler_->Stop(); 481 timeout_handler_->Stop();
485 is_double_tapping_ = false; 482 is_double_tapping_ = false;
486 always_in_tap_region_ = false;
487 always_in_bigger_tap_region_ = false; 483 always_in_bigger_tap_region_ = false;
488 defer_confirm_single_tap_ = false; 484 defer_confirm_single_tap_ = false;
489 is_down_candidate_for_repeated_single_tap_ = false; 485 is_down_candidate_for_repeated_single_tap_ = false;
490 current_single_tap_repeat_count_ = 0; 486 current_single_tap_repeat_count_ = 0;
491 } 487 }
492 488
493 bool GestureDetector::IsRepeatedTap(const MotionEvent& first_down, 489 bool GestureDetector::IsRepeatedTap(const MotionEvent& first_down,
494 const MotionEvent& first_up, 490 const MotionEvent& first_up,
495 const MotionEvent& second_down) const { 491 const MotionEvent& second_down) const {
496 if (!always_in_bigger_tap_region_) 492 if (!always_in_bigger_tap_region_)
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
532 if (ratio < min_swipe_direction_component_ratio_) 528 if (ratio < min_swipe_direction_component_ratio_)
533 return false; 529 return false;
534 530
535 if (vx_abs > vy_abs) 531 if (vx_abs > vy_abs)
536 vy = 0; 532 vy = 0;
537 else 533 else
538 vx = 0; 534 vx = 0;
539 return listener_->OnSwipe(*current_down_event_, up, vx, vy); 535 return listener_->OnSwipe(*current_down_event_, up, vx, vy);
540 } 536 }
541 537
538 bool GestureDetector::IsWithinTouchSlop(const MotionEvent& ev) {
539 // If there are more than two down pointers, tapping is not possible.
540 // Slop region check is not needed.
541 if (ev.GetPointerCount() > 2)
542 return false;
543
544 const int id0 = current_down_event_->GetPointerId(0);
545 const int ev_idx0 = ev.GetPointerId(0) == id0 ? 0 : 1;
546
547 // Check if the primary pointer exceeded the slop region.
548 float dx = current_down_event_->GetX() - ev.GetX(ev_idx0);
549 float dy = current_down_event_->GetY() - ev.GetY(ev_idx0);
550 if (dx * dx + dy * dy > touch_slop_square_)
551 return false;
552
553 if (ev.GetPointerCount() == 2 ) {
554 // Check if the secondary pointer exceeded the slop region.
555 const int ev_idx1 = ev_idx0 == 0 ? 1 : 0;
556 const int idx1 = secondary_pointer_down_event_->GetActionIndex();
557 dx = secondary_pointer_down_event_->GetX(idx1) - ev.GetX(ev_idx1);
558 dy = secondary_pointer_down_event_->GetY(idx1) - ev.GetY(ev_idx1);
559 if (dx * dx + dy * dy > touch_slop_square_) {
560 return false;
561 }
562 }
563
564 return true;
565 }
566
542 } // namespace ui 567 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698