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

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

Issue 128613003: [Tracking Patch] Unified gesture detection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Cleanup 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 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698