Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: ui/events/gesture_detection/gesture_provider.cc

Issue 171773012: Port of Android platform gesture detection code to C++ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Code review Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW
« no previous file with comments | « ui/events/gesture_detection/gesture_provider.h ('k') | ui/events/gesture_detection/gesture_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698