Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / ui / events / gesture_detection / gesture_provider.cc
blobca66e788ac83bb1ffb967e84f9bae18daaa43c23
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.
5 #include "ui/events/gesture_detection/gesture_provider.h"
7 #include <cmath>
9 #include "base/auto_reset.h"
10 #include "base/trace_event/trace_event.h"
11 #include "ui/events/event_constants.h"
12 #include "ui/events/gesture_detection/gesture_event_data.h"
13 #include "ui/events/gesture_detection/gesture_listeners.h"
14 #include "ui/events/gesture_detection/motion_event.h"
15 #include "ui/events/gesture_detection/motion_event_generic.h"
16 #include "ui/events/gesture_detection/scale_gesture_listeners.h"
17 #include "ui/gfx/geometry/point_f.h"
19 namespace ui {
20 namespace {
22 // Double-tap drag zoom sensitivity (speed).
23 const float kDoubleTapDragZoomSpeed = 0.005f;
25 const char* GetMotionEventActionName(MotionEvent::Action action) {
26 switch (action) {
27 case MotionEvent::ACTION_NONE:
28 return "ACTION_NONE";
29 case MotionEvent::ACTION_POINTER_DOWN:
30 return "ACTION_POINTER_DOWN";
31 case MotionEvent::ACTION_POINTER_UP:
32 return "ACTION_POINTER_UP";
33 case MotionEvent::ACTION_DOWN:
34 return "ACTION_DOWN";
35 case MotionEvent::ACTION_UP:
36 return "ACTION_UP";
37 case MotionEvent::ACTION_CANCEL:
38 return "ACTION_CANCEL";
39 case MotionEvent::ACTION_MOVE:
40 return "ACTION_MOVE";
42 return "";
45 gfx::RectF ClampBoundingBox(const gfx::RectF& bounds,
46 float min_length,
47 float max_length) {
48 float width = bounds.width();
49 float height = bounds.height();
50 if (min_length) {
51 width = std::max(min_length, width);
52 height = std::max(min_length, height);
54 if (max_length) {
55 width = std::min(max_length, width);
56 height = std::min(max_length, height);
58 const gfx::PointF center = bounds.CenterPoint();
59 return gfx::RectF(
60 center.x() - width / 2.f, center.y() - height / 2.f, width, height);
63 } // namespace
65 // GestureProvider:::Config
67 GestureProvider::Config::Config()
68 : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)),
69 double_tap_support_for_platform_enabled(true),
70 gesture_begin_end_types_enabled(false),
71 min_gesture_bounds_length(0),
72 max_gesture_bounds_length(0) {
75 GestureProvider::Config::~Config() {
78 // GestureProvider::GestureListener
80 class GestureProvider::GestureListenerImpl : public ScaleGestureListener,
81 public GestureListener,
82 public DoubleTapListener {
83 public:
84 GestureListenerImpl(const GestureProvider::Config& config,
85 GestureProviderClient* client)
86 : config_(config),
87 client_(client),
88 gesture_detector_(config.gesture_detector_config, this, this),
89 scale_gesture_detector_(config.scale_gesture_detector_config, this),
90 snap_scroll_controller_(config.gesture_detector_config.touch_slop,
91 config.display.size()),
92 ignore_multitouch_zoom_events_(false),
93 ignore_single_tap_(false),
94 pinch_event_sent_(false),
95 scroll_event_sent_(false),
96 max_diameter_before_show_press_(0),
97 show_press_event_sent_(false) {}
99 void OnTouchEvent(const MotionEvent& event) {
100 const bool in_scale_gesture = IsScaleGestureDetectionInProgress();
101 snap_scroll_controller_.SetSnapScrollMode(event, in_scale_gesture);
102 if (in_scale_gesture)
103 SetIgnoreSingleTap(true);
105 const MotionEvent::Action action = event.GetAction();
106 if (action == MotionEvent::ACTION_DOWN) {
107 current_down_time_ = event.GetEventTime();
108 current_longpress_time_ = base::TimeTicks();
109 ignore_single_tap_ = false;
110 scroll_event_sent_ = false;
111 pinch_event_sent_ = false;
112 show_press_event_sent_ = false;
113 gesture_detector_.set_longpress_enabled(true);
114 tap_down_point_ = gfx::PointF(event.GetX(), event.GetY());
115 max_diameter_before_show_press_ = event.GetTouchMajor();
118 gesture_detector_.OnTouchEvent(event);
119 scale_gesture_detector_.OnTouchEvent(event);
121 if (action == MotionEvent::ACTION_UP ||
122 action == MotionEvent::ACTION_CANCEL) {
123 // Note: This call will have no effect if a fling was just generated, as
124 // |Fling()| will have already signalled an end to touch-scrolling.
125 if (scroll_event_sent_)
126 Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
127 current_down_time_ = base::TimeTicks();
128 } else if (action == MotionEvent::ACTION_MOVE) {
129 if (!show_press_event_sent_ && !scroll_event_sent_) {
130 max_diameter_before_show_press_ =
131 std::max(max_diameter_before_show_press_, event.GetTouchMajor());
136 void Send(GestureEventData gesture) {
137 DCHECK(!gesture.time.is_null());
138 // The only valid events that should be sent without an active touch
139 // sequence are SHOW_PRESS and TAP, potentially triggered by the double-tap
140 // delay timing out.
141 DCHECK(!current_down_time_.is_null() || gesture.type() == ET_GESTURE_TAP ||
142 gesture.type() == ET_GESTURE_SHOW_PRESS ||
143 gesture.type() == ET_GESTURE_BEGIN ||
144 gesture.type() == ET_GESTURE_END);
146 if (gesture.primary_tool_type == MotionEvent::TOOL_TYPE_UNKNOWN ||
147 gesture.primary_tool_type == MotionEvent::TOOL_TYPE_FINGER) {
148 gesture.details.set_bounding_box(
149 ClampBoundingBox(gesture.details.bounding_box_f(),
150 config_.min_gesture_bounds_length,
151 config_.max_gesture_bounds_length));
154 switch (gesture.type()) {
155 case ET_GESTURE_LONG_PRESS:
156 DCHECK(!IsScaleGestureDetectionInProgress());
157 current_longpress_time_ = gesture.time;
158 break;
159 case ET_GESTURE_LONG_TAP:
160 current_longpress_time_ = base::TimeTicks();
161 break;
162 case ET_GESTURE_SCROLL_BEGIN:
163 DCHECK(!scroll_event_sent_);
164 scroll_event_sent_ = true;
165 break;
166 case ET_GESTURE_SCROLL_END:
167 DCHECK(scroll_event_sent_);
168 if (pinch_event_sent_)
169 Send(GestureEventData(ET_GESTURE_PINCH_END, gesture));
170 scroll_event_sent_ = false;
171 break;
172 case ET_SCROLL_FLING_START:
173 DCHECK(scroll_event_sent_);
174 scroll_event_sent_ = false;
175 break;
176 case ET_GESTURE_PINCH_BEGIN:
177 DCHECK(!pinch_event_sent_);
178 if (!scroll_event_sent_)
179 Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture));
180 pinch_event_sent_ = true;
181 break;
182 case ET_GESTURE_PINCH_END:
183 DCHECK(pinch_event_sent_);
184 pinch_event_sent_ = false;
185 break;
186 case ET_GESTURE_SHOW_PRESS:
187 // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
188 // will start before the press gesture fires (from GestureDetector), in
189 // which case the press should simply be dropped.
190 if (pinch_event_sent_ || scroll_event_sent_)
191 return;
192 default:
193 break;
196 client_->OnGestureEvent(gesture);
197 GestureTouchUMAHistogram::RecordGestureEvent(gesture);
200 // ScaleGestureListener implementation.
201 bool OnScaleBegin(const ScaleGestureDetector& detector,
202 const MotionEvent& e) override {
203 if (ignore_multitouch_zoom_events_ && !detector.InDoubleTapMode())
204 return false;
205 return true;
208 void OnScaleEnd(const ScaleGestureDetector& detector,
209 const MotionEvent& e) override {
210 if (!pinch_event_sent_)
211 return;
212 Send(CreateGesture(ET_GESTURE_PINCH_END, e));
215 bool OnScale(const ScaleGestureDetector& detector,
216 const MotionEvent& e) override {
217 if (ignore_multitouch_zoom_events_ && !detector.InDoubleTapMode())
218 return false;
219 if (!pinch_event_sent_) {
220 Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
221 e.GetPointerId(),
222 e.GetToolType(),
223 detector.GetEventTime(),
224 detector.GetFocusX(),
225 detector.GetFocusY(),
226 detector.GetFocusX() + e.GetRawOffsetX(),
227 detector.GetFocusY() + e.GetRawOffsetY(),
228 e.GetPointerCount(),
229 GetBoundingBox(e, ET_GESTURE_PINCH_BEGIN),
230 e.GetFlags()));
233 if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
234 config_.scale_gesture_detector_config.min_pinch_update_span_delta) {
235 return false;
238 float scale = detector.GetScaleFactor();
239 if (scale == 1)
240 return true;
242 if (detector.InDoubleTapMode()) {
243 // Relative changes in the double-tap scale factor computed by |detector|
244 // diminish as the touch moves away from the original double-tap focus.
245 // For historical reasons, Chrome has instead adopted a scale factor
246 // computation that is invariant to the focal distance, where
247 // the scale delta remains constant if the touch velocity is constant.
248 float dy =
249 (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
250 scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
251 : 1.0f - kDoubleTapDragZoomSpeed,
252 std::abs(dy));
254 GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE);
255 pinch_details.set_scale(scale);
256 Send(CreateGesture(pinch_details,
257 e.GetPointerId(),
258 e.GetToolType(),
259 detector.GetEventTime(),
260 detector.GetFocusX(),
261 detector.GetFocusY(),
262 detector.GetFocusX() + e.GetRawOffsetX(),
263 detector.GetFocusY() + e.GetRawOffsetY(),
264 e.GetPointerCount(),
265 GetBoundingBox(e, pinch_details.type()),
266 e.GetFlags()));
267 return true;
270 // GestureListener implementation.
271 bool OnDown(const MotionEvent& e) override {
272 GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN);
273 Send(CreateGesture(tap_details, e));
275 // Return true to indicate that we want to handle touch.
276 return true;
279 bool OnScroll(const MotionEvent& e1,
280 const MotionEvent& e2,
281 float raw_distance_x,
282 float raw_distance_y) override {
283 float distance_x = raw_distance_x;
284 float distance_y = raw_distance_y;
285 if (!scroll_event_sent_ && e2.GetPointerCount() == 1) {
286 // Remove the touch slop region from the first scroll event to
287 // avoid a jump. Touch slop isn't used for multi-finger
288 // gestures, so in those cases we don't subtract the slop.
289 float distance =
290 std::sqrt(distance_x * distance_x + distance_y * distance_y);
291 float epsilon = 1e-3f;
292 if (distance > epsilon) {
293 float ratio =
294 std::max(0.f,
295 distance - config_.gesture_detector_config.touch_slop) /
296 distance;
297 distance_x *= ratio;
298 distance_y *= ratio;
302 snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
303 if (snap_scroll_controller_.IsSnappingScrolls()) {
304 if (snap_scroll_controller_.IsSnapHorizontal())
305 distance_y = 0;
306 else
307 distance_x = 0;
310 if (!distance_x && !distance_y)
311 return true;
313 if (!scroll_event_sent_) {
314 // Note that scroll start hints are in distance traveled, where
315 // scroll deltas are in the opposite direction.
316 GestureEventDetails scroll_details(
317 ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
319 // Use the co-ordinates from the touch down, as these co-ordinates are
320 // used to determine which layer the scroll should affect.
321 Send(CreateGesture(scroll_details, e2.GetPointerId(), e2.GetToolType(),
322 e2.GetEventTime(), e1.GetX(), e1.GetY(), e1.GetRawX(),
323 e1.GetRawY(), e2.GetPointerCount(),
324 GetBoundingBox(e2, scroll_details.type()),
325 e2.GetFlags()));
326 DCHECK(scroll_event_sent_);
329 GestureEventDetails scroll_details(ET_GESTURE_SCROLL_UPDATE, -distance_x,
330 -distance_y);
331 const gfx::RectF bounding_box = GetBoundingBox(e2, scroll_details.type());
332 const gfx::PointF center = bounding_box.CenterPoint();
333 const gfx::PointF raw_center =
334 center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
335 Send(CreateGesture(scroll_details, e2.GetPointerId(), e2.GetToolType(),
336 e2.GetEventTime(), center.x(), center.y(),
337 raw_center.x(), raw_center.y(), e2.GetPointerCount(),
338 bounding_box, e2.GetFlags()));
340 return true;
343 bool OnFling(const MotionEvent& e1,
344 const MotionEvent& e2,
345 float velocity_x,
346 float velocity_y) override {
347 if (snap_scroll_controller_.IsSnappingScrolls()) {
348 if (snap_scroll_controller_.IsSnapHorizontal()) {
349 velocity_y = 0;
350 } else {
351 velocity_x = 0;
355 if (!velocity_x && !velocity_y)
356 return true;
358 if (!scroll_event_sent_) {
359 // The native side needs a ET_GESTURE_SCROLL_BEGIN before
360 // ET_SCROLL_FLING_START to send the fling to the correct target.
361 // The distance traveled in one second is a reasonable scroll start hint.
362 GestureEventDetails scroll_details(
363 ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
364 Send(CreateGesture(scroll_details, e2));
367 GestureEventDetails fling_details(
368 ET_SCROLL_FLING_START, velocity_x, velocity_y);
369 Send(CreateGesture(fling_details, e2));
370 return true;
373 bool OnSwipe(const MotionEvent& e1,
374 const MotionEvent& e2,
375 float velocity_x,
376 float velocity_y) override {
377 GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y);
378 Send(CreateGesture(swipe_details, e2));
379 return true;
382 bool OnTwoFingerTap(const MotionEvent& e1, const MotionEvent& e2) override {
383 // The location of the two finger tap event should be the location of the
384 // primary pointer.
385 GestureEventDetails two_finger_tap_details(
386 ET_GESTURE_TWO_FINGER_TAP, e1.GetTouchMajor(), e1.GetTouchMajor());
387 Send(CreateGesture(two_finger_tap_details,
388 e2.GetPointerId(),
389 e2.GetToolType(),
390 e2.GetEventTime(),
391 e1.GetX(),
392 e1.GetY(),
393 e1.GetRawX(),
394 e1.GetRawY(),
395 e2.GetPointerCount(),
396 GetBoundingBox(e2, two_finger_tap_details.type()),
397 e2.GetFlags()));
398 return true;
401 void OnShowPress(const MotionEvent& e) override {
402 GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS);
403 show_press_event_sent_ = true;
404 Send(CreateGesture(show_press_details, e));
407 bool OnSingleTapUp(const MotionEvent& e) override {
408 // This is a hack to address the issue where user hovers
409 // over a link for longer than double_tap_timeout_, then
410 // OnSingleTapConfirmed() is not triggered. But we still
411 // want to trigger the tap event at UP. So we override
412 // OnSingleTapUp() in this case. This assumes singleTapUp
413 // gets always called before singleTapConfirmed.
414 if (!ignore_single_tap_) {
415 if (e.GetEventTime() - current_down_time_ >
416 config_.gesture_detector_config.double_tap_timeout) {
417 return OnSingleTapConfirmed(e);
418 } else if (!IsDoubleTapEnabled()) {
419 // If double-tap has been disabled, there is no need to wait
420 // for the double-tap timeout.
421 return OnSingleTapConfirmed(e);
422 } else {
423 // Notify Blink about this tapUp event anyway, when none of the above
424 // conditions applied.
425 Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED, e));
429 if (e.GetAction() == MotionEvent::ACTION_UP &&
430 !current_longpress_time_.is_null() &&
431 !IsScaleGestureDetectionInProgress()) {
432 GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP);
433 Send(CreateGesture(long_tap_details, e));
434 return true;
437 return false;
440 // DoubleTapListener implementation.
441 bool OnSingleTapConfirmed(const MotionEvent& e) override {
442 // Long taps in the edges of the screen have their events delayed by
443 // ContentViewHolder for tab swipe operations. As a consequence of the delay
444 // this method might be called after receiving the up event.
445 // These corner cases should be ignored.
446 if (ignore_single_tap_)
447 return true;
449 ignore_single_tap_ = true;
451 Send(CreateTapGesture(ET_GESTURE_TAP, e));
452 return true;
455 bool OnDoubleTap(const MotionEvent& e) override {
456 return scale_gesture_detector_.OnDoubleTap(e);
459 bool OnDoubleTapEvent(const MotionEvent& e) override {
460 switch (e.GetAction()) {
461 case MotionEvent::ACTION_DOWN:
462 gesture_detector_.set_longpress_enabled(false);
463 break;
465 case MotionEvent::ACTION_UP:
466 if (!IsPinchInProgress() && !IsScrollInProgress()) {
467 Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP, e));
468 return true;
470 break;
472 default:
473 break;
475 return false;
478 void OnLongPress(const MotionEvent& e) override {
479 DCHECK(!IsDoubleTapInProgress());
480 SetIgnoreSingleTap(true);
481 GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS);
482 Send(CreateGesture(long_press_details, e));
485 GestureEventData CreateGesture(const GestureEventDetails& details,
486 int motion_event_id,
487 MotionEvent::ToolType primary_tool_type,
488 base::TimeTicks time,
489 float x,
490 float y,
491 float raw_x,
492 float raw_y,
493 size_t touch_point_count,
494 const gfx::RectF& bounding_box,
495 int flags) {
496 return GestureEventData(details,
497 motion_event_id,
498 primary_tool_type,
499 time,
502 raw_x,
503 raw_y,
504 touch_point_count,
505 bounding_box,
506 flags);
509 GestureEventData CreateGesture(EventType type,
510 int motion_event_id,
511 MotionEvent::ToolType primary_tool_type,
512 base::TimeTicks time,
513 float x,
514 float y,
515 float raw_x,
516 float raw_y,
517 size_t touch_point_count,
518 const gfx::RectF& bounding_box,
519 int flags) {
520 return GestureEventData(GestureEventDetails(type),
521 motion_event_id,
522 primary_tool_type,
523 time,
526 raw_x,
527 raw_y,
528 touch_point_count,
529 bounding_box,
530 flags);
533 GestureEventData CreateGesture(const GestureEventDetails& details,
534 const MotionEvent& event) {
535 return GestureEventData(details,
536 event.GetPointerId(),
537 event.GetToolType(),
538 event.GetEventTime(),
539 event.GetX(),
540 event.GetY(),
541 event.GetRawX(),
542 event.GetRawY(),
543 event.GetPointerCount(),
544 GetBoundingBox(event, details.type()),
545 event.GetFlags());
548 GestureEventData CreateGesture(EventType type, const MotionEvent& event) {
549 return CreateGesture(GestureEventDetails(type), event);
552 GestureEventData CreateTapGesture(EventType type, const MotionEvent& event) {
553 // Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
554 // consistent with double tap behavior on a mobile viewport. See
555 // crbug.com/234986 for context.
556 GestureEventDetails details(type);
557 details.set_tap_count(1);
558 return CreateGesture(details, event);
561 gfx::RectF GetBoundingBox(const MotionEvent& event, EventType type) {
562 // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
563 float left = std::numeric_limits<float>::max();
564 float top = std::numeric_limits<float>::max();
565 float right = -std::numeric_limits<float>::max();
566 float bottom = -std::numeric_limits<float>::max();
567 for (size_t i = 0; i < event.GetPointerCount(); ++i) {
568 float x, y, diameter;
569 // Only for the show press and tap events, the bounding box is calculated
570 // based on the touch start point and the maximum diameter before the
571 // show press event is sent.
572 if (type == ET_GESTURE_SHOW_PRESS || type == ET_GESTURE_TAP ||
573 type == ET_GESTURE_TAP_UNCONFIRMED) {
574 DCHECK_EQ(0U, i);
575 diameter = max_diameter_before_show_press_;
576 x = tap_down_point_.x();
577 y = tap_down_point_.y();
578 } else {
579 diameter = event.GetTouchMajor(i);
580 x = event.GetX(i);
581 y = event.GetY(i);
583 x = x - diameter / 2;
584 y = y - diameter / 2;
585 left = std::min(left, x);
586 right = std::max(right, x + diameter);
587 top = std::min(top, y);
588 bottom = std::max(bottom, y + diameter);
590 return gfx::RectF(left, top, right - left, bottom - top);
593 void SetDoubleTapEnabled(bool enabled) {
594 DCHECK(!IsDoubleTapInProgress());
595 gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
598 void SetMultiTouchZoomEnabled(bool enabled) {
599 // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
600 // the detector from emitting further scale updates for the current touch
601 // sequence. Thus, if multitouch events are enabled in the middle of a
602 // gesture, it will only take effect with the next gesture.
603 ignore_multitouch_zoom_events_ = !enabled;
606 bool IsDoubleTapInProgress() const {
607 return gesture_detector_.is_double_tapping() ||
608 (IsScaleGestureDetectionInProgress() && InDoubleTapMode());
611 bool IsScrollInProgress() const { return scroll_event_sent_; }
613 bool IsPinchInProgress() const { return pinch_event_sent_; }
615 private:
616 bool IsScaleGestureDetectionInProgress() const {
617 return scale_gesture_detector_.IsInProgress();
620 bool InDoubleTapMode() const {
621 return scale_gesture_detector_.InDoubleTapMode();
624 bool IsDoubleTapEnabled() const {
625 return gesture_detector_.has_doubletap_listener();
628 void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
630 const GestureProvider::Config config_;
631 GestureProviderClient* const client_;
633 GestureDetector gesture_detector_;
634 ScaleGestureDetector scale_gesture_detector_;
635 SnapScrollController snap_scroll_controller_;
637 base::TimeTicks current_down_time_;
639 // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
640 // opened after a GESTURE_LONG_PRESS, this is used to insert a
641 // GESTURE_TAP_CANCEL for removing any ::active styling.
642 base::TimeTicks current_longpress_time_;
644 // Completely silence multi-touch (pinch) scaling events. Used in WebView when
645 // zoom support is turned off.
646 bool ignore_multitouch_zoom_events_;
648 // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
649 // always_in_tap_region_ is not reset. So when the last finger is up,
650 // |OnSingleTapUp()| will be mistakenly fired.
651 bool ignore_single_tap_;
653 // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
654 // current touch sequence.
655 bool pinch_event_sent_;
656 bool scroll_event_sent_;
658 // Only track the maximum diameter before the show press event has been
659 // sent and a tap must still be possible for this touch sequence.
660 float max_diameter_before_show_press_;
662 gfx::PointF tap_down_point_;
664 // Tracks whether an ET_GESTURE_SHOW_PRESS event has been sent for this touch
665 // sequence.
666 bool show_press_event_sent_;
668 DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
671 // GestureProvider
673 GestureProvider::GestureProvider(const Config& config,
674 GestureProviderClient* client)
675 : double_tap_support_for_page_(true),
676 double_tap_support_for_platform_(
677 config.double_tap_support_for_platform_enabled),
678 gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) {
679 DCHECK(client);
680 DCHECK(!config.min_gesture_bounds_length ||
681 !config.max_gesture_bounds_length ||
682 config.min_gesture_bounds_length <= config.max_gesture_bounds_length);
683 TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
684 gesture_listener_.reset(new GestureListenerImpl(config, client));
685 UpdateDoubleTapDetectionSupport();
688 GestureProvider::~GestureProvider() {
691 bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
692 TRACE_EVENT1("input",
693 "GestureProvider::OnTouchEvent",
694 "action",
695 GetMotionEventActionName(event.GetAction()));
697 DCHECK_NE(0u, event.GetPointerCount());
699 if (!CanHandle(event))
700 return false;
702 OnTouchEventHandlingBegin(event);
703 gesture_listener_->OnTouchEvent(event);
704 OnTouchEventHandlingEnd(event);
705 uma_histogram_.RecordTouchEvent(event);
706 return true;
709 void GestureProvider::ResetDetection() {
710 MotionEventGeneric generic_cancel_event(MotionEvent::ACTION_CANCEL,
711 base::TimeTicks::Now(),
712 PointerProperties());
713 OnTouchEvent(generic_cancel_event);
716 void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
717 gesture_listener_->SetMultiTouchZoomEnabled(enabled);
720 void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
721 if (double_tap_support_for_platform_ == enabled)
722 return;
723 double_tap_support_for_platform_ = enabled;
724 UpdateDoubleTapDetectionSupport();
727 void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
728 if (double_tap_support_for_page_ == enabled)
729 return;
730 double_tap_support_for_page_ = enabled;
731 UpdateDoubleTapDetectionSupport();
734 bool GestureProvider::IsScrollInProgress() const {
735 return gesture_listener_->IsScrollInProgress();
738 bool GestureProvider::IsPinchInProgress() const {
739 return gesture_listener_->IsPinchInProgress();
742 bool GestureProvider::IsDoubleTapInProgress() const {
743 return gesture_listener_->IsDoubleTapInProgress();
746 bool GestureProvider::CanHandle(const MotionEvent& event) const {
747 // Aura requires one cancel event per touch point, whereas Android requires
748 // one cancel event per touch sequence. Thus we need to allow extra cancel
749 // events.
750 return current_down_event_ || event.GetAction() == MotionEvent::ACTION_DOWN ||
751 event.GetAction() == MotionEvent::ACTION_CANCEL;
754 void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
755 switch (event.GetAction()) {
756 case MotionEvent::ACTION_DOWN:
757 current_down_event_ = event.Clone();
758 if (gesture_begin_end_types_enabled_)
759 gesture_listener_->Send(
760 gesture_listener_->CreateGesture(ET_GESTURE_BEGIN, event));
761 break;
762 case MotionEvent::ACTION_POINTER_DOWN:
763 if (gesture_begin_end_types_enabled_) {
764 const int action_index = event.GetActionIndex();
765 gesture_listener_->Send(gesture_listener_->CreateGesture(
766 ET_GESTURE_BEGIN,
767 event.GetPointerId(),
768 event.GetToolType(),
769 event.GetEventTime(),
770 event.GetX(action_index),
771 event.GetY(action_index),
772 event.GetRawX(action_index),
773 event.GetRawY(action_index),
774 event.GetPointerCount(),
775 gesture_listener_->GetBoundingBox(event, ET_GESTURE_BEGIN),
776 event.GetFlags()));
778 break;
779 case MotionEvent::ACTION_POINTER_UP:
780 case MotionEvent::ACTION_UP:
781 case MotionEvent::ACTION_CANCEL:
782 case MotionEvent::ACTION_MOVE:
783 break;
784 case MotionEvent::ACTION_NONE:
785 NOTREACHED();
786 break;
790 void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
791 switch (event.GetAction()) {
792 case MotionEvent::ACTION_UP:
793 case MotionEvent::ACTION_CANCEL: {
794 if (gesture_begin_end_types_enabled_)
795 gesture_listener_->Send(
796 gesture_listener_->CreateGesture(ET_GESTURE_END, event));
798 current_down_event_.reset();
800 UpdateDoubleTapDetectionSupport();
801 break;
803 case MotionEvent::ACTION_POINTER_UP:
804 if (gesture_begin_end_types_enabled_)
805 gesture_listener_->Send(
806 gesture_listener_->CreateGesture(ET_GESTURE_END, event));
807 break;
808 case MotionEvent::ACTION_DOWN:
809 case MotionEvent::ACTION_POINTER_DOWN:
810 case MotionEvent::ACTION_MOVE:
811 break;
812 case MotionEvent::ACTION_NONE:
813 NOTREACHED();
814 break;
818 void GestureProvider::UpdateDoubleTapDetectionSupport() {
819 // The GestureDetector requires that any provided DoubleTapListener remain
820 // attached to it for the duration of a touch sequence. Defer any potential
821 // null'ing of the listener until the sequence has ended.
822 if (current_down_event_)
823 return;
825 const bool double_tap_enabled =
826 double_tap_support_for_page_ && double_tap_support_for_platform_;
827 gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
830 } // namespace ui