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