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