Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/events/gesture_detection/gesture_provider.h" | |
| 6 | |
| 7 #include "base/auto_reset.h" | |
| 8 #include "base/debug/trace_event.h" | |
| 9 #include "ui/events/gesture_detection/gesture_event_params.h" | |
| 10 #include "ui/events/gesture_detection/motion_event.h" | |
| 11 | |
| 12 namespace ui { | |
| 13 namespace { | |
| 14 | |
| 15 // Double tap drag zoom sensitive (speed). | |
|
tdresser
2014/02/19 17:44:12
sensitive -> sensitivity
jdduke (slow)
2014/02/19 18:51:58
Done.
| |
| 16 const float kDoubleTapDragZoomSpeed = 0.005f; | |
| 17 | |
| 18 GestureEventParams CreateGesture(GestureEventType type, | |
| 19 base::TimeTicks time, | |
| 20 float x, | |
| 21 float y, | |
| 22 const GestureEventParams::Data& extra_data) { | |
| 23 return GestureEventParams(type, time, x, y, extra_data); | |
| 24 } | |
| 25 | |
| 26 GestureEventParams CreateGesture(GestureEventType type, | |
| 27 base::TimeTicks time, | |
| 28 float x, | |
| 29 float y) { | |
| 30 return CreateGesture(type, time, x, y, GestureEventParams::Data()); | |
| 31 } | |
| 32 | |
| 33 GestureEventParams CreateGesture(GestureEventType type, | |
| 34 const MotionEvent& event, | |
| 35 const GestureEventParams::Data& extra_data) { | |
| 36 return CreateGesture(type, | |
| 37 event.GetEventTime(), | |
| 38 event.GetX(), | |
| 39 event.GetY(), | |
| 40 extra_data); | |
| 41 } | |
| 42 | |
| 43 | |
| 44 GestureEventParams CreateGesture(GestureEventType type, | |
| 45 const MotionEvent& event) { | |
| 46 return CreateGesture(type, event, GestureEventParams::Data()); | |
| 47 } | |
| 48 | |
| 49 float Round(float f) { | |
| 50 return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f); | |
| 51 } | |
| 52 | |
| 53 } // namespace | |
| 54 | |
| 55 | |
| 56 // GestureProvider:::Config | |
| 57 | |
| 58 GestureProvider::Config::Config() : disable_click_delay(false) {} | |
| 59 | |
| 60 GestureProvider::Config::~Config() {} | |
| 61 | |
| 62 | |
| 63 // GestureProvider::ScaleGestureListener | |
| 64 | |
| 65 class GestureProvider::ScaleGestureListener | |
| 66 : public ScaleGestureDetector::OnScaleGestureListener { | |
| 67 public: | |
| 68 ScaleGestureListener(const ScaleGestureDetector::Config& config, | |
| 69 GestureProvider* handler) | |
| 70 : scale_gesture_detector_(config, this), | |
| 71 provider_(handler), | |
| 72 ignore_detector_events_(false), | |
| 73 pinch_event_sent_(false) {} | |
| 74 | |
| 75 bool OnTouchEvent(const MotionEvent& event) { | |
| 76 // TODO: Need to deal with multi-touch transition | |
| 77 const bool in_scale_gesture = IsScaleGestureDetectionInProgress(); | |
| 78 bool handled = scale_gesture_detector_.OnTouchEvent(event); | |
| 79 if (!in_scale_gesture && | |
| 80 (event.GetAction() == MotionEvent::ACTION_UP || | |
| 81 event.GetAction() == MotionEvent::ACTION_CANCEL)) { | |
| 82 return false; | |
| 83 } | |
| 84 return handled; | |
| 85 } | |
| 86 | |
| 87 // ScaleGestureDetector::OnScaleGestureListener | |
| 88 virtual bool OnScaleBegin(const ScaleGestureDetector& detector) OVERRIDE { | |
| 89 if (ignore_detector_events_) | |
| 90 return false; | |
| 91 pinch_event_sent_ = false; | |
| 92 return true; | |
| 93 } | |
| 94 | |
| 95 virtual void OnScaleEnd(const ScaleGestureDetector& detector) OVERRIDE { | |
| 96 if (!pinch_event_sent_) | |
| 97 return; | |
| 98 provider_->Send( | |
| 99 CreateGesture(GESTURE_PINCH_END, detector.GetEventTime(), 0, 0)); | |
| 100 pinch_event_sent_ = false; | |
| 101 } | |
| 102 | |
| 103 virtual bool OnScale(const ScaleGestureDetector& detector) OVERRIDE { | |
| 104 if (ignore_detector_events_) | |
| 105 return false; | |
| 106 if (!pinch_event_sent_) { | |
| 107 pinch_event_sent_ = true; | |
| 108 provider_->Send(CreateGesture(GESTURE_PINCH_BEGIN, | |
| 109 detector.GetEventTime(), | |
| 110 detector.GetFocusX(), | |
| 111 detector.GetFocusY())); | |
| 112 } | |
| 113 GestureEventParams::Data pinch_data; | |
| 114 pinch_data.pinch_update.scale = detector.GetScaleFactor(); | |
| 115 provider_->Send(CreateGesture(GESTURE_PINCH_UPDATE, | |
| 116 detector.GetEventTime(), | |
| 117 detector.GetFocusX(), | |
| 118 detector.GetFocusY(), | |
| 119 pinch_data)); | |
| 120 return true; | |
| 121 } | |
| 122 | |
| 123 bool IsScaleGestureDetectionInProgress() { | |
| 124 return !ignore_detector_events_ && scale_gesture_detector_.IsInProgress(); | |
| 125 } | |
| 126 | |
| 127 void SetIgnoreDetectorEvents(bool value) { | |
| 128 // Note that returning false from OnScaleBegin / OnScale makes the | |
| 129 // gesture detector not to emit further scaling notifications | |
| 130 // related to this gesture. Thus, if detector events are enabled in | |
| 131 // the middle of the gesture, we don't need to do anything. | |
| 132 ignore_detector_events_ = value; | |
| 133 } | |
| 134 | |
| 135 private: | |
| 136 ScaleGestureDetector scale_gesture_detector_; | |
| 137 | |
| 138 GestureProvider* const provider_; | |
| 139 | |
| 140 // Completely silence scaling events. Used in WebView when zoom support | |
| 141 // is turned off. | |
| 142 bool ignore_detector_events_; | |
| 143 | |
| 144 // Whether any pinch zoom event has been sent to native. | |
| 145 bool pinch_event_sent_; | |
| 146 | |
| 147 DISALLOW_COPY_AND_ASSIGN(ScaleGestureListener); | |
| 148 }; | |
| 149 | |
| 150 // GestureProvider::GestureListener | |
| 151 | |
| 152 class GestureProvider::GestureListener | |
| 153 : public GestureDetector::OnGestureListener, | |
| 154 public GestureDetector::OnDoubleTapListener { | |
| 155 public: | |
| 156 GestureListener( | |
| 157 const GestureDetector::Config& gesture_detector_config, | |
| 158 const SnapScrollController::Config& snap_scroll_controller_config, | |
| 159 bool disable_click_delay, | |
| 160 GestureProvider* handler) | |
| 161 : gesture_detector_(gesture_detector_config, this, this), | |
| 162 snap_scroll_controller_(snap_scroll_controller_config), | |
| 163 provider_(handler), | |
| 164 px_to_dp_(1.0f / snap_scroll_controller_config.device_scale_factor), | |
| 165 disable_click_delay_(disable_click_delay), | |
| 166 scaled_touch_slop_(gesture_detector_config.scaled_touch_slop), | |
| 167 scaled_touch_slop_square_(scaled_touch_slop_ * scaled_touch_slop_), | |
| 168 double_tap_timeout_(gesture_detector_config.double_tap_timeout), | |
| 169 ignore_single_tap_(false), | |
| 170 seen_first_scroll_event_(false), | |
| 171 double_tap_mode_(DOUBLE_TAP_MODE_NONE), | |
| 172 double_tap_y_(0), | |
| 173 support_double_tap_(true), | |
| 174 double_tap_drag_zoom_anchor_x_(0), | |
| 175 double_tap_drag_zoom_anchor_y_(0), | |
| 176 last_raw_x_(0), | |
| 177 last_raw_y_(0), | |
| 178 accumulated_scroll_error_x_(0), | |
| 179 accumulated_scroll_error_y_(0) { | |
| 180 UpdateDoubleTapListener(); | |
| 181 } | |
| 182 | |
| 183 virtual ~GestureListener() {} | |
| 184 | |
| 185 bool OnTouchEvent(const MotionEvent& e, | |
| 186 bool is_scale_gesture_detection_in_progress) { | |
| 187 snap_scroll_controller_.SetSnapScrollingMode( | |
| 188 e, is_scale_gesture_detection_in_progress); | |
| 189 | |
| 190 if (is_scale_gesture_detection_in_progress) { | |
| 191 // TODO(jdduke): Determine if GestureDetector properly prevents | |
| 192 // taps if a secondary pointer is depressed. | |
| 193 SetIgnoreSingleTap(true); | |
| 194 } | |
| 195 | |
| 196 if (e.GetAction() == MotionEvent::ACTION_POINTER_DOWN) { | |
| 197 EndDoubleTapDragIfNecessary(e); | |
| 198 } else if (e.GetAction() == MotionEvent::ACTION_DOWN) { | |
| 199 gesture_detector_.set_is_longpress_enabled(true); | |
| 200 } | |
| 201 | |
| 202 return gesture_detector_.OnTouchEvent(e); | |
| 203 } | |
| 204 | |
| 205 // GestureDetector::OnGestureListener | |
| 206 virtual bool OnDown(const MotionEvent& e) OVERRIDE { | |
| 207 current_down_time_ = e.GetEventTime(); | |
| 208 ignore_single_tap_ = false; | |
| 209 seen_first_scroll_event_ = false; | |
| 210 last_raw_x_ = e.GetRawX(); | |
| 211 last_raw_y_ = e.GetRawY(); | |
| 212 accumulated_scroll_error_x_ = 0; | |
| 213 accumulated_scroll_error_y_ = 0; | |
| 214 | |
| 215 GestureEventParams::Data tap_down_data; | |
| 216 tap_down_data.tap.width = tap_down_data.tap.height = e.GetTouchMajor(); | |
| 217 provider_->Send(CreateGesture(GESTURE_TAP_DOWN, e, tap_down_data)); | |
| 218 | |
| 219 // Return true to indicate that we want to handle touch | |
| 220 return true; | |
| 221 } | |
| 222 | |
| 223 virtual bool OnScroll(const MotionEvent& e1, | |
| 224 const MotionEvent& e2, | |
| 225 float raw_distance_x, | |
| 226 float raw_distance_y) OVERRIDE { | |
| 227 float distance_x = raw_distance_x; | |
| 228 float distance_y = raw_distance_y; | |
| 229 if (!seen_first_scroll_event_) { | |
| 230 // Remove the touch slop region from the first scroll event to avoid a | |
| 231 // jump. | |
| 232 seen_first_scroll_event_ = true; | |
| 233 double distance = | |
| 234 std::sqrt(distance_x * distance_x + distance_y * distance_y); | |
| 235 double epsilon = 1e-3; | |
| 236 if (distance > epsilon) { | |
| 237 double ratio = std::max(0., distance - scaled_touch_slop_) / distance; | |
| 238 distance_x *= ratio; | |
| 239 distance_y *= ratio; | |
| 240 } | |
| 241 } | |
| 242 snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y); | |
| 243 if (snap_scroll_controller_.IsSnappingScrolls()) { | |
| 244 if (snap_scroll_controller_.IsSnapHorizontal()) { | |
| 245 distance_y = 0; | |
| 246 } else { | |
| 247 distance_x = 0; | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 last_raw_x_ = e2.GetRawX(); | |
| 252 last_raw_y_ = e2.GetRawY(); | |
| 253 if (!provider_->IsScrollInProgress()) { | |
| 254 // Note that scroll start hints are in distance traveled, where | |
| 255 // scroll deltas are in the opposite direction. | |
| 256 GestureEventParams::Data scroll_data; | |
| 257 scroll_data.scroll_begin.delta_x_hint = -raw_distance_x; | |
| 258 scroll_data.scroll_begin.delta_y_hint = -raw_distance_y; | |
| 259 provider_->Send(CreateGesture(GESTURE_SCROLL_BEGIN, | |
| 260 e2.GetEventTime(), | |
| 261 e1.GetX(), | |
| 262 e1.GetY(), | |
| 263 scroll_data)); | |
| 264 } | |
| 265 | |
| 266 // distance_x and distance_y is the scrolling offset since last OnScroll. | |
| 267 // Because we are passing integers to webkit, this could introduce | |
| 268 // rounding errors. The rounding errors will accumulate overtime. | |
| 269 // To solve this, we should be adding back the rounding errors each time | |
| 270 // when we calculate the new offset. | |
| 271 int dx = (int)(distance_x + accumulated_scroll_error_x_); | |
| 272 int dy = (int)(distance_y + accumulated_scroll_error_y_); | |
| 273 accumulated_scroll_error_x_ += (distance_x - dx); | |
| 274 accumulated_scroll_error_y_ += (distance_y - dy); | |
| 275 | |
| 276 if (dx || dy) { | |
| 277 GestureEventParams::Data scroll_data; | |
| 278 scroll_data.scroll_update.delta_x = -dx; | |
| 279 scroll_data.scroll_update.delta_y = -dy; | |
| 280 provider_->Send(CreateGesture(GESTURE_SCROLL_UPDATE, e2, scroll_data)); | |
| 281 } | |
| 282 | |
| 283 return true; | |
| 284 } | |
| 285 | |
| 286 virtual bool OnFling(const MotionEvent& e1, | |
| 287 const MotionEvent& e2, | |
| 288 float velocity_x, | |
| 289 float velocity_y) OVERRIDE { | |
| 290 if (snap_scroll_controller_.IsSnappingScrolls()) { | |
| 291 if (snap_scroll_controller_.IsSnapHorizontal()) { | |
| 292 velocity_y = 0; | |
| 293 } else { | |
| 294 velocity_x = 0; | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 provider_->Fling( | |
| 299 e2.GetEventTime(), e1.GetX(), e1.GetY(), velocity_x, velocity_y); | |
| 300 return true; | |
| 301 } | |
| 302 | |
| 303 virtual void OnShowPress(const MotionEvent& e) OVERRIDE { | |
| 304 GestureEventParams::Data show_press_data; | |
| 305 show_press_data.show_press.width = e.GetTouchMajor(); | |
| 306 show_press_data.show_press.height = show_press_data.show_press.width; | |
| 307 provider_->Send(CreateGesture(GESTURE_SHOW_PRESS, e, show_press_data)); | |
| 308 } | |
| 309 | |
| 310 virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE { | |
| 311 if (IsPointOutsideCurrentSlopRegion(e.GetRawX(), e.GetRawY())) { | |
| 312 provider_->SendTapCancelIfNecessary(e); | |
| 313 ignore_single_tap_ = true; | |
| 314 return true; | |
| 315 } | |
| 316 // This is a hack to address the issue where user hovers | |
| 317 // over a link for longer than double_tap_timeout_, then | |
| 318 // OnSingleTapConfirmed() is not triggered. But we still | |
| 319 // want to trigger the tap event at UP. So we override | |
| 320 // OnSingleTapUp() in this case. This assumes singleTapUp | |
| 321 // gets always called before singleTapConfirmed. | |
| 322 if (!ignore_single_tap_) { | |
| 323 if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) { | |
| 324 return OnSingleTapConfirmed(e); | |
| 325 } else if (IsDoubleTapDisabled() || disable_click_delay_) { | |
| 326 // If double tap has been disabled, there is no need to wait | |
| 327 // for the double tap timeout. | |
| 328 return OnSingleTapConfirmed(e); | |
| 329 } else { | |
| 330 // Notify Blink about this tapUp event anyway, | |
| 331 // when none of the above conditions applied. | |
| 332 provider_->Send(CreateGesture(GESTURE_SINGLE_TAP_UNCONFIRMED, e)); | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 return provider_->SendLongTapIfNecessary(e); | |
| 337 } | |
| 338 | |
| 339 // GestureDetector::OnDoubleTapListener | |
| 340 virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE { | |
| 341 // Long taps in the edges of the screen have their events delayed by | |
| 342 // ContentViewHolder for tab swipe operations. As a consequence of the delay | |
| 343 // this method might be called after receiving the up event. | |
| 344 // These corner cases should be ignored. | |
| 345 if (ignore_single_tap_) | |
| 346 return true; | |
| 347 | |
| 348 ignore_single_tap_ = true; | |
| 349 | |
| 350 GestureEventParams::Data tap_data; | |
| 351 tap_data.tap.tap_count = 1; | |
| 352 tap_data.tap.width = tap_data.tap.height = e.GetTouchMajor(); | |
| 353 provider_->Send(CreateGesture(GESTURE_SINGLE_TAP_CONFIRMED, e, tap_data)); | |
| 354 return true; | |
| 355 } | |
| 356 | |
| 357 virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; } | |
| 358 | |
| 359 virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE { | |
| 360 switch (e.GetAction()) { | |
| 361 case MotionEvent::ACTION_DOWN: | |
| 362 // Note that this will be called before the corresponding |onDown()| | |
| 363 // of the same ACTION_DOWN event. Thus, the preceding TAP_DOWN | |
| 364 // should be cancelled prior to sending a new one (in |onDown()|). | |
| 365 double_tap_drag_zoom_anchor_x_ = e.GetX(); | |
| 366 double_tap_drag_zoom_anchor_y_ = e.GetY(); | |
| 367 double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS; | |
| 368 // If a long-press fires during a double-tap, the GestureDetector | |
| 369 // will stop feeding MotionEvents to |onDoubleTapEvent()|, | |
| 370 // preventing double-tap drag zoom. Long press detection will be | |
| 371 // re-enabled on the next ACTION_DOWN. | |
| 372 gesture_detector_.set_is_longpress_enabled(false); | |
| 373 break; | |
| 374 case MotionEvent::ACTION_MOVE: | |
| 375 if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS) { | |
| 376 float distance_x = double_tap_drag_zoom_anchor_x_ - e.GetX(); | |
| 377 float distance_y = double_tap_drag_zoom_anchor_y_ - e.GetY(); | |
| 378 | |
| 379 // Begin double tap drag zoom mode if the move distance is | |
| 380 // further than the threshold. | |
| 381 if (IsDistanceGreaterThanTouchSlop(distance_x, distance_y)) { | |
| 382 GestureEventParams::Data scroll_data; | |
| 383 scroll_data.scroll_begin.delta_x_hint = -distance_x; | |
| 384 scroll_data.scroll_begin.delta_y_hint = -distance_y; | |
| 385 provider_->Send( | |
| 386 CreateGesture(GESTURE_SCROLL_BEGIN, e, scroll_data)); | |
| 387 provider_->Send( | |
| 388 CreateGesture(GESTURE_PINCH_BEGIN, | |
| 389 e.GetEventTime(), | |
| 390 Round(double_tap_drag_zoom_anchor_x_), | |
| 391 Round(double_tap_drag_zoom_anchor_y_))); | |
| 392 double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_ZOOM; | |
| 393 } | |
| 394 } else if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) { | |
| 395 provider_->Send(CreateGesture(GESTURE_SCROLL_UPDATE, e)); | |
| 396 | |
| 397 float dy = double_tap_y_ - e.GetY(); | |
| 398 GestureEventParams::Data pinch_data; | |
| 399 pinch_data.pinch_update.scale = | |
| 400 std::pow(dy > 0 ? 1.0f - kDoubleTapDragZoomSpeed | |
| 401 : 1.0f + kDoubleTapDragZoomSpeed, | |
| 402 std::abs(dy * px_to_dp_)); | |
| 403 provider_->Send(CreateGesture(GESTURE_PINCH_UPDATE, | |
| 404 e.GetEventTime(), | |
| 405 Round(double_tap_drag_zoom_anchor_x_), | |
| 406 Round(double_tap_drag_zoom_anchor_y_), | |
| 407 pinch_data)); | |
| 408 } | |
| 409 break; | |
| 410 case MotionEvent::ACTION_UP: | |
| 411 if (double_tap_mode_ != DOUBLE_TAP_MODE_DRAG_ZOOM) { | |
| 412 // Normal double tap gesture. | |
| 413 provider_->Send(CreateGesture(GESTURE_DOUBLE_TAP, e)); | |
| 414 } | |
| 415 EndDoubleTapDragIfNecessary(e); | |
| 416 break; | |
| 417 case MotionEvent::ACTION_CANCEL: | |
| 418 EndDoubleTapDragIfNecessary(e); | |
| 419 break; | |
| 420 default: | |
| 421 break; | |
| 422 } | |
| 423 double_tap_y_ = e.GetY(); | |
| 424 return true; | |
| 425 } | |
| 426 | |
| 427 virtual bool OnLongPress(const MotionEvent& e) OVERRIDE { | |
| 428 DCHECK(!IsDoubleTapInProgress()); | |
| 429 SetIgnoreSingleTap(true); | |
| 430 | |
| 431 GestureEventParams::Data long_press_data; | |
| 432 long_press_data.long_press.width = e.GetTouchMajor(); | |
| 433 long_press_data.long_press.height = long_press_data.long_press.width; | |
| 434 provider_->Send(CreateGesture(GESTURE_LONG_PRESS, e, long_press_data)); | |
| 435 | |
| 436 // Returning true puts the GestureDetector in "longpress" mode, disabling | |
| 437 // further scrolling. This is undesirable, as it is quite common for a | |
| 438 // longpress gesture to fire on content that won't trigger a context menu. | |
| 439 return false; | |
| 440 } | |
| 441 | |
| 442 void UpdateDoubleTapSupportForPlatform(bool support_double_tap) { | |
| 443 DCHECK(!IsDoubleTapInProgress()); | |
| 444 DoubleTapMode double_tap_mode = | |
| 445 support_double_tap ? DOUBLE_TAP_MODE_NONE : DOUBLE_TAP_MODE_DISABLED; | |
| 446 if (double_tap_mode_ == double_tap_mode) | |
| 447 return; | |
| 448 double_tap_mode_ = double_tap_mode; | |
| 449 UpdateDoubleTapListener(); | |
| 450 } | |
| 451 | |
| 452 void UpdateDoubleTapSupportForPage(bool support_double_tap) { | |
| 453 if (support_double_tap_ == support_double_tap) | |
| 454 return; | |
| 455 support_double_tap_ = support_double_tap; | |
| 456 UpdateDoubleTapListener(); | |
| 457 } | |
| 458 | |
| 459 bool IsDoubleTapDisabled() const { | |
| 460 return double_tap_mode_ == DOUBLE_TAP_MODE_DISABLED || !support_double_tap_; | |
| 461 } | |
| 462 | |
| 463 bool IsClickDelayDisabled() const { return disable_click_delay_; } | |
| 464 | |
| 465 bool IsDoubleTapInProgress() const { | |
| 466 return double_tap_mode_ != DOUBLE_TAP_MODE_DISABLED && | |
| 467 double_tap_mode_ != DOUBLE_TAP_MODE_NONE; | |
| 468 } | |
| 469 | |
| 470 private: | |
| 471 enum DoubleTapMode { | |
| 472 DOUBLE_TAP_MODE_NONE, | |
| 473 DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS, | |
| 474 DOUBLE_TAP_MODE_DRAG_ZOOM, | |
| 475 DOUBLE_TAP_MODE_DISABLED | |
| 476 }; | |
| 477 | |
| 478 bool IsPointOutsideCurrentSlopRegion(float x, float y) const { | |
| 479 return IsDistanceGreaterThanTouchSlop(last_raw_x_ - x, last_raw_y_ - y); | |
| 480 } | |
| 481 | |
| 482 bool IsDistanceGreaterThanTouchSlop(float distance_x, | |
| 483 float distance_y) const { | |
| 484 return distance_x * distance_x + distance_y * distance_y > | |
| 485 scaled_touch_slop_square_; | |
| 486 } | |
| 487 | |
| 488 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; } | |
| 489 | |
| 490 void EndDoubleTapDragIfNecessary(const MotionEvent& event) { | |
| 491 if (!IsDoubleTapInProgress()) | |
| 492 return; | |
| 493 if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) { | |
| 494 provider_->Send(CreateGesture(GESTURE_PINCH_END, event)); | |
| 495 provider_->Send(CreateGesture(GESTURE_SCROLL_END, event)); | |
| 496 } | |
| 497 double_tap_mode_ = DOUBLE_TAP_MODE_NONE; | |
| 498 UpdateDoubleTapListener(); | |
| 499 } | |
| 500 | |
| 501 void UpdateDoubleTapListener() { | |
| 502 if (IsDoubleTapDisabled()) { | |
| 503 // Defer nulling the DoubleTapListener until the double tap gesture is | |
| 504 // complete. | |
| 505 if (IsDoubleTapInProgress()) | |
| 506 return; | |
| 507 gesture_detector_.set_on_doubletap_listener(NULL); | |
| 508 } else { | |
| 509 gesture_detector_.set_on_doubletap_listener(this); | |
| 510 } | |
| 511 } | |
| 512 | |
| 513 GestureDetector gesture_detector_; | |
| 514 SnapScrollController snap_scroll_controller_; | |
| 515 | |
| 516 GestureProvider* const provider_; | |
| 517 | |
| 518 const float px_to_dp_; | |
| 519 | |
| 520 // Whether the click delay should always be disabled by sending clicks for | |
| 521 // double tap gestures. | |
| 522 const bool disable_click_delay_; | |
| 523 | |
| 524 const int scaled_touch_slop_; | |
| 525 | |
| 526 // Cache of square of the scaled touch slop so we don't have to calculate it | |
| 527 // on every touch. | |
| 528 const int scaled_touch_slop_square_; | |
| 529 | |
| 530 const base::TimeDelta double_tap_timeout_; | |
| 531 | |
| 532 base::TimeTicks current_down_time_; | |
| 533 | |
| 534 // TODO(klobag): this is to avoid a bug in GestureDetector. With multi-touch, | |
| 535 // mAlwaysInTapRegion is not reset. So when the last finger is up, | |
| 536 // onSingleTapUp() will be mistakenly fired. | |
| 537 bool ignore_single_tap_; | |
| 538 | |
| 539 // Used to remove the touch slop from the initial scroll event in a scroll | |
| 540 // gesture. | |
| 541 bool seen_first_scroll_event_; | |
| 542 | |
| 543 // Indicate current double tap mode state. | |
| 544 int double_tap_mode_; | |
| 545 | |
| 546 // On double tap this will store the y coordinates of the touch. | |
| 547 float double_tap_y_; | |
| 548 | |
| 549 // The page's viewport and scale sometimes allow us to disable double tap | |
| 550 // gesture detection, | |
| 551 // according to the logic in ContentViewCore.onRenderCoordinatesUpdated(). | |
| 552 bool support_double_tap_; | |
| 553 | |
| 554 // x, y coordinates for an Anchor on double tap drag zoom. | |
| 555 float double_tap_drag_zoom_anchor_x_; | |
| 556 float double_tap_drag_zoom_anchor_y_; | |
| 557 | |
| 558 // Used to track the last rawX/Y coordinates for moves. This gives absolute | |
| 559 // scroll distance. | |
| 560 // Useful for full screen tracking. | |
| 561 float last_raw_x_; | |
| 562 float last_raw_y_; | |
| 563 | |
| 564 // Used to track the accumulated scroll error over time. This is used to | |
| 565 // remove the | |
| 566 // rounding error we introduced by passing integers to webkit. | |
| 567 float accumulated_scroll_error_x_; | |
| 568 float accumulated_scroll_error_y_; | |
| 569 | |
| 570 DISALLOW_COPY_AND_ASSIGN(GestureListener); | |
| 571 }; | |
| 572 | |
| 573 // GestureProvider | |
| 574 | |
| 575 GestureProvider::GestureProvider(const Config& config, | |
| 576 GestureProviderClient* client) | |
| 577 : client_(client), | |
| 578 needs_show_press_event_(false), | |
| 579 needs_tap_ending_event_(false), | |
| 580 touch_scroll_in_progress_(false), | |
| 581 pinch_in_progress_(false) { | |
| 582 DCHECK(client); | |
| 583 InitGestureDetectors(config); | |
| 584 } | |
| 585 | |
| 586 GestureProvider::~GestureProvider() {} | |
| 587 | |
| 588 bool GestureProvider::OnTouchEvent(const MotionEvent& event) { | |
| 589 TRACE_EVENT0("input", "GestureProvider::OnTouchEvent"); | |
| 590 if (!CanHandle(event)) | |
| 591 return false; | |
| 592 | |
| 593 const bool was_touch_scrolling_ = touch_scroll_in_progress_; | |
| 594 const bool in_scale_gesture = | |
| 595 scale_gesture_listener_->IsScaleGestureDetectionInProgress(); | |
| 596 | |
| 597 if (event.GetAction() == MotionEvent::ACTION_DOWN) { | |
| 598 current_down_event_ = event.Clone(); | |
| 599 touch_scroll_in_progress_ = false; | |
| 600 needs_show_press_event_ = true; | |
| 601 current_longpress_time_ = base::TimeTicks(); | |
| 602 SendTapCancelIfNecessary(event); | |
| 603 } | |
| 604 | |
| 605 bool handled = gesture_listener_->OnTouchEvent(event, in_scale_gesture); | |
| 606 handled |= scale_gesture_listener_->OnTouchEvent(event); | |
| 607 | |
| 608 if (event.GetAction() == MotionEvent::ACTION_UP || | |
| 609 event.GetAction() == MotionEvent::ACTION_CANCEL) { | |
| 610 // "Last finger raised" could be an end to movement, but it should | |
| 611 // only terminate scrolling if the event did not cause a fling. | |
| 612 if (was_touch_scrolling_ && !handled) | |
| 613 EndTouchScrollIfNecessary(event.GetEventTime(), true); | |
| 614 | |
| 615 // We shouldn't necessarily cancel a tap on ACTION_UP, as the double-tap | |
| 616 // timeout may yet trigger a SINGLE_TAP_CONFIRMED. | |
| 617 if (event.GetAction() == MotionEvent::ACTION_CANCEL) | |
| 618 SendTapCancelIfNecessary(event); | |
| 619 | |
| 620 current_down_event_.reset(); | |
| 621 } | |
| 622 | |
| 623 // TODO(jdduke): Determine if we should always return true or |handled|. | |
| 624 return true; | |
| 625 } | |
| 626 | |
| 627 void GestureProvider::ResetGestureDetectors() { | |
| 628 if (!current_down_event_) | |
| 629 return; | |
| 630 scoped_ptr<MotionEvent> cancel_event = current_down_event_->Cancel(); | |
| 631 // TODO(jdduke): Determine if gestures generated by this reset should be | |
| 632 // forwarded to the client. | |
| 633 gesture_listener_->OnTouchEvent(*cancel_event, false); | |
| 634 scale_gesture_listener_->OnTouchEvent(*cancel_event); | |
| 635 } | |
| 636 | |
| 637 void GestureProvider::CancelActiveTouchSequence() { | |
| 638 if (!current_down_event_) | |
| 639 return; | |
| 640 OnTouchEvent(*current_down_event_->Cancel()); | |
| 641 current_down_event_.reset(); | |
| 642 } | |
| 643 | |
| 644 void GestureProvider::UpdateMultiTouchSupport(bool support_multi_touch_zoom) { | |
| 645 scale_gesture_listener_->SetIgnoreDetectorEvents(!support_multi_touch_zoom); | |
| 646 } | |
| 647 | |
| 648 void GestureProvider::UpdateDoubleTapSupportForPlatform( | |
| 649 bool support_double_tap) { | |
| 650 gesture_listener_->UpdateDoubleTapSupportForPlatform(support_double_tap); | |
| 651 } | |
| 652 | |
| 653 void GestureProvider::UpdateDoubleTapSupportForPage( | |
| 654 bool support_double_tap) { | |
| 655 gesture_listener_->UpdateDoubleTapSupportForPage(support_double_tap); | |
| 656 } | |
| 657 | |
| 658 bool GestureProvider::IsScrollInProgress() const { | |
| 659 // TODO(wangxianzhu): Also return true when fling is active once the UI knows | |
| 660 // exactly when the fling ends. | |
| 661 return touch_scroll_in_progress_; | |
| 662 } | |
| 663 | |
| 664 bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; } | |
| 665 | |
| 666 bool GestureProvider::IsDoubleTapInProgress() const { | |
| 667 return gesture_listener_->IsDoubleTapInProgress(); | |
| 668 } | |
| 669 | |
| 670 void GestureProvider::InitGestureDetectors(const Config& config) { | |
| 671 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors"); | |
| 672 gesture_listener_.reset( | |
| 673 new GestureListener(config.gesture_detector_config, | |
| 674 config.snap_scroll_controller_config, | |
| 675 config.disable_click_delay, | |
| 676 this)); | |
| 677 | |
| 678 scale_gesture_listener_.reset( | |
| 679 new ScaleGestureListener(config.scale_gesture_detector_config, this)); | |
| 680 } | |
| 681 | |
| 682 bool GestureProvider::CanHandle(const MotionEvent& event) const { | |
| 683 // TODO(jdduke): Determine if we need this check. Are there cases | |
| 684 // where a touch stream can be paused/terminated and resumed | |
| 685 // without getting the appropriate stream-ending event? | |
| 686 return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_; | |
| 687 } | |
| 688 | |
| 689 void GestureProvider::Fling(base::TimeTicks time, | |
| 690 float x, | |
| 691 float y, | |
| 692 float velocity_x, | |
| 693 float velocity_y) { | |
| 694 if (!velocity_x && !velocity_y) { | |
| 695 EndTouchScrollIfNecessary(time, true); | |
| 696 return; | |
| 697 } | |
| 698 | |
| 699 if (!touch_scroll_in_progress_) { | |
| 700 // The native side needs a GESTURE_SCROLL_BEGIN before GESTURE_FLING_START | |
| 701 // to send the fling to the correct target. Send if it has not sent. | |
| 702 // The distance traveled in one second is a reasonable scroll start hint. | |
| 703 GestureEventParams::Data scroll_data; | |
| 704 scroll_data.scroll_begin.delta_x_hint = velocity_x; | |
| 705 scroll_data.scroll_begin.delta_y_hint = velocity_y; | |
| 706 Send(CreateGesture(GESTURE_SCROLL_BEGIN, time, x, y, scroll_data)); | |
| 707 } | |
| 708 EndTouchScrollIfNecessary(time, false); | |
| 709 | |
| 710 GestureEventParams::Data fling_data; | |
| 711 fling_data.fling_start.velocity_x = velocity_x; | |
| 712 fling_data.fling_start.velocity_y = velocity_y; | |
| 713 Send(CreateGesture(GESTURE_FLING_START, time, x, y, fling_data)); | |
| 714 } | |
| 715 | |
| 716 void GestureProvider::Send(const GestureEventParams& gesture) { | |
| 717 DCHECK(!gesture.time.is_null()); | |
| 718 // The only valid events that should be sent without an active touch sequence | |
| 719 // are SHOW_PRESS and TAP_CONFIRMED, potentially triggered by the double-tap | |
| 720 // delay timing out. | |
| 721 DCHECK(current_down_event_ || | |
| 722 gesture.type == GESTURE_SINGLE_TAP_CONFIRMED || | |
| 723 gesture.type == GESTURE_SHOW_PRESS); | |
| 724 | |
| 725 switch (gesture.type) { | |
| 726 case GESTURE_TAP_DOWN: | |
| 727 needs_tap_ending_event_ = true; | |
| 728 break; | |
| 729 case GESTURE_SINGLE_TAP_UNCONFIRMED: | |
| 730 needs_show_press_event_ = false; | |
| 731 break; | |
| 732 case GESTURE_SINGLE_TAP_CONFIRMED: | |
| 733 if (needs_show_press_event_) | |
| 734 Send(CreateGesture( | |
| 735 GESTURE_SHOW_PRESS, gesture.time, gesture.x, gesture.y)); | |
| 736 needs_tap_ending_event_ = false; | |
| 737 break; | |
| 738 case GESTURE_DOUBLE_TAP: | |
| 739 needs_tap_ending_event_ = false; | |
| 740 break; | |
| 741 case GESTURE_TAP_CANCEL: | |
| 742 if (!needs_tap_ending_event_) | |
| 743 return; | |
| 744 needs_tap_ending_event_ = false; | |
| 745 break; | |
| 746 case GESTURE_SHOW_PRESS: | |
| 747 needs_show_press_event_ = false; | |
| 748 break; | |
| 749 case GESTURE_LONG_TAP: | |
| 750 needs_tap_ending_event_ = false; | |
| 751 current_longpress_time_ = base::TimeTicks(); | |
| 752 break; | |
| 753 case GESTURE_LONG_PRESS: | |
| 754 DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress()); | |
| 755 current_longpress_time_ = gesture.time; | |
| 756 break; | |
| 757 case GESTURE_PINCH_UPDATE: | |
| 758 pinch_in_progress_ = true; | |
| 759 break; | |
| 760 case GESTURE_PINCH_END: | |
| 761 pinch_in_progress_ = false; | |
| 762 break; | |
| 763 case GESTURE_SCROLL_BEGIN: | |
| 764 touch_scroll_in_progress_ = true; | |
| 765 SendTapCancelIfNecessary(*current_down_event_); | |
| 766 break; | |
| 767 case GESTURE_SCROLL_END: | |
| 768 touch_scroll_in_progress_ = false; | |
| 769 break; | |
| 770 default: | |
| 771 break; | |
| 772 }; | |
| 773 | |
| 774 client_->OnGestureEvent(gesture); | |
| 775 } | |
| 776 | |
| 777 void GestureProvider::SendTapCancelIfNecessary(const MotionEvent& event) { | |
| 778 if (!needs_tap_ending_event_) | |
| 779 return; | |
| 780 current_longpress_time_ = base::TimeTicks(); | |
| 781 Send(CreateGesture(GESTURE_TAP_CANCEL, event)); | |
| 782 } | |
| 783 | |
| 784 bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) { | |
| 785 if (event.GetAction() == MotionEvent::ACTION_UP && | |
| 786 !current_longpress_time_.is_null() && | |
| 787 !scale_gesture_listener_->IsScaleGestureDetectionInProgress()) { | |
| 788 SendTapCancelIfNecessary(event); | |
| 789 GestureEventParams::Data long_tap_data; | |
| 790 long_tap_data.long_press.width = event.GetTouchMajor(); | |
| 791 long_tap_data.long_press.height = long_tap_data.long_press.width; | |
| 792 Send(CreateGesture(GESTURE_LONG_TAP, event, long_tap_data)); | |
| 793 return true; | |
| 794 } | |
| 795 return false; | |
| 796 } | |
| 797 | |
| 798 void GestureProvider::EndTouchScrollIfNecessary(base::TimeTicks time, | |
| 799 bool send_scroll_end_event) { | |
| 800 if (!touch_scroll_in_progress_) | |
| 801 return; | |
| 802 touch_scroll_in_progress_ = false; | |
| 803 if (send_scroll_end_event) | |
| 804 Send(CreateGesture(GESTURE_SCROLL_END, time, 0, 0)); | |
| 805 } | |
| 806 | |
| 807 } // namespace ui | |
| OLD | NEW |