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 4deeba2580f3a7a72928cd3cec30f4a401aa2f18..d5a617f4d716b0370b5e415034cd23dd67708faf 100644 |
--- a/ui/events/gesture_detection/gesture_provider.cc |
+++ b/ui/events/gesture_detection/gesture_provider.cc |
@@ -43,7 +43,7 @@ GestureEventData CreateGesture(EventType type, |
float x, |
float y) { |
return GestureEventData(type, time, x, y); |
- } |
+} |
GestureEventData CreateGesture(EventType type, |
const MotionEvent& event, |
@@ -57,10 +57,6 @@ GestureEventData CreateGesture(EventType type, |
return CreateGesture(type, event.GetEventTime(), event.GetX(), event.GetY()); |
} |
-float Round(float f) { |
- return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f); |
-} |
- |
GestureEventDetails CreateTapGestureDetails(EventType type, |
const MotionEvent& event) { |
// Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be |
@@ -86,10 +82,12 @@ class GestureProvider::ScaleGestureListenerImpl |
: public ScaleGestureDetector::ScaleGestureListener { |
public: |
ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config, |
+ float device_scale_factor, |
GestureProvider* provider) |
: scale_gesture_detector_(config, this), |
provider_(provider), |
- ignore_detector_events_(false), |
+ px_to_dp_(1.0f / device_scale_factor), |
+ ignore_multitouch_events_(false), |
pinch_event_sent_(false) {} |
bool OnTouchEvent(const MotionEvent& event) { |
@@ -106,7 +104,7 @@ class GestureProvider::ScaleGestureListenerImpl |
// ScaleGestureDetector::ScaleGestureListener implementation. |
virtual bool OnScaleBegin(const ScaleGestureDetector& detector) OVERRIDE { |
- if (ignore_detector_events_) |
+ if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) |
return false; |
pinch_event_sent_ = false; |
return true; |
@@ -121,7 +119,7 @@ class GestureProvider::ScaleGestureListenerImpl |
} |
virtual bool OnScale(const ScaleGestureDetector& detector) OVERRIDE { |
- if (ignore_detector_events_) |
+ if (ignore_multitouch_events_ && !detector.InDoubleTapMode()) |
return false; |
if (!pinch_event_sent_) { |
pinch_event_sent_ = true; |
@@ -130,8 +128,23 @@ class GestureProvider::ScaleGestureListenerImpl |
detector.GetFocusX(), |
detector.GetFocusY())); |
} |
- GestureEventDetails pinch_details( |
- ET_GESTURE_PINCH_UPDATE, detector.GetScaleFactor(), 0); |
+ |
+ float scale = detector.GetScaleFactor(); |
+ if (scale == 1) |
+ return true; |
+ |
+ if (detector.InDoubleTapMode()) { |
+ // Relative changes in the double-tap scale factor computed by |detector| |
+ // diminish as the touch moves away from the original double-tap focus. |
+ // Instead, compute a scale factor delta invariant to the distance, where |
+ // the scale delta remains constant if the touch velocity is constant. |
+ float dy = |
+ (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f; |
+ scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed |
+ : 1.0f - kDoubleTapDragZoomSpeed, |
+ std::abs(dy * px_to_dp_)); |
tdresser
2014/04/03 17:53:30
Do we have any justification for why Chromium and
jdduke (slow)
2014/04/03 19:28:01
I don't, but I can find out.
|
+ } |
+ GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0); |
provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE, |
detector.GetEventTime(), |
detector.GetFocusX(), |
@@ -140,26 +153,42 @@ class GestureProvider::ScaleGestureListenerImpl |
return true; |
} |
- bool IsScaleGestureDetectionInProgress() const { |
- return !ignore_detector_events_ && scale_gesture_detector_.IsInProgress(); |
+ void SetDoubleTapSupportEnabled(bool enabled) { |
tdresser
2014/04/03 17:53:30
Maybe just "SetDoubleTapEnabled"?
jdduke (slow)
2014/04/03 19:28:01
Done.
|
+ DCHECK(!IsDoubleTapInProgress()); |
+ scale_gesture_detector_.SetQuickScaleEnabled(enabled); |
} |
- void set_ignore_detector_events(bool value) { |
+ void SetMultiTouchSupportEnabled(bool value) { |
// 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_detector_events_ = value; |
+ ignore_multitouch_events_ = value; |
+ } |
+ |
+ 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_; |
+ // TODO(jdduke): Remove this when all MotionEvent's use DIPs. |
+ const float px_to_dp_; |
+ |
// Completely silence scaling events. Used in WebView when zoom support |
// is turned off. |
- bool ignore_detector_events_; |
+ bool ignore_multitouch_events_; |
tdresser
2014/04/03 17:53:30
The comment and the variable name don't line up he
jdduke (slow)
2014/04/03 19:28:01
Oops yeah didn't notice the comment, thanks.
|
// Whether any pinch zoom event has been sent to native. |
bool pinch_event_sent_; |
@@ -181,24 +210,16 @@ class GestureProvider::GestureListenerImpl |
: gesture_detector_(gesture_detector_config, this, this), |
snap_scroll_controller_(snap_scroll_controller_config), |
provider_(provider), |
- px_to_dp_(1.0f / snap_scroll_controller_config.device_scale_factor), |
disable_click_delay_(disable_click_delay), |
scaled_touch_slop_(gesture_detector_config.scaled_touch_slop), |
scaled_touch_slop_square_(scaled_touch_slop_ * scaled_touch_slop_), |
double_tap_timeout_(gesture_detector_config.double_tap_timeout), |
ignore_single_tap_(false), |
seen_first_scroll_event_(false), |
- double_tap_mode_(DOUBLE_TAP_MODE_NONE), |
- double_tap_y_(0), |
- double_tap_support_enabled_(true), |
- double_tap_drag_zoom_anchor_x_(0), |
- double_tap_drag_zoom_anchor_y_(0), |
last_raw_x_(0), |
last_raw_y_(0), |
accumulated_scroll_error_x_(0), |
- accumulated_scroll_error_y_(0) { |
- UpdateDoubleTapListener(); |
- } |
+ accumulated_scroll_error_y_(0) {} |
virtual ~GestureListenerImpl() {} |
@@ -210,12 +231,8 @@ class GestureProvider::GestureListenerImpl |
if (is_scale_gesture_detection_in_progress) |
SetIgnoreSingleTap(true); |
- if (e.GetAction() == MotionEvent::ACTION_POINTER_DOWN || |
- e.GetAction() == MotionEvent::ACTION_CANCEL) { |
- EndDoubleTapDragIfNecessary(e); |
- } else if (e.GetAction() == MotionEvent::ACTION_DOWN) { |
+ if (e.GetAction() == MotionEvent::ACTION_DOWN) |
gesture_detector_.set_is_longpress_enabled(true); |
- } |
return gesture_detector_.OnTouchEvent(e); |
} |
@@ -343,7 +360,7 @@ class GestureProvider::GestureListenerImpl |
if (!ignore_single_tap_) { |
if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) { |
return OnSingleTapConfirmed(e); |
- } else if (IsDoubleTapDisabled() || disable_click_delay_) { |
+ } else if (!IsDoubleTapEnabled() || disable_click_delay_) { |
// If double-tap has been disabled, there is no need to wait |
// for the double-tap timeout. |
return OnSingleTapConfirmed(e); |
@@ -381,71 +398,22 @@ class GestureProvider::GestureListenerImpl |
virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE { |
switch (e.GetAction()) { |
case MotionEvent::ACTION_DOWN: |
- // Note that this will be called before the corresponding |onDown()| |
- // of the same ACTION_DOWN event. Thus, the preceding TAP_DOWN |
- // should be cancelled prior to sending a new one (in |onDown()|). |
- double_tap_drag_zoom_anchor_x_ = e.GetX(); |
- double_tap_drag_zoom_anchor_y_ = e.GetY(); |
- double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS; |
- // If a long-press fires during a double-tap, the GestureDetector |
- // will stop feeding MotionEvents to |onDoubleTapEvent()|, |
- // preventing double-tap drag zoom. Long press detection will be |
- // re-enabled on the next ACTION_DOWN. |
gesture_detector_.set_is_longpress_enabled(false); |
break; |
- case MotionEvent::ACTION_MOVE: |
- if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS) { |
- float distance_x = double_tap_drag_zoom_anchor_x_ - e.GetX(); |
- float distance_y = double_tap_drag_zoom_anchor_y_ - e.GetY(); |
- |
- // Begin double-tap drag zoom mode if the move distance is |
- // further than the threshold. |
- if (IsDistanceGreaterThanTouchSlop(distance_x, distance_y)) { |
- GestureEventDetails scroll_details( |
- ET_GESTURE_SCROLL_BEGIN, -distance_x, -distance_y); |
- provider_->Send( |
- CreateGesture(ET_GESTURE_SCROLL_BEGIN, e, scroll_details)); |
- provider_->Send( |
- CreateGesture(ET_GESTURE_PINCH_BEGIN, |
- e.GetEventTime(), |
- Round(double_tap_drag_zoom_anchor_x_), |
- Round(double_tap_drag_zoom_anchor_y_))); |
- double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_ZOOM; |
- } |
- } else if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) { |
- provider_->Send(CreateGesture(ET_GESTURE_SCROLL_UPDATE, e)); |
- |
- float dy = double_tap_y_ - e.GetY(); |
- float scale = std::pow(dy > 0 ? 1.0f - kDoubleTapDragZoomSpeed |
- : 1.0f + kDoubleTapDragZoomSpeed, |
- std::abs(dy * px_to_dp_)); |
- GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0); |
- provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE, |
- e.GetEventTime(), |
- Round(double_tap_drag_zoom_anchor_x_), |
- Round(double_tap_drag_zoom_anchor_y_), |
- pinch_details)); |
- } |
- break; |
+ |
case MotionEvent::ACTION_UP: |
- if (double_tap_mode_ != DOUBLE_TAP_MODE_DRAG_ZOOM) { |
- // Normal double-tap gesture. |
+ if (!provider_->IsPinchInProgress()) { |
provider_->Send( |
CreateGesture(ET_GESTURE_DOUBLE_TAP, |
e, |
CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP, e))); |
+ return true; |
} |
- EndDoubleTapDragIfNecessary(e); |
- break; |
- case MotionEvent::ACTION_CANCEL: |
- EndDoubleTapDragIfNecessary(e); |
break; |
default: |
- NOTREACHED() << "Invalid double-tap event."; |
break; |
} |
- double_tap_y_ = e.GetY(); |
- return true; |
+ return false; |
} |
virtual bool OnLongPress(const MotionEvent& e) OVERRIDE { |
@@ -464,43 +432,23 @@ class GestureProvider::GestureListenerImpl |
return false; |
} |
- void SetDoubleTapSupportForPlatformEnabled(bool enabled) { |
+ void SetDoubleTapSupportEnabled(bool enabled) { |
DCHECK(!IsDoubleTapInProgress()); |
- DoubleTapMode double_tap_mode = |
- enabled ? DOUBLE_TAP_MODE_NONE : DOUBLE_TAP_MODE_DISABLED; |
- if (double_tap_mode_ == double_tap_mode) |
- return; |
- double_tap_mode_ = double_tap_mode; |
- UpdateDoubleTapListener(); |
- } |
- |
- void SetDoubleTapSupportForPageEnabled(bool enabled) { |
- if (double_tap_support_enabled_ == enabled) |
- return; |
- double_tap_support_enabled_ = enabled; |
- UpdateDoubleTapListener(); |
- } |
- |
- bool IsDoubleTapDisabled() const { |
- return double_tap_mode_ == DOUBLE_TAP_MODE_DISABLED || |
- !double_tap_support_enabled_; |
+ if (enabled) { |
+ gesture_detector_.set_doubletap_listener(this); |
+ } else { |
+ // TODO(jdduke): Send GESTURE_TAP if GESTURE_TAP_UNCONFIRMED already sent. |
+ gesture_detector_.set_doubletap_listener(NULL); |
+ } |
} |
bool IsClickDelayDisabled() const { return disable_click_delay_; } |
bool IsDoubleTapInProgress() const { |
- return double_tap_mode_ != DOUBLE_TAP_MODE_DISABLED && |
- double_tap_mode_ != DOUBLE_TAP_MODE_NONE; |
+ return gesture_detector_.is_double_tapping(); |
} |
private: |
- enum DoubleTapMode { |
- DOUBLE_TAP_MODE_NONE, |
- DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS, |
- DOUBLE_TAP_MODE_DRAG_ZOOM, |
- DOUBLE_TAP_MODE_DISABLED |
- }; |
- |
bool IsPointOutsideCurrentSlopRegion(float x, float y) const { |
return IsDistanceGreaterThanTouchSlop(last_raw_x_ - x, last_raw_y_ - y); |
} |
@@ -513,27 +461,8 @@ class GestureProvider::GestureListenerImpl |
void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; } |
- void EndDoubleTapDragIfNecessary(const MotionEvent& event) { |
- if (!IsDoubleTapInProgress()) |
- return; |
- if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) { |
- provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, event)); |
- provider_->Send(CreateGesture(ET_GESTURE_SCROLL_END, event)); |
- } |
- double_tap_mode_ = DOUBLE_TAP_MODE_NONE; |
- UpdateDoubleTapListener(); |
- } |
- |
- void UpdateDoubleTapListener() { |
- if (IsDoubleTapDisabled()) { |
- // Defer nulling the DoubleTapListener until the double-tap gesture is |
- // complete. |
- if (IsDoubleTapInProgress()) |
- return; |
- gesture_detector_.set_doubletap_listener(NULL); |
- } else { |
- gesture_detector_.set_doubletap_listener(this); |
- } |
+ bool IsDoubleTapEnabled() const { |
+ return gesture_detector_.has_doubletap_listener(); |
} |
GestureDetector gesture_detector_; |
@@ -541,8 +470,6 @@ class GestureProvider::GestureListenerImpl |
GestureProvider* const provider_; |
- const float px_to_dp_; |
- |
// Whether the click delay should always be disabled by sending clicks for |
// double-tap gestures. |
const bool disable_click_delay_; |
@@ -566,21 +493,6 @@ class GestureProvider::GestureListenerImpl |
// gesture. |
bool seen_first_scroll_event_; |
- // Indicate current double-tap mode state. |
- int double_tap_mode_; |
- |
- // On double-tap this will store the y coordinates of the touch. |
- float double_tap_y_; |
- |
- // The page's viewport and scale sometimes allow us to disable double-tap |
- // gesture detection, |
- // according to the logic in ContentViewCore.onRenderCoordinatesUpdated(). |
- bool double_tap_support_enabled_; |
- |
- // x, y coordinates for an Anchor on double-tap drag zoom. |
- float double_tap_drag_zoom_anchor_x_; |
- float double_tap_drag_zoom_anchor_y_; |
- |
// Used to track the last rawX/Y coordinates for moves. This gives absolute |
// scroll distance. |
// Useful for full screen tracking. |
@@ -604,7 +516,9 @@ GestureProvider::GestureProvider(const Config& config, |
needs_show_press_event_(false), |
needs_tap_ending_event_(false), |
touch_scroll_in_progress_(false), |
- pinch_in_progress_(false) { |
+ pinch_in_progress_(false), |
+ double_tap_support_for_page_(true), |
+ double_tap_support_for_platform_(true) { |
DCHECK(client); |
InitGestureDetectors(config); |
} |
@@ -617,7 +531,6 @@ bool GestureProvider::OnTouchEvent(const MotionEvent& event) { |
if (!CanHandle(event)) |
return false; |
- const bool was_touch_scrolling_ = touch_scroll_in_progress_; |
const bool in_scale_gesture = |
scale_gesture_listener_->IsScaleGestureDetectionInProgress(); |
@@ -634,16 +547,17 @@ bool GestureProvider::OnTouchEvent(const MotionEvent& event) { |
if (event.GetAction() == MotionEvent::ACTION_UP || |
event.GetAction() == MotionEvent::ACTION_CANCEL) { |
- // "Last finger raised" could be an end to movement, but it should |
- // only terminate scrolling if the event did not cause a fling. |
- if (was_touch_scrolling_ && !handled) |
- EndTouchScrollIfNecessary(event.GetEventTime(), true); |
+ // 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.GetEventTime(), true); |
// We shouldn't necessarily cancel a tap on ACTION_UP, as the double-tap |
// timeout may yet trigger a SINGLE_TAP. |
if (event.GetAction() == MotionEvent::ACTION_CANCEL) |
SendTapCancelIfNecessary(event); |
+ UpdateDoubleTapDetectionSupport(); |
+ |
current_down_event_.reset(); |
} |
@@ -659,15 +573,17 @@ void GestureProvider::ResetGestureDetectors() { |
} |
void GestureProvider::SetMultiTouchSupportEnabled(bool enabled) { |
- scale_gesture_listener_->set_ignore_detector_events(!enabled); |
+ scale_gesture_listener_->SetMultiTouchSupportEnabled(!enabled); |
} |
void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) { |
- gesture_listener_->SetDoubleTapSupportForPlatformEnabled(enabled); |
+ double_tap_support_for_platform_ = enabled; |
+ UpdateDoubleTapDetectionSupport(); |
} |
void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) { |
- gesture_listener_->SetDoubleTapSupportForPageEnabled(enabled); |
+ double_tap_support_for_page_ = enabled; |
+ UpdateDoubleTapDetectionSupport(); |
} |
bool GestureProvider::IsScrollInProgress() const { |
@@ -679,7 +595,12 @@ bool GestureProvider::IsScrollInProgress() const { |
bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; } |
bool GestureProvider::IsDoubleTapInProgress() const { |
- return gesture_listener_->IsDoubleTapInProgress(); |
+ return gesture_listener_->IsDoubleTapInProgress() || |
+ scale_gesture_listener_->IsDoubleTapInProgress(); |
+} |
+ |
+bool GestureProvider::IsDoubleTapSupportedEnabled() const { |
+ return double_tap_support_for_page_ && double_tap_support_for_platform_; |
} |
bool GestureProvider::IsClickDelayDisabled() const { |
@@ -694,8 +615,12 @@ void GestureProvider::InitGestureDetectors(const Config& config) { |
config.disable_click_delay, |
this)); |
- scale_gesture_listener_.reset( |
- new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this)); |
+ scale_gesture_listener_.reset(new ScaleGestureListenerImpl( |
+ config.scale_gesture_detector_config, |
+ config.snap_scroll_controller_config.device_scale_factor, |
+ this)); |
+ |
+ UpdateDoubleTapDetectionSupport(); |
} |
bool GestureProvider::CanHandle(const MotionEvent& event) const { |
@@ -776,6 +701,9 @@ void GestureProvider::Send(const GestureEventData& gesture) { |
touch_scroll_in_progress_ = false; |
break; |
case ET_GESTURE_PINCH_BEGIN: |
+ if (!touch_scroll_in_progress_) |
+ Send(CreateGesture( |
+ ET_GESTURE_SCROLL_BEGIN, gesture.time, gesture.x, gesture.y)); |
pinch_in_progress_ = true; |
break; |
case ET_GESTURE_PINCH_END: |
@@ -818,4 +746,13 @@ void GestureProvider::EndTouchScrollIfNecessary(base::TimeTicks time, |
Send(CreateGesture(ET_GESTURE_SCROLL_END, time, 0, 0)); |
} |
+void GestureProvider::UpdateDoubleTapDetectionSupport() { |
+ if (IsDoubleTapInProgress()) |
+ return; |
+ |
+ const bool supports_double_tap = IsDoubleTapSupportedEnabled(); |
+ gesture_listener_->SetDoubleTapSupportEnabled(supports_double_tap); |
+ scale_gesture_listener_->SetDoubleTapSupportEnabled(supports_double_tap); |
+} |
+ |
} // namespace ui |