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