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

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, 5 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_within_slop_regions_(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 30 matching lines...) Expand all
178 switch (action) { 179 switch (action) {
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();
189 maximum_pointer_count_ = std::max(maximum_pointer_count_,
190 static_cast<int>(ev.GetPointerCount()));
tdresser 2016/06/28 15:24:19 Indentation is off here.
sahel 2016/06/29 16:26:18 Done.
191
192 // Even when two_finger_tap_allowed_for_gesture_ is false,
193 // second pointer down information must be stored to check
194 // the slop region in multi-finger scrolls.
195 if (ev.GetPointerCount() == 2)
196 secondary_pointer_down_event_ = ev.Clone();
188 197
189 if (!two_finger_tap_allowed_for_gesture_) 198 if (!two_finger_tap_allowed_for_gesture_)
190 break; 199 break;
191 200
192 const int action_index = ev.GetActionIndex(); 201 const int action_index = ev.GetActionIndex();
193 const float dx = ev.GetX(action_index) - current_down_event_->GetX(); 202 const float dx = ev.GetX(action_index) - current_down_event_->GetX();
194 const float dy = ev.GetY(action_index) - current_down_event_->GetY(); 203 const float dy = ev.GetY(action_index) - current_down_event_->GetY();
195 204
196 if (ev.GetPointerCount() == 2 && 205 if (maximum_pointer_count_ > 2 ||
197 dx * dx + dy * dy < two_finger_tap_distance_square_) { 206 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; 207 two_finger_tap_allowed_for_gesture_ = false;
201 }
202 } break; 208 } break;
203 209
204 case MotionEvent::ACTION_POINTER_UP: { 210 case MotionEvent::ACTION_POINTER_UP: {
205 down_focus_x_ = last_focus_x_ = focus_x; 211 down_focus_x_ = last_focus_x_ = focus_x;
206 down_focus_y_ = last_focus_y_ = focus_y; 212 down_focus_y_ = last_focus_y_ = focus_y;
207 213
208 // Check the dot product of current velocities. 214 // Check the dot product of current velocities.
209 // If the pointer that left was opposing another velocity vector, clear. 215 // If the pointer that left was opposing another velocity vector, clear.
210 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_); 216 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
211 const int up_index = ev.GetActionIndex(); 217 const int up_index = ev.GetActionIndex();
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 } 271 }
266 } else { 272 } else {
267 is_down_candidate_for_repeated_single_tap_ = is_repeated_tap; 273 is_down_candidate_for_repeated_single_tap_ = is_repeated_tap;
268 } 274 }
269 275
270 down_focus_x_ = last_focus_x_ = focus_x; 276 down_focus_x_ = last_focus_x_ = focus_x;
271 down_focus_y_ = last_focus_y_ = focus_y; 277 down_focus_y_ = last_focus_y_ = focus_y;
272 current_down_event_ = ev.Clone(); 278 current_down_event_ = ev.Clone();
273 279
274 secondary_pointer_down_event_.reset(); 280 secondary_pointer_down_event_.reset();
275 always_in_tap_region_ = true; 281 all_pointers_within_slop_regions_ = true;
276 always_in_bigger_tap_region_ = true; 282 always_in_bigger_tap_region_ = true;
277 still_down_ = true; 283 still_down_ = true;
278 defer_confirm_single_tap_ = false; 284 defer_confirm_single_tap_ = false;
279 two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_; 285 two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_;
286 maximum_pointer_count_ = 1;
280 287
281 // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure 288 // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure
282 // proper timeout ordering. 289 // proper timeout ordering.
283 if (showpress_enabled_) 290 if (showpress_enabled_)
284 timeout_handler_->StartTimeout(SHOW_PRESS); 291 timeout_handler_->StartTimeout(SHOW_PRESS);
285 if (longpress_enabled_) 292 if (longpress_enabled_)
286 timeout_handler_->StartTimeout(LONG_PRESS); 293 timeout_handler_->StartTimeout(LONG_PRESS);
287 handled |= listener_->OnDown(ev); 294 handled |= listener_->OnDown(ev);
288 } break; 295 } break;
289 296
290 case MotionEvent::ACTION_MOVE: 297 case MotionEvent::ACTION_MOVE:
291 { 298 {
292 const float scroll_x = last_focus_x_ - focus_x; 299 const float scroll_x = last_focus_x_ - focus_x;
293 const float scroll_y = last_focus_y_ - focus_y; 300 const float scroll_y = last_focus_y_ - focus_y;
294 if (is_double_tapping_) { 301 if (is_double_tapping_) {
295 // Give the move events of the double-tap. 302 // Give the move events of the double-tap.
296 DCHECK(double_tap_listener_); 303 DCHECK(double_tap_listener_);
297 handled |= double_tap_listener_->OnDoubleTapEvent(ev); 304 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
298 } else if (always_in_tap_region_) { 305 } else if (all_pointers_within_slop_regions_) {
306 if (!IsWithinTouchSlop(ev)) {
307 handled = listener_->OnScroll(*current_down_event_, ev,
308 (ev.GetPointerCount() > 1 ?
309 *secondary_pointer_down_event_ : ev),
310 scroll_x, scroll_y);
311 last_focus_x_ = focus_x;
312 last_focus_y_ = focus_y;
313 all_pointers_within_slop_regions_ = false;
314 timeout_handler_->Stop();
315 }
316
299 const float delta_x = focus_x - down_focus_x_; 317 const float delta_x = focus_x - down_focus_x_;
300 const float delta_y = focus_y - down_focus_y_; 318 const float delta_y = focus_y - down_focus_y_;
301 const float distance_square = delta_x * delta_x + delta_y * delta_y; 319 const float distance_square = delta_x * delta_x + delta_y * delta_y;
302 if (distance_square > touch_slop_square_) {
303 handled = listener_->OnScroll(
304 *current_down_event_, ev, scroll_x, scroll_y);
305 last_focus_x_ = focus_x;
306 last_focus_y_ = focus_y;
307 always_in_tap_region_ = false;
308 timeout_handler_->Stop();
309 }
310 if (distance_square > double_tap_touch_slop_square_) 320 if (distance_square > double_tap_touch_slop_square_)
311 always_in_bigger_tap_region_ = false; 321 always_in_bigger_tap_region_ = false;
312 } else if (std::abs(scroll_x) > kScrollEpsilon || 322 } else if (std::abs(scroll_x) > kScrollEpsilon ||
313 std::abs(scroll_y) > kScrollEpsilon) { 323 std::abs(scroll_y) > kScrollEpsilon) {
314 // We should eventually apply touch slop for multi-finger 324 handled = listener_->OnScroll(*current_down_event_, ev,
315 // scrolls as well as single finger scrolls. See 325 (ev.GetPointerCount() > 1 ?
316 // crbug.com/492185 for details. 326 *secondary_pointer_down_event_ : ev),
tdresser 2016/06/28 15:24:19 Is this indentation right? I'd recommend "git cl f
sahel 2016/06/29 16:26:18 Didn't know about clang-format and git cl format,
317 handled = 327 scroll_x, scroll_y);
318 listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y);
319 last_focus_x_ = focus_x; 328 last_focus_x_ = focus_x;
320 last_focus_y_ = focus_y; 329 last_focus_y_ = focus_y;
321 } 330 }
322 331
323 if (!two_finger_tap_allowed_for_gesture_) 332 if (!two_finger_tap_allowed_for_gesture_)
324 break; 333 break;
325 334
326 // Two-finger tap should be prevented if either pointer exceeds its 335 // Two-finger tap should be prevented if either pointer exceeds its
327 // (independent) slop region. 336 // (independent) slop region.
328 const int id0 = current_down_event_->GetPointerId(0); 337 // If the event has had more than two pointers down at any time,
329 const int ev_idx0 = ev.GetPointerId(0) == id0 ? 0 : 1; 338 // two finger tap should be prevented.
330 339 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; 340 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 } 341 }
347 } 342 }
348 break; 343 break;
349 344
350 case MotionEvent::ACTION_UP: 345 case MotionEvent::ACTION_UP:
351 still_down_ = false; 346 still_down_ = false;
352 { 347 {
353 if (is_double_tapping_) { 348 if (is_double_tapping_) {
354 // Finally, give the up event of the double-tap. 349 // Finally, give the up event of the double-tap.
355 DCHECK(double_tap_listener_); 350 DCHECK(double_tap_listener_);
356 handled |= double_tap_listener_->OnDoubleTapEvent(ev); 351 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
357 } else if (always_in_tap_region_) { 352 } else if (all_pointers_within_slop_regions_ &&
353 maximum_pointer_count_ == 1) {
358 if (is_down_candidate_for_repeated_single_tap_) { 354 if (is_down_candidate_for_repeated_single_tap_) {
359 current_single_tap_repeat_count_ = 355 current_single_tap_repeat_count_ =
360 (1 + current_single_tap_repeat_count_) % 356 (1 + current_single_tap_repeat_count_) %
361 single_tap_repeat_interval_; 357 single_tap_repeat_interval_;
362 } else { 358 } else {
363 current_single_tap_repeat_count_ = 0; 359 current_single_tap_repeat_count_ = 0;
364 } 360 }
365 handled = listener_->OnSingleTapUp( 361 handled = listener_->OnSingleTapUp(
366 ev, 1 + current_single_tap_repeat_count_); 362 ev, 1 + current_single_tap_repeat_count_);
367 if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) { 363 if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) {
(...skipping 17 matching lines...) Expand all
385 } 381 }
386 382
387 previous_up_event_ = ev.Clone(); 383 previous_up_event_ = ev.Clone();
388 384
389 velocity_tracker_.Clear(); 385 velocity_tracker_.Clear();
390 is_double_tapping_ = false; 386 is_double_tapping_ = false;
391 defer_confirm_single_tap_ = false; 387 defer_confirm_single_tap_ = false;
392 timeout_handler_->StopTimeout(SHOW_PRESS); 388 timeout_handler_->StopTimeout(SHOW_PRESS);
393 timeout_handler_->StopTimeout(LONG_PRESS); 389 timeout_handler_->StopTimeout(LONG_PRESS);
394 } 390 }
391 maximum_pointer_count_ = 0;
395 break; 392 break;
396 393
397 case MotionEvent::ACTION_CANCEL: 394 case MotionEvent::ACTION_CANCEL:
398 Cancel(); 395 Cancel();
399 break; 396 break;
400 } 397 }
401 398
402 return handled; 399 return handled;
403 } 400 }
404 401
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
470 CHECK(previous_up_event_); 467 CHECK(previous_up_event_);
471 double_tap_listener_->OnSingleTapConfirmed(*previous_up_event_); 468 double_tap_listener_->OnSingleTapConfirmed(*previous_up_event_);
472 } else { 469 } else {
473 defer_confirm_single_tap_ = true; 470 defer_confirm_single_tap_ = true;
474 } 471 }
475 } 472 }
476 473
477 void GestureDetector::Cancel() { 474 void GestureDetector::Cancel() {
478 CancelTaps(); 475 CancelTaps();
479 velocity_tracker_.Clear(); 476 velocity_tracker_.Clear();
477 all_pointers_within_slop_regions_ = false;
480 still_down_ = false; 478 still_down_ = false;
481 } 479 }
482 480
483 void GestureDetector::CancelTaps() { 481 void GestureDetector::CancelTaps() {
484 timeout_handler_->Stop(); 482 timeout_handler_->Stop();
485 is_double_tapping_ = false; 483 is_double_tapping_ = false;
486 always_in_tap_region_ = false;
487 always_in_bigger_tap_region_ = false; 484 always_in_bigger_tap_region_ = false;
488 defer_confirm_single_tap_ = false; 485 defer_confirm_single_tap_ = false;
489 is_down_candidate_for_repeated_single_tap_ = false; 486 is_down_candidate_for_repeated_single_tap_ = false;
490 current_single_tap_repeat_count_ = 0; 487 current_single_tap_repeat_count_ = 0;
491 } 488 }
492 489
493 bool GestureDetector::IsRepeatedTap(const MotionEvent& first_down, 490 bool GestureDetector::IsRepeatedTap(const MotionEvent& first_down,
494 const MotionEvent& first_up, 491 const MotionEvent& first_up,
495 const MotionEvent& second_down) const { 492 const MotionEvent& second_down) const {
496 if (!always_in_bigger_tap_region_) 493 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_) 529 if (ratio < min_swipe_direction_component_ratio_)
533 return false; 530 return false;
534 531
535 if (vx_abs > vy_abs) 532 if (vx_abs > vy_abs)
536 vy = 0; 533 vy = 0;
537 else 534 else
538 vx = 0; 535 vx = 0;
539 return listener_->OnSwipe(*current_down_event_, up, vx, vy); 536 return listener_->OnSwipe(*current_down_event_, up, vx, vy);
540 } 537 }
541 538
539 bool GestureDetector::IsWithinTouchSlop(const MotionEvent& ev) {
540 // If there are more than two down pointers, tapping is not possible.
541 // Slop region check is not needed.
542 if (ev.GetPointerCount() > 2)
543 return false;
544
545 const int id0 = current_down_event_->GetPointerId(0);
546 const int ev_idx0 = ev.GetPointerId(0) == id0 ? 0 : 1;
547
548 // Check if the primary pointer exceeded the slop region.
549 float dx = current_down_event_->GetX() - ev.GetX(ev_idx0);
550 float dy = current_down_event_->GetY() - ev.GetY(ev_idx0);
551 if (dx * dx + dy * dy > touch_slop_square_)
552 return false;
553
554 if (ev.GetPointerCount() == 2 ) {
555 // Check if the secondary pointer exceeded the slop region.
556 const int ev_idx1 = ev_idx0 == 0 ? 1 : 0;
557 const int idx1 = secondary_pointer_down_event_->GetActionIndex();
558 dx = secondary_pointer_down_event_->GetX(idx1) - ev.GetX(ev_idx1);
559 dy = secondary_pointer_down_event_->GetY(idx1) - ev.GetY(ev_idx1);
560 if (dx * dx + dy * dy > touch_slop_square_)
561 return false;
562 }
563
564 return true;
565 }
566
542 } // namespace ui 567 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698