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. 4 5 #include "ui/events/gesture_detection/gesture_provider.h" 6 7 #include <cmath> 8 9 #include "base/auto_reset.h" 10 #include "base/debug/trace_event.h" 11 #include "ui/events/event_constants.h" 12 #include "ui/events/gesture_detection/gesture_event_data.h" 13 #include "ui/events/gesture_detection/motion_event.h" 14 15 namespace ui { 16 namespace { 17 18 // Double-tap drag zoom sensitivity (speed). 19 const float kDoubleTapDragZoomSpeed = 0.005f; 20 21 const char* GetMotionEventActionName(MotionEvent::Action action) { 22 switch(action) { 23 case MotionEvent::ACTION_POINTER_DOWN: return "ACTION_POINTER_DOWN"; 24 case MotionEvent::ACTION_POINTER_UP: return "ACTION_POINTER_UP"; 25 case MotionEvent::ACTION_DOWN: return "ACTION_DOWN"; 26 case MotionEvent::ACTION_UP: return "ACTION_UP"; 27 case MotionEvent::ACTION_CANCEL: return "ACTION_CANCEL"; 28 case MotionEvent::ACTION_MOVE: return "ACTION_MOVE"; 29 } 30 return ""; 31 } 32 33 gfx::RectF GetBoundingBox(const MotionEvent& event) { 34 gfx::RectF bounds; 35 for (size_t i = 0; i < event.GetPointerCount(); ++i) { 36 float diameter = event.GetTouchMajor(i); 37 bounds.Union(gfx::RectF(event.GetX(i) - diameter / 2, 38 event.GetY(i) - diameter / 2, 39 diameter, 40 diameter)); 41 } 42 return bounds; 43 } 44 45 GestureEventData CreateGesture(const GestureEventDetails& details, 46 int motion_event_id, 47 base::TimeTicks time, 48 float x, 49 float y, 50 float raw_x, 51 float raw_y, 52 size_t touch_point_count, 53 const gfx::RectF& bounding_box) { 54 return GestureEventData(details, 55 motion_event_id, 56 time, 57 x, 58 y, 59 raw_x, 60 raw_y, 61 touch_point_count, 62 bounding_box); 63 } 64 65 GestureEventData CreateGesture(EventType type, 66 int motion_event_id, 67 base::TimeTicks time, 68 float x, 69 float y, 70 float raw_x, 71 float raw_y, 72 size_t touch_point_count, 73 const gfx::RectF& bounding_box) { 74 return GestureEventData(GestureEventDetails(type, 0, 0), 75 motion_event_id, 76 time, 77 x, 78 y, 79 raw_x, 80 raw_y, 81 touch_point_count, 82 bounding_box); 83 } 84 85 GestureEventData CreateGesture(const GestureEventDetails& details, 86 const MotionEvent& event) { 87 return GestureEventData(details, 88 event.GetId(), 89 event.GetEventTime(), 90 event.GetX(), 91 event.GetY(), 92 event.GetRawX(), 93 event.GetRawY(), 94 event.GetPointerCount(), 95 GetBoundingBox(event)); 96 } 97 98 GestureEventData CreateGesture(EventType type, const MotionEvent& event) { 99 return CreateGesture(GestureEventDetails(type, 0, 0), event); 100 } 101 102 GestureEventDetails CreateTapGestureDetails(EventType type) { 103 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be 104 // consistent with double tap behavior on a mobile viewport. See 105 // crbug.com/234986 for context. 106 GestureEventDetails tap_details(type, 1, 0); 107 return tap_details; 108 } 109 110 } // namespace 111 112 // GestureProvider:::Config 113 114 GestureProvider::Config::Config() 115 : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)), 116 disable_click_delay(false), 117 gesture_begin_end_types_enabled(false), 118 min_gesture_bounds_length(0) {} 119 120 GestureProvider::Config::~Config() {} 121 122 // GestureProvider::ScaleGestureListener 123 124 class GestureProvider::ScaleGestureListenerImpl 125 : public ScaleGestureDetector::ScaleGestureListener { 126 public: 127 ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config, 128 GestureProvider* provider) 129 : scale_gesture_detector_(config, this), 130 provider_(provider), 131 ignore_multitouch_events_(false), 132 pinch_event_sent_(false), 133 min_pinch_update_span_delta_(config.min_pinch_update_span_delta) {} 134 135 bool OnTouchEvent(const MotionEvent& event) { 136 // TODO: Need to deal with multi-touch transition. 137 const bool in_scale_gesture = IsScaleGestureDetectionInProgress(); 138 bool handled = scale_gesture_detector_.OnTouchEvent(event); 139 if (!in_scale_gesture && 140 (event.GetAction() == MotionEvent::ACTION_UP || 141 event.GetAction() == MotionEvent::ACTION_CANCEL)) { 142 return false; 143 } 144 return handled; 145 } 146 147 // ScaleGestureDetector::ScaleGestureListener implementation. 148 virtual bool OnScaleBegin(const ScaleGestureDetector& detector, 149 const MotionEvent& e) OVERRIDE { 150 if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) 151 return false; 152 pinch_event_sent_ = false; 153 return true; 154 } 155 156 virtual void OnScaleEnd(const ScaleGestureDetector& detector, 157 const MotionEvent& e) OVERRIDE { 158 if (!pinch_event_sent_) 159 return; 160 provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, e)); 161 pinch_event_sent_ = false; 162 } 163 164 virtual bool OnScale(const ScaleGestureDetector& detector, 165 const MotionEvent& e) OVERRIDE { 166 if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) 167 return false; 168 if (!pinch_event_sent_) { 169 pinch_event_sent_ = true; 170 provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN, 171 e.GetId(), 172 detector.GetEventTime(), 173 detector.GetFocusX(), 174 detector.GetFocusY(), 175 detector.GetFocusX() + e.GetRawOffsetX(), 176 detector.GetFocusY() + e.GetRawOffsetY(), 177 e.GetPointerCount(), 178 GetBoundingBox(e))); 179 } 180 181 if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) < 182 min_pinch_update_span_delta_) { 183 return false; 184 } 185 186 float scale = detector.GetScaleFactor(); 187 if (scale == 1) 188 return true; 189 190 if (detector.InDoubleTapMode()) { 191 // Relative changes in the double-tap scale factor computed by |detector| 192 // diminish as the touch moves away from the original double-tap focus. 193 // For historical reasons, Chrome has instead adopted a scale factor 194 // computation that is invariant to the focal distance, where 195 // the scale delta remains constant if the touch velocity is constant. 196 float dy = 197 (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f; 198 scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed 199 : 1.0f - kDoubleTapDragZoomSpeed, 200 std::abs(dy)); 201 } 202 GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0); 203 provider_->Send(CreateGesture(pinch_details, 204 e.GetId(), 205 detector.GetEventTime(), 206 detector.GetFocusX(), 207 detector.GetFocusY(), 208 detector.GetFocusX() + e.GetRawOffsetX(), 209 detector.GetFocusY() + e.GetRawOffsetY(), 210 e.GetPointerCount(), 211 GetBoundingBox(e))); 212 return true; 213 } 214 215 void SetDoubleTapEnabled(bool enabled) { 216 DCHECK(!IsDoubleTapInProgress()); 217 scale_gesture_detector_.SetQuickScaleEnabled(enabled); 218 } 219 220 void SetMultiTouchEnabled(bool enabled) { 221 // Note that returning false from OnScaleBegin / OnScale makes the 222 // gesture detector not to emit further scaling notifications 223 // related to this gesture. Thus, if detector events are enabled in 224 // the middle of the gesture, we don't need to do anything. 225 ignore_multitouch_events_ = !enabled; 226 } 227 228 bool IsDoubleTapInProgress() const { 229 return IsScaleGestureDetectionInProgress() && InDoubleTapMode(); 230 } 231 232 bool IsScaleGestureDetectionInProgress() const { 233 return scale_gesture_detector_.IsInProgress(); 234 } 235 236 private: 237 bool InDoubleTapMode() const { 238 return scale_gesture_detector_.InDoubleTapMode(); 239 } 240 241 ScaleGestureDetector scale_gesture_detector_; 242 243 GestureProvider* const provider_; 244 245 // Completely silence multi-touch (pinch) scaling events. Used in WebView when 246 // zoom support is turned off. 247 bool ignore_multitouch_events_; 248 249 // Whether any pinch zoom event has been sent to native. 250 bool pinch_event_sent_; 251 252 // The minimum change in span required before this is considered a pinch. See 253 // crbug.com/373318. 254 float min_pinch_update_span_delta_; 255 256 DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl); 257 }; 258 259 // GestureProvider::GestureListener 260 261 class GestureProvider::GestureListenerImpl 262 : public GestureDetector::GestureListener, 263 public GestureDetector::DoubleTapListener { 264 public: 265 GestureListenerImpl( 266 const gfx::Display& display, 267 const GestureDetector::Config& gesture_detector_config, 268 bool disable_click_delay, 269 GestureProvider* provider) 270 : gesture_detector_(gesture_detector_config, this, this), 271 snap_scroll_controller_(display), 272 provider_(provider), 273 disable_click_delay_(disable_click_delay), 274 touch_slop_(gesture_detector_config.touch_slop), 275 double_tap_timeout_(gesture_detector_config.double_tap_timeout), 276 ignore_single_tap_(false), 277 seen_first_scroll_event_(false) {} 278 279 virtual ~GestureListenerImpl() {} 280 281 bool OnTouchEvent(const MotionEvent& e, 282 bool is_scale_gesture_detection_in_progress) { 283 snap_scroll_controller_.SetSnapScrollingMode( 284 e, is_scale_gesture_detection_in_progress); 285 286 if (is_scale_gesture_detection_in_progress) 287 SetIgnoreSingleTap(true); 288 289 if (e.GetAction() == MotionEvent::ACTION_DOWN) 290 gesture_detector_.set_longpress_enabled(true); 291 292 return gesture_detector_.OnTouchEvent(e); 293 } 294 295 // GestureDetector::GestureListener implementation. 296 virtual bool OnDown(const MotionEvent& e) OVERRIDE { 297 current_down_time_ = e.GetEventTime(); 298 ignore_single_tap_ = false; 299 seen_first_scroll_event_ = false; 300 301 GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0); 302 provider_->Send(CreateGesture(tap_details, e)); 303 304 // Return true to indicate that we want to handle touch. 305 return true; 306 } 307 308 virtual bool OnScroll(const MotionEvent& e1, 309 const MotionEvent& e2, 310 float raw_distance_x, 311 float raw_distance_y) OVERRIDE { 312 float distance_x = raw_distance_x; 313 float distance_y = raw_distance_y; 314 if (!seen_first_scroll_event_) { 315 // Remove the touch slop region from the first scroll event to avoid a 316 // jump. 317 seen_first_scroll_event_ = true; 318 double distance = 319 std::sqrt(distance_x * distance_x + distance_y * distance_y); 320 double epsilon = 1e-3; 321 if (distance > epsilon) { 322 double ratio = std::max(0., distance - touch_slop_) / distance; 323 distance_x *= ratio; 324 distance_y *= ratio; 325 } 326 } 327 snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y); 328 if (snap_scroll_controller_.IsSnappingScrolls()) { 329 if (snap_scroll_controller_.IsSnapHorizontal()) { 330 distance_y = 0; 331 } else { 332 distance_x = 0; 333 } 334 } 335 336 if (!provider_->IsScrollInProgress()) { 337 // Note that scroll start hints are in distance traveled, where 338 // scroll deltas are in the opposite direction. 339 GestureEventDetails scroll_details( 340 ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y); 341 342 // Use the co-ordinates from the touch down, as these co-ordinates are 343 // used to determine which layer the scroll should affect. 344 provider_->Send(CreateGesture(scroll_details, 345 e2.GetId(), 346 e2.GetEventTime(), 347 e1.GetX(), 348 e1.GetY(), 349 e1.GetRawX(), 350 e1.GetRawY(), 351 e2.GetPointerCount(), 352 GetBoundingBox(e2))); 353 } 354 355 if (distance_x || distance_y) { 356 const gfx::RectF bounding_box = GetBoundingBox(e2); 357 const gfx::PointF center = bounding_box.CenterPoint(); 358 const gfx::PointF raw_center = 359 center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY()); 360 GestureEventDetails scroll_details( 361 ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y); 362 provider_->Send(CreateGesture(scroll_details, 363 e2.GetId(), 364 e2.GetEventTime(), 365 center.x(), 366 center.y(), 367 raw_center.x(), 368 raw_center.y(), 369 e2.GetPointerCount(), 370 bounding_box)); 371 } 372 373 return true; 374 } 375 376 virtual bool OnFling(const MotionEvent& e1, 377 const MotionEvent& e2, 378 float velocity_x, 379 float velocity_y) OVERRIDE { 380 if (snap_scroll_controller_.IsSnappingScrolls()) { 381 if (snap_scroll_controller_.IsSnapHorizontal()) { 382 velocity_y = 0; 383 } else { 384 velocity_x = 0; 385 } 386 } 387 388 provider_->Fling(e2, velocity_x, velocity_y); 389 return true; 390 } 391 392 virtual bool OnSwipe(const MotionEvent& e1, 393 const MotionEvent& e2, 394 float velocity_x, 395 float velocity_y) OVERRIDE { 396 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y); 397 provider_->Send(CreateGesture(swipe_details, e2)); 398 return true; 399 } 400 401 virtual bool OnTwoFingerTap(const MotionEvent& e1, 402 const MotionEvent& e2) OVERRIDE { 403 // The location of the two finger tap event should be the location of the 404 // primary pointer. 405 GestureEventDetails two_finger_tap_details(ET_GESTURE_TWO_FINGER_TAP, 406 e1.GetTouchMajor(), 407 e1.GetTouchMajor()); 408 provider_->Send(CreateGesture(two_finger_tap_details, 409 e2.GetId(), 410 e2.GetEventTime(), 411 e1.GetX(), 412 e1.GetY(), 413 e1.GetRawX(), 414 e1.GetRawY(), 415 e2.GetPointerCount(), 416 GetBoundingBox(e2))); 417 return true; 418 } 419 420 virtual void OnShowPress(const MotionEvent& e) OVERRIDE { 421 GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0); 422 provider_->Send(CreateGesture(show_press_details, e)); 423 } 424 425 virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE { 426 // This is a hack to address the issue where user hovers 427 // over a link for longer than double_tap_timeout_, then 428 // OnSingleTapConfirmed() is not triggered. But we still 429 // want to trigger the tap event at UP. So we override 430 // OnSingleTapUp() in this case. This assumes singleTapUp 431 // gets always called before singleTapConfirmed. 432 if (!ignore_single_tap_) { 433 if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) { 434 return OnSingleTapConfirmed(e); 435 } else if (!IsDoubleTapEnabled() || disable_click_delay_) { 436 // If double-tap has been disabled, there is no need to wait 437 // for the double-tap timeout. 438 return OnSingleTapConfirmed(e); 439 } else { 440 // Notify Blink about this tapUp event anyway, when none of the above 441 // conditions applied. 442 provider_->Send(CreateGesture( 443 CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED), e)); 444 } 445 } 446 447 return provider_->SendLongTapIfNecessary(e); 448 } 449 450 // GestureDetector::DoubleTapListener implementation. 451 virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE { 452 // Long taps in the edges of the screen have their events delayed by 453 // ContentViewHolder for tab swipe operations. As a consequence of the delay 454 // this method might be called after receiving the up event. 455 // These corner cases should be ignored. 456 if (ignore_single_tap_) 457 return true; 458 459 ignore_single_tap_ = true; 460 461 provider_->Send(CreateGesture(CreateTapGestureDetails(ET_GESTURE_TAP), e)); 462 return true; 463 } 464 465 virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; } 466 467 virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE { 468 switch (e.GetAction()) { 469 case MotionEvent::ACTION_DOWN: 470 gesture_detector_.set_longpress_enabled(false); 471 break; 472 473 case MotionEvent::ACTION_UP: 474 if (!provider_->IsPinchInProgress() && 475 !provider_->IsScrollInProgress()) { 476 provider_->Send( 477 CreateGesture(CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP), e)); 478 return true; 479 } 480 break; 481 default: 482 break; 483 } 484 return false; 485 } 486 487 virtual void OnLongPress(const MotionEvent& e) OVERRIDE { 488 DCHECK(!IsDoubleTapInProgress()); 489 SetIgnoreSingleTap(true); 490 491 GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0); 492 provider_->Send(CreateGesture(long_press_details, e)); 493 } 494 495 void SetDoubleTapEnabled(bool enabled) { 496 DCHECK(!IsDoubleTapInProgress()); 497 gesture_detector_.SetDoubleTapListener(enabled ? this : NULL); 498 } 499 500 bool IsDoubleTapInProgress() const { 501 return gesture_detector_.is_double_tapping(); 502 } 503 504 private: 505 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; } 506 507 bool IsDoubleTapEnabled() const { 508 return gesture_detector_.has_doubletap_listener(); 509 } 510 511 GestureDetector gesture_detector_; 512 SnapScrollController snap_scroll_controller_; 513 514 GestureProvider* const provider_; 515 516 // Whether the click delay should always be disabled by sending clicks for 517 // double-tap gestures. 518 const bool disable_click_delay_; 519 520 const float touch_slop_; 521 522 const base::TimeDelta double_tap_timeout_; 523 524 base::TimeTicks current_down_time_; 525 526 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch, 527 // always_in_tap_region_ is not reset. So when the last finger is up, 528 // OnSingleTapUp() will be mistakenly fired. 529 bool ignore_single_tap_; 530 531 // Used to remove the touch slop from the initial scroll event in a scroll 532 // gesture. 533 bool seen_first_scroll_event_; 534 535 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl); 536 }; 537 538 // GestureProvider 539 540 GestureProvider::GestureProvider(const Config& config, 541 GestureProviderClient* client) 542 : client_(client), 543 touch_scroll_in_progress_(false), 544 pinch_in_progress_(false), 545 double_tap_support_for_page_(true), 546 double_tap_support_for_platform_(true), 547 gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled), 548 min_gesture_bounds_length_(config.min_gesture_bounds_length) { 549 DCHECK(client); 550 InitGestureDetectors(config); 551 } 552 553 GestureProvider::~GestureProvider() {} 554 555 bool GestureProvider::OnTouchEvent(const MotionEvent& event) { 556 TRACE_EVENT1("input", "GestureProvider::OnTouchEvent", 557 "action", GetMotionEventActionName(event.GetAction())); 558 559 DCHECK_NE(0u, event.GetPointerCount()); 560 561 if (!CanHandle(event)) 562 return false; 563 564 const bool in_scale_gesture = 565 scale_gesture_listener_->IsScaleGestureDetectionInProgress(); 566 567 OnTouchEventHandlingBegin(event); 568 gesture_listener_->OnTouchEvent(event, in_scale_gesture); 569 scale_gesture_listener_->OnTouchEvent(event); 570 OnTouchEventHandlingEnd(event); 571 return true; 572 } 573 574 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) { 575 scale_gesture_listener_->SetMultiTouchEnabled(enabled); 576 } 577 578 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) { 579 if (double_tap_support_for_platform_ == enabled) 580 return; 581 double_tap_support_for_platform_ = enabled; 582 UpdateDoubleTapDetectionSupport(); 583 } 584 585 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) { 586 if (double_tap_support_for_page_ == enabled) 587 return; 588 double_tap_support_for_page_ = enabled; 589 UpdateDoubleTapDetectionSupport(); 590 } 591 592 bool GestureProvider::IsScrollInProgress() const { 593 // TODO(wangxianzhu): Also return true when fling is active once the UI knows 594 // exactly when the fling ends. 595 return touch_scroll_in_progress_; 596 } 597 598 bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; } 599 600 bool GestureProvider::IsDoubleTapInProgress() const { 601 return gesture_listener_->IsDoubleTapInProgress() || 602 scale_gesture_listener_->IsDoubleTapInProgress(); 603 } 604 605 void GestureProvider::InitGestureDetectors(const Config& config) { 606 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors"); 607 gesture_listener_.reset( 608 new GestureListenerImpl(config.display, 609 config.gesture_detector_config, 610 config.disable_click_delay, 611 this)); 612 613 scale_gesture_listener_.reset( 614 new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this)); 615 616 UpdateDoubleTapDetectionSupport(); 617 } 618 619 bool GestureProvider::CanHandle(const MotionEvent& event) const { 620 return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_; 621 } 622 623 void GestureProvider::Fling(const MotionEvent& event, 624 float velocity_x, 625 float velocity_y) { 626 if (!velocity_x && !velocity_y) { 627 EndTouchScrollIfNecessary(event, true); 628 return; 629 } 630 631 if (!touch_scroll_in_progress_) { 632 // The native side needs a ET_GESTURE_SCROLL_BEGIN before 633 // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it 634 // has not sent. The distance traveled in one second is a reasonable scroll 635 // start hint. 636 GestureEventDetails scroll_details( 637 ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y); 638 Send(CreateGesture(scroll_details, event)); 639 } 640 EndTouchScrollIfNecessary(event, false); 641 642 GestureEventDetails fling_details( 643 ET_SCROLL_FLING_START, velocity_x, velocity_y); 644 Send(CreateGesture(fling_details, event)); 645 } 646 647 void GestureProvider::Send(GestureEventData gesture) { 648 DCHECK(!gesture.time.is_null()); 649 // The only valid events that should be sent without an active touch sequence 650 // are SHOW_PRESS and TAP, potentially triggered by the double-tap 651 // delay timing out. 652 DCHECK(current_down_event_ || gesture.type() == ET_GESTURE_TAP || 653 gesture.type() == ET_GESTURE_SHOW_PRESS); 654 655 // TODO(jdduke): Provide a way of skipping this clamping for stylus and/or 656 // mouse-based input, perhaps by exposing the source type on MotionEvent. 657 const gfx::RectF& gesture_bounds = gesture.details.bounding_box_f(); 658 gesture.details.set_bounding_box(gfx::RectF( 659 gesture_bounds.x(), 660 gesture_bounds.y(), 661 std::max(min_gesture_bounds_length_, gesture_bounds.width()), 662 std::max(min_gesture_bounds_length_, gesture_bounds.height()))); 663 664 switch (gesture.type()) { 665 case ET_GESTURE_LONG_PRESS: 666 DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress()); 667 current_longpress_time_ = gesture.time; 668 break; 669 case ET_GESTURE_LONG_TAP: 670 current_longpress_time_ = base::TimeTicks(); 671 break; 672 case ET_GESTURE_SCROLL_BEGIN: 673 DCHECK(!touch_scroll_in_progress_); 674 touch_scroll_in_progress_ = true; 675 break; 676 case ET_GESTURE_SCROLL_END: 677 DCHECK(touch_scroll_in_progress_); 678 if (pinch_in_progress_) 679 Send(GestureEventData(ET_GESTURE_PINCH_END, gesture)); 680 touch_scroll_in_progress_ = false; 681 break; 682 case ET_GESTURE_PINCH_BEGIN: 683 DCHECK(!pinch_in_progress_); 684 if (!touch_scroll_in_progress_) 685 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture)); 686 pinch_in_progress_ = true; 687 break; 688 case ET_GESTURE_PINCH_END: 689 DCHECK(pinch_in_progress_); 690 pinch_in_progress_ = false; 691 break; 692 case ET_GESTURE_SHOW_PRESS: 693 // It's possible that a double-tap drag zoom (from ScaleGestureDetector) 694 // will start before the press gesture fires (from GestureDetector), in 695 // which case the press should simply be dropped. 696 if (pinch_in_progress_ || touch_scroll_in_progress_) 697 return; 698 default: 699 break; 700 }; 701 702 client_->OnGestureEvent(gesture); 703 } 704 705 bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) { 706 if (event.GetAction() == MotionEvent::ACTION_UP && 707 !current_longpress_time_.is_null() && 708 !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) { 709 GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0); 710 Send(CreateGesture(long_tap_details, event)); 711 return true; 712 } 713 return false; 714 } 715 716 void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event, 717 bool send_scroll_end_event) { 718 if (!touch_scroll_in_progress_) 719 return; 720 if (send_scroll_end_event) 721 Send(CreateGesture(ET_GESTURE_SCROLL_END, event)); 722 touch_scroll_in_progress_ = false; 723 } 724 725 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) { 726 switch (event.GetAction()) { 727 case MotionEvent::ACTION_DOWN: 728 current_down_event_ = event.Clone(); 729 touch_scroll_in_progress_ = false; 730 pinch_in_progress_ = false; 731 current_longpress_time_ = base::TimeTicks(); 732 if (gesture_begin_end_types_enabled_) 733 Send(CreateGesture(ET_GESTURE_BEGIN, event)); 734 break; 735 case MotionEvent::ACTION_POINTER_DOWN: 736 if (gesture_begin_end_types_enabled_) { 737 const int action_index = event.GetActionIndex(); 738 Send(CreateGesture(ET_GESTURE_BEGIN, 739 event.GetId(), 740 event.GetEventTime(), 741 event.GetX(action_index), 742 event.GetY(action_index), 743 event.GetRawX(action_index), 744 event.GetRawY(action_index), 745 event.GetPointerCount(), 746 GetBoundingBox(event))); 747 } 748 break; 749 case MotionEvent::ACTION_POINTER_UP: 750 case MotionEvent::ACTION_UP: 751 case MotionEvent::ACTION_CANCEL: 752 case MotionEvent::ACTION_MOVE: 753 break; 754 } 755 } 756 757 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) { 758 switch (event.GetAction()) { 759 case MotionEvent::ACTION_UP: 760 case MotionEvent::ACTION_CANCEL: { 761 // Note: This call will have no effect if a fling was just generated, as 762 // |Fling()| will have already signalled an end to touch-scrolling. 763 EndTouchScrollIfNecessary(event, true); 764 765 const gfx::RectF bounding_box = GetBoundingBox(event); 766 767 if (gesture_begin_end_types_enabled_) { 768 for (size_t i = 0; i < event.GetPointerCount(); ++i) { 769 Send(CreateGesture(ET_GESTURE_END, 770 event.GetId(), 771 event.GetEventTime(), 772 event.GetX(i), 773 event.GetY(i), 774 event.GetRawX(i), 775 event.GetRawY(i), 776 event.GetPointerCount() - i, 777 bounding_box)); 778 } 779 } 780 781 current_down_event_.reset(); 782 783 UpdateDoubleTapDetectionSupport(); 784 break; 785 } 786 case MotionEvent::ACTION_POINTER_UP: 787 if (gesture_begin_end_types_enabled_) 788 Send(CreateGesture(ET_GESTURE_END, event)); 789 break; 790 case MotionEvent::ACTION_DOWN: 791 case MotionEvent::ACTION_POINTER_DOWN: 792 case MotionEvent::ACTION_MOVE: 793 break; 794 } 795 } 796 797 void GestureProvider::UpdateDoubleTapDetectionSupport() { 798 // The GestureDetector requires that any provided DoubleTapListener remain 799 // attached to it for the duration of a touch sequence. Defer any potential 800 // null'ing of the listener until the sequence has ended. 801 if (current_down_event_) 802 return; 803 804 const bool double_tap_enabled = double_tap_support_for_page_ && 805 double_tap_support_for_platform_; 806 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); 807 scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); 808 } 809 810 } // namespace ui 811