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 |