Index: ui/events/gesture_detection/gesture_provider.cc |
diff --git a/ui/events/gesture_detection/gesture_provider.cc b/ui/events/gesture_detection/gesture_provider.cc |
index e38957ab31c4fcefccf0417e5ffe2c042a760b37..c76460e53c63da200bda0dcf3cec80946e79fd0a 100644 |
--- a/ui/events/gesture_detection/gesture_provider.cc |
+++ b/ui/events/gesture_detection/gesture_provider.cc |
@@ -19,13 +19,19 @@ namespace { |
const float kDoubleTapDragZoomSpeed = 0.005f; |
const char* GetMotionEventActionName(MotionEvent::Action action) { |
- switch(action) { |
- case MotionEvent::ACTION_POINTER_DOWN: return "ACTION_POINTER_DOWN"; |
- case MotionEvent::ACTION_POINTER_UP: return "ACTION_POINTER_UP"; |
- case MotionEvent::ACTION_DOWN: return "ACTION_DOWN"; |
- case MotionEvent::ACTION_UP: return "ACTION_UP"; |
- case MotionEvent::ACTION_CANCEL: return "ACTION_CANCEL"; |
- case MotionEvent::ACTION_MOVE: return "ACTION_MOVE"; |
+ switch (action) { |
+ case MotionEvent::ACTION_POINTER_DOWN: |
+ return "ACTION_POINTER_DOWN"; |
+ case MotionEvent::ACTION_POINTER_UP: |
+ return "ACTION_POINTER_UP"; |
+ case MotionEvent::ACTION_DOWN: |
+ return "ACTION_DOWN"; |
+ case MotionEvent::ACTION_UP: |
+ return "ACTION_UP"; |
+ case MotionEvent::ACTION_CANCEL: |
+ return "ACTION_CANCEL"; |
+ case MotionEvent::ACTION_MOVE: |
+ return "ACTION_MOVE"; |
} |
return ""; |
} |
@@ -110,12 +116,11 @@ GestureEventData CreateGesture(EventType type, const MotionEvent& event) { |
return CreateGesture(GestureEventDetails(type, 0, 0), event); |
} |
-GestureEventDetails CreateTapGestureDetails(EventType type) { |
+GestureEventData CreateTapGesture(EventType type, const MotionEvent& event) { |
// Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be |
// consistent with double tap behavior on a mobile viewport. See |
// crbug.com/234986 for context. |
- GestureEventDetails tap_details(type, 1, 0); |
- return tap_details; |
+ return CreateGesture(GestureEventDetails(type, 1, 0), event); |
} |
gfx::RectF ClampBoundingBox(const gfx::RectF& bounds, |
@@ -145,41 +150,128 @@ GestureProvider::Config::Config() |
disable_click_delay(false), |
gesture_begin_end_types_enabled(false), |
min_gesture_bounds_length(0), |
- max_gesture_bounds_length(0) {} |
+ max_gesture_bounds_length(0) { |
+} |
-GestureProvider::Config::~Config() {} |
+GestureProvider::Config::~Config() { |
+} |
-// GestureProvider::ScaleGestureListener |
+// GestureProvider::GestureListener |
-class GestureProvider::ScaleGestureListenerImpl |
- : public ScaleGestureDetector::ScaleGestureListener { |
+class GestureProvider::GestureListenerImpl |
+ : public ScaleGestureDetector::ScaleGestureListener, |
+ public GestureDetector::GestureListener, |
+ public GestureDetector::DoubleTapListener { |
public: |
- ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config, |
- GestureProvider* provider) |
- : scale_gesture_detector_(config, this), |
- provider_(provider), |
- ignore_multitouch_events_(false), |
+ GestureListenerImpl(const GestureProvider::Config& config, |
+ GestureProviderClient* client) |
+ : config_(config), |
+ client_(client), |
+ gesture_detector_(config.gesture_detector_config, this, this), |
+ scale_gesture_detector_(config.scale_gesture_detector_config, this), |
+ snap_scroll_controller_(config.display), |
+ ignore_multitouch_zoom_events_(false), |
+ ignore_single_tap_(false), |
pinch_event_sent_(false), |
- min_pinch_update_span_delta_(config.min_pinch_update_span_delta) {} |
+ scroll_event_sent_(false) {} |
- bool OnTouchEvent(const MotionEvent& event) { |
- // TODO: Need to deal with multi-touch transition. |
+ void OnTouchEvent(const MotionEvent& event) { |
const bool in_scale_gesture = IsScaleGestureDetectionInProgress(); |
- bool handled = scale_gesture_detector_.OnTouchEvent(event); |
- if (!in_scale_gesture && |
- (event.GetAction() == MotionEvent::ACTION_UP || |
- event.GetAction() == MotionEvent::ACTION_CANCEL)) { |
- return false; |
+ snap_scroll_controller_.SetSnapScrollingMode(event, in_scale_gesture); |
+ if (in_scale_gesture) |
+ SetIgnoreSingleTap(true); |
+ |
+ const MotionEvent::Action action = event.GetAction(); |
+ if (action == MotionEvent::ACTION_DOWN) { |
+ current_down_time_ = event.GetEventTime(); |
+ current_longpress_time_ = base::TimeTicks(); |
+ ignore_single_tap_ = false; |
+ scroll_event_sent_ = false; |
+ pinch_event_sent_ = false; |
+ gesture_detector_.set_longpress_enabled(true); |
+ } |
+ |
+ gesture_detector_.OnTouchEvent(event); |
+ scale_gesture_detector_.OnTouchEvent(event); |
+ |
+ if (action == MotionEvent::ACTION_UP || |
+ action == MotionEvent::ACTION_CANCEL) { |
+ // Note: This call will have no effect if a fling was just generated, as |
+ // |Fling()| will have already signalled an end to touch-scrolling. |
+ if (scroll_event_sent_) |
+ Send(CreateGesture(ET_GESTURE_SCROLL_END, event)); |
+ current_down_time_ = base::TimeTicks(); |
} |
- return handled; |
+ } |
+ |
+ void Send(GestureEventData gesture) { |
+ DCHECK(!gesture.time.is_null()); |
+ // The only valid events that should be sent without an active touch |
+ // sequence are SHOW_PRESS and TAP, potentially triggered by the double-tap |
+ // delay timing out. |
+ DCHECK(!current_down_time_.is_null() || gesture.type() == ET_GESTURE_TAP || |
+ gesture.type() == ET_GESTURE_SHOW_PRESS || |
+ gesture.type() == ET_GESTURE_BEGIN || |
+ gesture.type() == ET_GESTURE_END); |
+ |
+ if (gesture.primary_tool_type == MotionEvent::TOOL_TYPE_UNKNOWN || |
+ gesture.primary_tool_type == MotionEvent::TOOL_TYPE_FINGER) { |
+ gesture.details.set_bounding_box( |
+ ClampBoundingBox(gesture.details.bounding_box_f(), |
+ config_.min_gesture_bounds_length, |
+ config_.max_gesture_bounds_length)); |
+ } |
+ |
+ switch (gesture.type()) { |
+ case ET_GESTURE_LONG_PRESS: |
+ DCHECK(!IsScaleGestureDetectionInProgress()); |
+ current_longpress_time_ = gesture.time; |
+ break; |
+ case ET_GESTURE_LONG_TAP: |
+ current_longpress_time_ = base::TimeTicks(); |
+ break; |
+ case ET_GESTURE_SCROLL_BEGIN: |
+ DCHECK(!scroll_event_sent_); |
+ scroll_event_sent_ = true; |
+ break; |
+ case ET_GESTURE_SCROLL_END: |
+ DCHECK(scroll_event_sent_); |
+ if (pinch_event_sent_) |
+ Send(GestureEventData(ET_GESTURE_PINCH_END, gesture)); |
+ scroll_event_sent_ = false; |
+ break; |
+ case ET_SCROLL_FLING_START: |
+ DCHECK(scroll_event_sent_); |
+ scroll_event_sent_ = false; |
+ break; |
+ case ET_GESTURE_PINCH_BEGIN: |
+ DCHECK(!pinch_event_sent_); |
+ if (!scroll_event_sent_) |
+ Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture)); |
+ pinch_event_sent_ = true; |
+ break; |
+ case ET_GESTURE_PINCH_END: |
+ DCHECK(pinch_event_sent_); |
+ pinch_event_sent_ = false; |
+ break; |
+ case ET_GESTURE_SHOW_PRESS: |
+ // It's possible that a double-tap drag zoom (from ScaleGestureDetector) |
+ // will start before the press gesture fires (from GestureDetector), in |
+ // which case the press should simply be dropped. |
+ if (pinch_event_sent_ || scroll_event_sent_) |
+ return; |
+ default: |
+ break; |
+ }; |
+ |
+ client_->OnGestureEvent(gesture); |
} |
// ScaleGestureDetector::ScaleGestureListener implementation. |
virtual bool OnScaleBegin(const ScaleGestureDetector& detector, |
const MotionEvent& e) OVERRIDE { |
- if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) |
+ if (ignore_multitouch_zoom_events_ && !detector.InDoubleTapMode()) |
return false; |
- pinch_event_sent_ = false; |
return true; |
} |
@@ -187,30 +279,28 @@ class GestureProvider::ScaleGestureListenerImpl |
const MotionEvent& e) OVERRIDE { |
if (!pinch_event_sent_) |
return; |
- provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, e)); |
- pinch_event_sent_ = false; |
+ Send(CreateGesture(ET_GESTURE_PINCH_END, e)); |
} |
virtual bool OnScale(const ScaleGestureDetector& detector, |
const MotionEvent& e) OVERRIDE { |
- if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) |
+ if (ignore_multitouch_zoom_events_ && !detector.InDoubleTapMode()) |
return false; |
if (!pinch_event_sent_) { |
- pinch_event_sent_ = true; |
- provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN, |
- e.GetId(), |
- e.GetToolType(), |
- detector.GetEventTime(), |
- detector.GetFocusX(), |
- detector.GetFocusY(), |
- detector.GetFocusX() + e.GetRawOffsetX(), |
- detector.GetFocusY() + e.GetRawOffsetY(), |
- e.GetPointerCount(), |
- GetBoundingBox(e))); |
+ Send(CreateGesture(ET_GESTURE_PINCH_BEGIN, |
+ e.GetId(), |
+ e.GetToolType(), |
+ detector.GetEventTime(), |
+ detector.GetFocusX(), |
+ detector.GetFocusY(), |
+ detector.GetFocusX() + e.GetRawOffsetX(), |
+ detector.GetFocusY() + e.GetRawOffsetY(), |
+ e.GetPointerCount(), |
+ GetBoundingBox(e))); |
} |
if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) < |
- min_pinch_update_span_delta_) { |
+ config_.scale_gesture_detector_config.min_pinch_update_span_delta) { |
return false; |
} |
@@ -231,107 +321,23 @@ class GestureProvider::ScaleGestureListenerImpl |
std::abs(dy)); |
} |
GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0); |
- provider_->Send(CreateGesture(pinch_details, |
- e.GetId(), |
- e.GetToolType(), |
- detector.GetEventTime(), |
- detector.GetFocusX(), |
- detector.GetFocusY(), |
- detector.GetFocusX() + e.GetRawOffsetX(), |
- detector.GetFocusY() + e.GetRawOffsetY(), |
- e.GetPointerCount(), |
- GetBoundingBox(e))); |
+ Send(CreateGesture(pinch_details, |
+ e.GetId(), |
+ e.GetToolType(), |
+ detector.GetEventTime(), |
+ detector.GetFocusX(), |
+ detector.GetFocusY(), |
+ detector.GetFocusX() + e.GetRawOffsetX(), |
+ detector.GetFocusY() + e.GetRawOffsetY(), |
+ e.GetPointerCount(), |
+ GetBoundingBox(e))); |
return true; |
} |
- void SetDoubleTapEnabled(bool enabled) { |
- DCHECK(!IsDoubleTapInProgress()); |
- scale_gesture_detector_.SetQuickScaleEnabled(enabled); |
- } |
- |
- void SetMultiTouchEnabled(bool enabled) { |
- // Note that returning false from OnScaleBegin / OnScale makes the |
- // gesture detector not to emit further scaling notifications |
- // related to this gesture. Thus, if detector events are enabled in |
- // the middle of the gesture, we don't need to do anything. |
- ignore_multitouch_events_ = !enabled; |
- } |
- |
- bool IsDoubleTapInProgress() const { |
- return IsScaleGestureDetectionInProgress() && InDoubleTapMode(); |
- } |
- |
- bool IsScaleGestureDetectionInProgress() const { |
- return scale_gesture_detector_.IsInProgress(); |
- } |
- |
- private: |
- bool InDoubleTapMode() const { |
- return scale_gesture_detector_.InDoubleTapMode(); |
- } |
- |
- ScaleGestureDetector scale_gesture_detector_; |
- |
- GestureProvider* const provider_; |
- |
- // Completely silence multi-touch (pinch) scaling events. Used in WebView when |
- // zoom support is turned off. |
- bool ignore_multitouch_events_; |
- |
- // Whether any pinch zoom event has been sent to native. |
- bool pinch_event_sent_; |
- |
- // The minimum change in span required before this is considered a pinch. See |
- // crbug.com/373318. |
- float min_pinch_update_span_delta_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl); |
-}; |
- |
-// GestureProvider::GestureListener |
- |
-class GestureProvider::GestureListenerImpl |
- : public GestureDetector::GestureListener, |
- public GestureDetector::DoubleTapListener { |
- public: |
- GestureListenerImpl( |
- const gfx::Display& display, |
- const GestureDetector::Config& gesture_detector_config, |
- bool disable_click_delay, |
- GestureProvider* provider) |
- : gesture_detector_(gesture_detector_config, this, this), |
- snap_scroll_controller_(display), |
- provider_(provider), |
- disable_click_delay_(disable_click_delay), |
- touch_slop_(gesture_detector_config.touch_slop), |
- double_tap_timeout_(gesture_detector_config.double_tap_timeout), |
- ignore_single_tap_(false), |
- seen_first_scroll_event_(false) {} |
- |
- virtual ~GestureListenerImpl() {} |
- |
- bool OnTouchEvent(const MotionEvent& e, |
- bool is_scale_gesture_detection_in_progress) { |
- snap_scroll_controller_.SetSnapScrollingMode( |
- e, is_scale_gesture_detection_in_progress); |
- |
- if (is_scale_gesture_detection_in_progress) |
- SetIgnoreSingleTap(true); |
- |
- if (e.GetAction() == MotionEvent::ACTION_DOWN) |
- gesture_detector_.set_longpress_enabled(true); |
- |
- return gesture_detector_.OnTouchEvent(e); |
- } |
- |
// GestureDetector::GestureListener implementation. |
virtual bool OnDown(const MotionEvent& e) OVERRIDE { |
- current_down_time_ = e.GetEventTime(); |
- ignore_single_tap_ = false; |
- seen_first_scroll_event_ = false; |
- |
GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0); |
- provider_->Send(CreateGesture(tap_details, e)); |
+ Send(CreateGesture(tap_details, e)); |
// Return true to indicate that we want to handle touch. |
return true; |
@@ -343,29 +349,21 @@ class GestureProvider::GestureListenerImpl |
float raw_distance_y) OVERRIDE { |
float distance_x = raw_distance_x; |
float distance_y = raw_distance_y; |
- if (!seen_first_scroll_event_) { |
+ if (!scroll_event_sent_) { |
// Remove the touch slop region from the first scroll event to avoid a |
// jump. |
- seen_first_scroll_event_ = true; |
double distance = |
std::sqrt(distance_x * distance_x + distance_y * distance_y); |
double epsilon = 1e-3; |
if (distance > epsilon) { |
- double ratio = std::max(0., distance - touch_slop_) / distance; |
+ double ratio = |
+ std::max(0., |
+ distance - config_.gesture_detector_config.touch_slop) / |
+ distance; |
distance_x *= ratio; |
distance_y *= ratio; |
} |
- } |
- snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y); |
- if (snap_scroll_controller_.IsSnappingScrolls()) { |
- if (snap_scroll_controller_.IsSnapHorizontal()) { |
- distance_y = 0; |
- } else { |
- distance_x = 0; |
- } |
- } |
- if (!provider_->IsScrollInProgress()) { |
// Note that scroll start hints are in distance traveled, where |
// scroll deltas are in the opposite direction. |
GestureEventDetails scroll_details( |
@@ -373,16 +371,25 @@ class GestureProvider::GestureListenerImpl |
// Use the co-ordinates from the touch down, as these co-ordinates are |
// used to determine which layer the scroll should affect. |
- provider_->Send(CreateGesture(scroll_details, |
- e2.GetId(), |
- e2.GetToolType(), |
- e2.GetEventTime(), |
- e1.GetX(), |
- e1.GetY(), |
- e1.GetRawX(), |
- e1.GetRawY(), |
- e2.GetPointerCount(), |
- GetBoundingBox(e2))); |
+ Send(CreateGesture(scroll_details, |
+ e2.GetId(), |
+ e2.GetToolType(), |
+ e2.GetEventTime(), |
+ e1.GetX(), |
+ e1.GetY(), |
+ e1.GetRawX(), |
+ e1.GetRawY(), |
+ e2.GetPointerCount(), |
+ GetBoundingBox(e2))); |
+ DCHECK(scroll_event_sent_); |
+ } |
+ |
+ snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y); |
+ if (snap_scroll_controller_.IsSnappingScrolls()) { |
+ if (snap_scroll_controller_.IsSnapHorizontal()) |
+ distance_y = 0; |
+ else |
+ distance_x = 0; |
} |
if (distance_x || distance_y) { |
@@ -392,16 +399,16 @@ class GestureProvider::GestureListenerImpl |
center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY()); |
GestureEventDetails scroll_details( |
ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y); |
- provider_->Send(CreateGesture(scroll_details, |
- e2.GetId(), |
- e2.GetToolType(), |
- e2.GetEventTime(), |
- center.x(), |
- center.y(), |
- raw_center.x(), |
- raw_center.y(), |
- e2.GetPointerCount(), |
- bounding_box)); |
+ Send(CreateGesture(scroll_details, |
+ e2.GetId(), |
+ e2.GetToolType(), |
+ e2.GetEventTime(), |
+ center.x(), |
+ center.y(), |
+ raw_center.x(), |
+ raw_center.y(), |
+ e2.GetPointerCount(), |
+ bounding_box)); |
} |
return true; |
@@ -419,7 +426,21 @@ class GestureProvider::GestureListenerImpl |
} |
} |
- provider_->Fling(e2, velocity_x, velocity_y); |
+ if (!velocity_x && !velocity_y) |
+ return true; |
+ |
+ if (!scroll_event_sent_) { |
+ // The native side needs a ET_GESTURE_SCROLL_BEGIN before |
+ // ET_SCROLL_FLING_START to send the fling to the correct target. |
+ // The distance traveled in one second is a reasonable scroll start hint. |
+ GestureEventDetails scroll_details( |
+ ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y); |
+ Send(CreateGesture(scroll_details, e2)); |
+ } |
+ |
+ GestureEventDetails fling_details( |
+ ET_SCROLL_FLING_START, velocity_x, velocity_y); |
+ Send(CreateGesture(fling_details, e2)); |
return true; |
} |
@@ -428,7 +449,7 @@ class GestureProvider::GestureListenerImpl |
float velocity_x, |
float velocity_y) OVERRIDE { |
GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y); |
- provider_->Send(CreateGesture(swipe_details, e2)); |
+ Send(CreateGesture(swipe_details, e2)); |
return true; |
} |
@@ -436,25 +457,24 @@ class GestureProvider::GestureListenerImpl |
const MotionEvent& e2) OVERRIDE { |
// The location of the two finger tap event should be the location of the |
// primary pointer. |
- GestureEventDetails two_finger_tap_details(ET_GESTURE_TWO_FINGER_TAP, |
- e1.GetTouchMajor(), |
- e1.GetTouchMajor()); |
- provider_->Send(CreateGesture(two_finger_tap_details, |
- e2.GetId(), |
- e2.GetToolType(), |
- e2.GetEventTime(), |
- e1.GetX(), |
- e1.GetY(), |
- e1.GetRawX(), |
- e1.GetRawY(), |
- e2.GetPointerCount(), |
- GetBoundingBox(e2))); |
+ GestureEventDetails two_finger_tap_details( |
+ ET_GESTURE_TWO_FINGER_TAP, e1.GetTouchMajor(), e1.GetTouchMajor()); |
+ Send(CreateGesture(two_finger_tap_details, |
+ e2.GetId(), |
+ e2.GetToolType(), |
+ e2.GetEventTime(), |
+ e1.GetX(), |
+ e1.GetY(), |
+ e1.GetRawX(), |
+ e1.GetRawY(), |
+ e2.GetPointerCount(), |
+ GetBoundingBox(e2))); |
return true; |
} |
virtual void OnShowPress(const MotionEvent& e) OVERRIDE { |
GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0); |
- provider_->Send(CreateGesture(show_press_details, e)); |
+ Send(CreateGesture(show_press_details, e)); |
} |
virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE { |
@@ -465,21 +485,29 @@ class GestureProvider::GestureListenerImpl |
// OnSingleTapUp() in this case. This assumes singleTapUp |
// gets always called before singleTapConfirmed. |
if (!ignore_single_tap_) { |
- if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) { |
+ if (e.GetEventTime() - current_down_time_ > |
+ config_.gesture_detector_config.double_tap_timeout) { |
return OnSingleTapConfirmed(e); |
- } else if (!IsDoubleTapEnabled() || disable_click_delay_) { |
+ } else if (!IsDoubleTapEnabled() || config_.disable_click_delay) { |
// If double-tap has been disabled, there is no need to wait |
// for the double-tap timeout. |
return OnSingleTapConfirmed(e); |
} else { |
// Notify Blink about this tapUp event anyway, when none of the above |
// conditions applied. |
- provider_->Send(CreateGesture( |
- CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED), e)); |
+ Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED, e)); |
} |
} |
- return provider_->SendLongTapIfNecessary(e); |
+ if (e.GetAction() == MotionEvent::ACTION_UP && |
+ !current_longpress_time_.is_null() && |
+ !IsScaleGestureDetectionInProgress()) { |
+ GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0); |
+ Send(CreateGesture(long_tap_details, e)); |
+ return true; |
+ } |
+ |
+ return false; |
} |
// GestureDetector::DoubleTapListener implementation. |
@@ -493,11 +521,13 @@ class GestureProvider::GestureListenerImpl |
ignore_single_tap_ = true; |
- provider_->Send(CreateGesture(CreateTapGestureDetails(ET_GESTURE_TAP), e)); |
+ Send(CreateTapGesture(ET_GESTURE_TAP, e)); |
return true; |
} |
- virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; } |
+ virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { |
+ return scale_gesture_detector_.OnDoubleTap(e); |
+ } |
virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE { |
switch (e.GetAction()) { |
@@ -506,13 +536,12 @@ class GestureProvider::GestureListenerImpl |
break; |
case MotionEvent::ACTION_UP: |
- if (!provider_->IsPinchInProgress() && |
- !provider_->IsScrollInProgress()) { |
- provider_->Send( |
- CreateGesture(CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP), e)); |
+ if (!IsPinchInProgress() && !IsScrollInProgress()) { |
+ Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP, e)); |
return true; |
} |
break; |
+ |
default: |
break; |
} |
@@ -522,9 +551,8 @@ class GestureProvider::GestureListenerImpl |
virtual void OnLongPress(const MotionEvent& e) OVERRIDE { |
DCHECK(!IsDoubleTapInProgress()); |
SetIgnoreSingleTap(true); |
- |
GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0); |
- provider_->Send(CreateGesture(long_press_details, e)); |
+ Send(CreateGesture(long_press_details, e)); |
} |
void SetDoubleTapEnabled(bool enabled) { |
@@ -532,40 +560,65 @@ class GestureProvider::GestureListenerImpl |
gesture_detector_.SetDoubleTapListener(enabled ? this : NULL); |
} |
+ void SetMultiTouchZoomEnabled(bool enabled) { |
+ // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents |
+ // the detector from emitting further scale updates for the current touch |
+ // sequence. Thus, if multitouch events are enabled in the middle of a |
+ // gesture, it will only take effect with the next gesture. |
+ ignore_multitouch_zoom_events_ = !enabled; |
+ } |
+ |
bool IsDoubleTapInProgress() const { |
- return gesture_detector_.is_double_tapping(); |
+ return gesture_detector_.is_double_tapping() || |
+ (IsScaleGestureDetectionInProgress() && InDoubleTapMode()); |
} |
+ bool IsScrollInProgress() const { return scroll_event_sent_; } |
+ |
+ bool IsPinchInProgress() const { return pinch_event_sent_; } |
+ |
private: |
- void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; } |
+ bool IsScaleGestureDetectionInProgress() const { |
+ return scale_gesture_detector_.IsInProgress(); |
+ } |
+ |
+ bool InDoubleTapMode() const { |
+ return scale_gesture_detector_.InDoubleTapMode(); |
+ } |
bool IsDoubleTapEnabled() const { |
return gesture_detector_.has_doubletap_listener(); |
} |
- GestureDetector gesture_detector_; |
- SnapScrollController snap_scroll_controller_; |
+ void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; } |
- GestureProvider* const provider_; |
+ const GestureProvider::Config config_; |
+ GestureProviderClient* const client_; |
- // Whether the click delay should always be disabled by sending clicks for |
- // double-tap gestures. |
- const bool disable_click_delay_; |
+ GestureDetector gesture_detector_; |
+ ScaleGestureDetector scale_gesture_detector_; |
+ SnapScrollController snap_scroll_controller_; |
- const float touch_slop_; |
+ base::TimeTicks current_down_time_; |
- const base::TimeDelta double_tap_timeout_; |
+ // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is |
+ // opened after a GESTURE_LONG_PRESS, this is used to insert a |
+ // GESTURE_TAP_CANCEL for removing any ::active styling. |
+ base::TimeTicks current_longpress_time_; |
- base::TimeTicks current_down_time_; |
+ // Completely silence multi-touch (pinch) scaling events. Used in WebView when |
+ // zoom support is turned off. |
+ bool ignore_multitouch_zoom_events_; |
// TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch, |
// always_in_tap_region_ is not reset. So when the last finger is up, |
- // OnSingleTapUp() will be mistakenly fired. |
+ // |OnSingleTapUp()| will be mistakenly fired. |
bool ignore_single_tap_; |
- // Used to remove the touch slop from the initial scroll event in a scroll |
- // gesture. |
- bool seen_first_scroll_event_; |
+ // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the |
+ // current touch sequence. |
+ bool pinch_event_sent_; |
+ bool scroll_event_sent_; |
DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl); |
}; |
@@ -574,43 +627,40 @@ class GestureProvider::GestureListenerImpl |
GestureProvider::GestureProvider(const Config& config, |
GestureProviderClient* client) |
- : client_(client), |
- touch_scroll_in_progress_(false), |
- pinch_in_progress_(false), |
- double_tap_support_for_page_(true), |
+ : double_tap_support_for_page_(true), |
double_tap_support_for_platform_(true), |
- gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled), |
- min_gesture_bounds_length_(config.min_gesture_bounds_length), |
- max_gesture_bounds_length_(config.max_gesture_bounds_length) { |
+ gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) { |
DCHECK(client); |
- DCHECK(!min_gesture_bounds_length_ || !max_gesture_bounds_length_ || |
- min_gesture_bounds_length_ <= max_gesture_bounds_length_); |
- InitGestureDetectors(config); |
+ DCHECK(!config.min_gesture_bounds_length || |
+ !config.max_gesture_bounds_length || |
+ config.min_gesture_bounds_length <= config.max_gesture_bounds_length); |
+ TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors"); |
+ gesture_listener_.reset(new GestureListenerImpl(config, client)); |
+ UpdateDoubleTapDetectionSupport(); |
} |
-GestureProvider::~GestureProvider() {} |
+GestureProvider::~GestureProvider() { |
+} |
bool GestureProvider::OnTouchEvent(const MotionEvent& event) { |
- TRACE_EVENT1("input", "GestureProvider::OnTouchEvent", |
- "action", GetMotionEventActionName(event.GetAction())); |
+ TRACE_EVENT1("input", |
+ "GestureProvider::OnTouchEvent", |
+ "action", |
+ GetMotionEventActionName(event.GetAction())); |
DCHECK_NE(0u, event.GetPointerCount()); |
if (!CanHandle(event)) |
return false; |
- const bool in_scale_gesture = |
- scale_gesture_listener_->IsScaleGestureDetectionInProgress(); |
- |
OnTouchEventHandlingBegin(event); |
- gesture_listener_->OnTouchEvent(event, in_scale_gesture); |
- scale_gesture_listener_->OnTouchEvent(event); |
+ gesture_listener_->OnTouchEvent(event); |
OnTouchEventHandlingEnd(event); |
return true; |
} |
void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) { |
- scale_gesture_listener_->SetMultiTouchEnabled(enabled); |
+ gesture_listener_->SetMultiTouchZoomEnabled(enabled); |
} |
void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) { |
@@ -628,165 +678,45 @@ void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) { |
} |
bool GestureProvider::IsScrollInProgress() const { |
- // TODO(wangxianzhu): Also return true when fling is active once the UI knows |
- // exactly when the fling ends. |
- return touch_scroll_in_progress_; |
+ return gesture_listener_->IsScrollInProgress(); |
} |
-bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; } |
- |
-bool GestureProvider::IsDoubleTapInProgress() const { |
- return gesture_listener_->IsDoubleTapInProgress() || |
- scale_gesture_listener_->IsDoubleTapInProgress(); |
+bool GestureProvider::IsPinchInProgress() const { |
+ return gesture_listener_->IsPinchInProgress(); |
} |
-void GestureProvider::InitGestureDetectors(const Config& config) { |
- TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors"); |
- gesture_listener_.reset( |
- new GestureListenerImpl(config.display, |
- config.gesture_detector_config, |
- config.disable_click_delay, |
- this)); |
- |
- scale_gesture_listener_.reset( |
- new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this)); |
- |
- UpdateDoubleTapDetectionSupport(); |
+bool GestureProvider::IsDoubleTapInProgress() const { |
+ return gesture_listener_->IsDoubleTapInProgress(); |
} |
bool GestureProvider::CanHandle(const MotionEvent& event) const { |
// Aura requires one cancel event per touch point, whereas Android requires |
// one cancel event per touch sequence. Thus we need to allow extra cancel |
// events. |
- return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_ || |
+ return current_down_event_ || event.GetAction() == MotionEvent::ACTION_DOWN || |
event.GetAction() == MotionEvent::ACTION_CANCEL; |
} |
-void GestureProvider::Fling(const MotionEvent& event, |
- float velocity_x, |
- float velocity_y) { |
- if (!velocity_x && !velocity_y) { |
- EndTouchScrollIfNecessary(event, true); |
- return; |
- } |
- |
- if (!touch_scroll_in_progress_) { |
- // The native side needs a ET_GESTURE_SCROLL_BEGIN before |
- // ET_SCROLL_FLING_START to send the fling to the correct target. Send if it |
- // has not sent. The distance traveled in one second is a reasonable scroll |
- // start hint. |
- GestureEventDetails scroll_details( |
- ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y); |
- Send(CreateGesture(scroll_details, event)); |
- } |
- EndTouchScrollIfNecessary(event, false); |
- |
- GestureEventDetails fling_details( |
- ET_SCROLL_FLING_START, velocity_x, velocity_y); |
- Send(CreateGesture(fling_details, event)); |
-} |
- |
-void GestureProvider::Send(GestureEventData gesture) { |
- DCHECK(!gesture.time.is_null()); |
- // The only valid events that should be sent without an active touch sequence |
- // are SHOW_PRESS and TAP, potentially triggered by the double-tap |
- // delay timing out. |
- DCHECK(current_down_event_ || gesture.type() == ET_GESTURE_TAP || |
- gesture.type() == ET_GESTURE_SHOW_PRESS || |
- gesture.type() == ET_GESTURE_END); |
- |
- if (gesture.primary_tool_type == MotionEvent::TOOL_TYPE_UNKNOWN || |
- gesture.primary_tool_type == MotionEvent::TOOL_TYPE_FINGER) { |
- gesture.details.set_bounding_box( |
- ClampBoundingBox(gesture.details.bounding_box_f(), |
- min_gesture_bounds_length_, |
- max_gesture_bounds_length_)); |
- } |
- |
- switch (gesture.type()) { |
- case ET_GESTURE_LONG_PRESS: |
- DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress()); |
- current_longpress_time_ = gesture.time; |
- break; |
- case ET_GESTURE_LONG_TAP: |
- current_longpress_time_ = base::TimeTicks(); |
- break; |
- case ET_GESTURE_SCROLL_BEGIN: |
- DCHECK(!touch_scroll_in_progress_); |
- touch_scroll_in_progress_ = true; |
- break; |
- case ET_GESTURE_SCROLL_END: |
- DCHECK(touch_scroll_in_progress_); |
- if (pinch_in_progress_) |
- Send(GestureEventData(ET_GESTURE_PINCH_END, gesture)); |
- touch_scroll_in_progress_ = false; |
- break; |
- case ET_GESTURE_PINCH_BEGIN: |
- DCHECK(!pinch_in_progress_); |
- if (!touch_scroll_in_progress_) |
- Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture)); |
- pinch_in_progress_ = true; |
- break; |
- case ET_GESTURE_PINCH_END: |
- DCHECK(pinch_in_progress_); |
- pinch_in_progress_ = false; |
- break; |
- case ET_GESTURE_SHOW_PRESS: |
- // It's possible that a double-tap drag zoom (from ScaleGestureDetector) |
- // will start before the press gesture fires (from GestureDetector), in |
- // which case the press should simply be dropped. |
- if (pinch_in_progress_ || touch_scroll_in_progress_) |
- return; |
- default: |
- break; |
- }; |
- |
- client_->OnGestureEvent(gesture); |
-} |
- |
-bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) { |
- if (event.GetAction() == MotionEvent::ACTION_UP && |
- !current_longpress_time_.is_null() && |
- !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) { |
- GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0); |
- Send(CreateGesture(long_tap_details, event)); |
- return true; |
- } |
- return false; |
-} |
- |
-void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event, |
- bool send_scroll_end_event) { |
- if (!touch_scroll_in_progress_) |
- return; |
- if (send_scroll_end_event) |
- Send(CreateGesture(ET_GESTURE_SCROLL_END, event)); |
- touch_scroll_in_progress_ = false; |
-} |
- |
void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) { |
switch (event.GetAction()) { |
case MotionEvent::ACTION_DOWN: |
current_down_event_ = event.Clone(); |
- touch_scroll_in_progress_ = false; |
- pinch_in_progress_ = false; |
- current_longpress_time_ = base::TimeTicks(); |
if (gesture_begin_end_types_enabled_) |
- Send(CreateGesture(ET_GESTURE_BEGIN, event)); |
+ gesture_listener_->Send(CreateGesture(ET_GESTURE_BEGIN, event)); |
break; |
case MotionEvent::ACTION_POINTER_DOWN: |
if (gesture_begin_end_types_enabled_) { |
const int action_index = event.GetActionIndex(); |
- Send(CreateGesture(ET_GESTURE_BEGIN, |
- event.GetId(), |
- event.GetToolType(), |
- event.GetEventTime(), |
- event.GetX(action_index), |
- event.GetY(action_index), |
- event.GetRawX(action_index), |
- event.GetRawY(action_index), |
- event.GetPointerCount(), |
- GetBoundingBox(event))); |
+ gesture_listener_->Send(CreateGesture(ET_GESTURE_BEGIN, |
+ event.GetId(), |
+ event.GetToolType(), |
+ event.GetEventTime(), |
+ event.GetX(action_index), |
+ event.GetY(action_index), |
+ event.GetRawX(action_index), |
+ event.GetRawY(action_index), |
+ event.GetPointerCount(), |
+ GetBoundingBox(event))); |
} |
break; |
case MotionEvent::ACTION_POINTER_UP: |
@@ -801,12 +731,8 @@ void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) { |
switch (event.GetAction()) { |
case MotionEvent::ACTION_UP: |
case MotionEvent::ACTION_CANCEL: { |
- // Note: This call will have no effect if a fling was just generated, as |
- // |Fling()| will have already signalled an end to touch-scrolling. |
- EndTouchScrollIfNecessary(event, true); |
- |
if (gesture_begin_end_types_enabled_) |
- Send(CreateGesture(ET_GESTURE_END, event)); |
+ gesture_listener_->Send(CreateGesture(ET_GESTURE_END, event)); |
current_down_event_.reset(); |
@@ -815,7 +741,7 @@ void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) { |
} |
case MotionEvent::ACTION_POINTER_UP: |
if (gesture_begin_end_types_enabled_) |
- Send(CreateGesture(ET_GESTURE_END, event)); |
+ gesture_listener_->Send(CreateGesture(ET_GESTURE_END, event)); |
break; |
case MotionEvent::ACTION_DOWN: |
case MotionEvent::ACTION_POINTER_DOWN: |
@@ -831,10 +757,9 @@ void GestureProvider::UpdateDoubleTapDetectionSupport() { |
if (current_down_event_) |
return; |
- const bool double_tap_enabled = double_tap_support_for_page_ && |
- double_tap_support_for_platform_; |
+ const bool double_tap_enabled = |
+ double_tap_support_for_page_ && double_tap_support_for_platform_; |
gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); |
- scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled); |
} |
} // namespace ui |