1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ui/events/gesture_detection/gesture_provider.h"
- #include <stddef.h>
- #include <cmath>
- #include "base/auto_reset.h"
- #include "base/memory/raw_ptr.h"
- #include "base/time/time.h"
- #include "base/trace_event/trace_event.h"
- #include "ui/events/event_constants.h"
- #include "ui/events/gesture_detection/gesture_configuration.h"
- #include "ui/events/gesture_detection/gesture_event_data.h"
- #include "ui/events/gesture_detection/gesture_listeners.h"
- #include "ui/events/gesture_detection/motion_event.h"
- #include "ui/events/gesture_detection/motion_event_generic.h"
- #include "ui/events/gesture_detection/scale_gesture_listeners.h"
- #include "ui/events/types/event_type.h"
- #include "ui/gfx/geometry/point_f.h"
- #include "ui/gfx/geometry/vector2d_f.h"
- namespace ui {
- namespace {
- // Double-tap drag zoom sensitivity (speed).
- const float kDoubleTapDragZoomSpeed = 0.005f;
- const char* GetMotionEventActionName(MotionEvent::Action action) {
- switch (action) {
- case MotionEvent::Action::NONE:
- return "Action::NONE";
- case MotionEvent::Action::POINTER_DOWN:
- return "Action::POINTER_DOWN";
- case MotionEvent::Action::POINTER_UP:
- return "Action::POINTER_UP";
- case MotionEvent::Action::DOWN:
- return "Action::DOWN";
- case MotionEvent::Action::UP:
- return "Action::UP";
- case MotionEvent::Action::CANCEL:
- return "Action::CANCEL";
- case MotionEvent::Action::MOVE:
- return "Action::MOVE";
- case MotionEvent::Action::HOVER_ENTER:
- return "Action::HOVER_ENTER";
- case MotionEvent::Action::HOVER_EXIT:
- return "Action::HOVER_EXIT";
- case MotionEvent::Action::HOVER_MOVE:
- return "Action::HOVER_MOVE";
- case MotionEvent::Action::BUTTON_PRESS:
- return "Action::BUTTON_PRESS";
- case MotionEvent::Action::BUTTON_RELEASE:
- return "Action::BUTTON_RELEASE";
- }
- return "";
- }
- gfx::RectF ClampBoundingBox(const gfx::RectF& bounds,
- float min_length,
- float max_length) {
- float width = bounds.width();
- float height = bounds.height();
- if (min_length) {
- width = std::max(min_length, width);
- height = std::max(min_length, height);
- }
- if (max_length) {
- width = std::min(max_length, width);
- height = std::min(max_length, height);
- }
- const gfx::PointF center = bounds.CenterPoint();
- return gfx::RectF(
- center.x() - width / 2.f, center.y() - height / 2.f, width, height);
- }
- } // namespace
- // GestureProviderClient:
- bool GestureProviderClient::RequiresDoubleTapGestureEvents() const {
- return false;
- }
- // GestureProvider:::Config
- GestureProvider::Config::Config()
- : display(display::kInvalidDisplayId, gfx::Rect(1, 1)),
- double_tap_support_for_platform_enabled(true),
- gesture_begin_end_types_enabled(false),
- min_gesture_bounds_length(0),
- max_gesture_bounds_length(0) {}
- GestureProvider::Config::Config(const Config& other) = default;
- GestureProvider::Config::~Config() {
- }
- // GestureProvider::GestureListener
- class GestureProvider::GestureListenerImpl : public ScaleGestureListener,
- public GestureListener,
- public DoubleTapListener {
- public:
- GestureListenerImpl(const GestureProvider::Config& config,
- GestureProviderClient* client,
- GestureProvider* gesture_provider)
- : config_(config),
- client_(client),
- gesture_provider_(gesture_provider),
- gesture_detector_(config.gesture_detector_config, this, this),
- scale_gesture_detector_(config.scale_gesture_detector_config, this),
- snap_scroll_controller_(config.gesture_detector_config.touch_slop,
- gfx::SizeF(config.display.size())),
- ignore_multitouch_zoom_events_(false),
- ignore_single_tap_(false),
- pinch_event_sent_(false),
- scroll_event_sent_(false),
- max_diameter_before_show_press_(0),
- show_press_event_sent_(false) {}
- GestureListenerImpl(const GestureListenerImpl&) = delete;
- GestureListenerImpl& operator=(const GestureListenerImpl&) = delete;
- void OnTouchEvent(const MotionEvent& event) {
- const bool in_scale_gesture = IsScaleGestureDetectionInProgress();
- snap_scroll_controller_.SetSnapScrollMode(event, in_scale_gesture);
- if (in_scale_gesture)
- SetIgnoreSingleTap(true);
- const MotionEvent::Action action = event.GetAction();
- if (action == MotionEvent::Action::DOWN) {
- current_down_action_event_time_ = event.GetEventTime();
- DCHECK(gesture_provider_->current_down_event());
- current_down_action_unique_touch_event_id_ =
- gesture_provider_->current_down_event()->GetUniqueEventId();
- current_longpress_time_ = base::TimeTicks();
- ignore_single_tap_ = false;
- scroll_event_sent_ = false;
- pinch_event_sent_ = false;
- show_press_event_sent_ = false;
- gesture_detector_.set_press_and_hold_enabled(true);
- tap_down_point_ = gfx::PointF(event.GetX(), event.GetY());
- max_diameter_before_show_press_ = event.GetTouchMajor();
- }
- gesture_detector_.OnTouchEvent(event,
- client_->RequiresDoubleTapGestureEvents());
- scale_gesture_detector_.OnTouchEvent(event);
- if (action == MotionEvent::Action::UP ||
- action == MotionEvent::Action::CANCEL) {
- // Note: This call will have no effect if a fling was just generated, as
- // |Fling()| will have already signalled an end to touch-scrolling.
- if (scroll_event_sent_)
- Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
- // If this was the last pointer that was canceled or lifted reset the
- // |current_down_action_event_time_| to indicate no sequence is going on.
- if (action != MotionEvent::Action::CANCEL ||
- !GestureConfiguration::GetInstance()
- ->single_pointer_cancel_enabled() ||
- event.GetPointerCount() == 1)
- current_down_action_event_time_ = base::TimeTicks();
- } else if (action == MotionEvent::Action::MOVE) {
- if (!show_press_event_sent_ && !scroll_event_sent_) {
- max_diameter_before_show_press_ =
- std::max(max_diameter_before_show_press_, event.GetTouchMajor());
- }
- }
- }
- void UpdateStateForEventPost(GestureEventData gesture) {
- switch (gesture.type()) {
- case ET_GESTURE_LONG_PRESS:
- DCHECK(!IsScaleGestureDetectionInProgress());
- current_longpress_time_ = gesture.time;
- break;
- case ET_GESTURE_LONG_TAP:
- current_longpress_time_ = base::TimeTicks();
- break;
- case ET_GESTURE_SCROLL_BEGIN:
- DCHECK(!scroll_event_sent_);
- scroll_event_sent_ = true;
- break;
- case ET_GESTURE_SCROLL_END:
- DCHECK(scroll_event_sent_);
- scroll_event_sent_ = false;
- break;
- case ET_SCROLL_FLING_START:
- DCHECK(scroll_event_sent_);
- scroll_event_sent_ = false;
- break;
- case ET_GESTURE_PINCH_BEGIN:
- DCHECK(!pinch_event_sent_);
- pinch_event_sent_ = true;
- break;
- case ET_GESTURE_PINCH_END:
- DCHECK(pinch_event_sent_);
- pinch_event_sent_ = false;
- break;
- case ET_GESTURE_SHOW_PRESS:
- // It's possible that a double-tap drag zoom (from ScaleGestureDetector)
- // will start before the press gesture fires (from GestureDetector), in
- // which case the press should simply be dropped.
- if (pinch_event_sent_ || scroll_event_sent_)
- return;
- break;
- default:
- break;
- };
- }
- // `should_update` indicates whether sending `gesture` should update the
- // internal states.
- void SendImpl(GestureEventData gesture, bool should_update) {
- DCHECK(!gesture.time.is_null());
- // The only valid events that should be sent without an active touch
- // sequence are SHOW_PRESS, TAP and TAP_CANCEL, potentially triggered by
- // the double-tap delay timing out or being cancelled.
- DCHECK(!current_down_action_event_time_.is_null() ||
- gesture.type() == ET_GESTURE_TAP ||
- gesture.type() == ET_GESTURE_SHOW_PRESS ||
- gesture.type() == ET_GESTURE_TAP_CANCEL ||
- gesture.type() == ET_GESTURE_BEGIN ||
- gesture.type() == ET_GESTURE_END);
- if (gesture.primary_tool_type == MotionEvent::ToolType::UNKNOWN ||
- gesture.primary_tool_type == MotionEvent::ToolType::FINGER) {
- gesture.details.set_bounding_box(ClampBoundingBox(
- gesture.details.bounding_box_f(), config_.min_gesture_bounds_length,
- config_.max_gesture_bounds_length));
- }
- // Sending one gesture event may trigger propagation of another.
- switch (gesture.type()) {
- case ET_GESTURE_SCROLL_END:
- DCHECK(scroll_event_sent_);
- if (pinch_event_sent_)
- SendImpl(GestureEventData(ET_GESTURE_PINCH_END, gesture),
- should_update);
- break;
- case ET_GESTURE_PINCH_BEGIN:
- DCHECK(!pinch_event_sent_);
- if (!scroll_event_sent_ &&
- !scale_gesture_detector_.InAnchoredScaleMode()) {
- SendImpl(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture),
- should_update);
- }
- break;
- default:
- break;
- }
- if (should_update) {
- UpdateStateForEventPost(gesture);
- GestureTouchUMAHistogram::RecordGestureEvent(gesture);
- }
- client_->OnGestureEvent(gesture);
- }
- void Send(GestureEventData gesture) {
- SendImpl(gesture, /*should_update=*/true);
- }
- void SendSynthesizedEndEvents() {
- MotionEventGeneric generic_cancel_event(MotionEvent::Action::CANCEL,
- base::TimeTicks::Now(),
- PointerProperties());
- // Sending the synthesized end events should not update the internal states.
- // Because this function may be called when a new event handler replaces the
- // old one. In that scenario, the old event handler is informed of the
- // gesture end through the synthesized end events while the new handler
- // should handle the incoming gestures.
- if (scroll_event_sent_) {
- SendImpl(CreateGesture(ET_GESTURE_SCROLL_END, generic_cancel_event),
- /*should_update=*/false);
- }
- SendImpl(CreateGesture(ET_GESTURE_END, generic_cancel_event),
- /*should_update=*/false);
- }
- // ScaleGestureListener implementation.
- bool OnScaleBegin(const ScaleGestureDetector& detector,
- const MotionEvent& e) override {
- if (ignore_multitouch_zoom_events_ && !detector.InAnchoredScaleMode())
- return false;
- return true;
- }
- void OnScaleEnd(const ScaleGestureDetector& detector,
- const MotionEvent& e) override {
- if (!pinch_event_sent_)
- return;
- Send(CreateGesture(ET_GESTURE_PINCH_END, e));
- }
- bool OnScale(const ScaleGestureDetector& detector,
- const MotionEvent& e) override {
- if (ignore_multitouch_zoom_events_ && !detector.InAnchoredScaleMode())
- return false;
- bool first_scale = false;
- if (!pinch_event_sent_) {
- first_scale = true;
- GestureEventDetails details(ET_GESTURE_PINCH_BEGIN);
- details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(
- details, e.GetPointerId(), e.GetToolType(), detector.GetEventTime(),
- detector.GetFocusX(), detector.GetFocusY(),
- detector.GetFocusX() + e.GetRawOffsetX(),
- detector.GetFocusY() + e.GetRawOffsetY(), e.GetPointerCount(),
- GetBoundingBox(e, ET_GESTURE_PINCH_BEGIN), e.GetFlags()));
- }
- if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
- config_.scale_gesture_detector_config.min_pinch_update_span_delta) {
- return false;
- }
- float scale = detector.GetScaleFactor();
- if (scale == 1)
- return true;
- if (detector.InAnchoredScaleMode()) {
- // Relative changes in the double-tap scale factor computed by |detector|
- // diminish as the touch moves away from the original double-tap focus.
- // For historical reasons, Chrome has instead adopted a scale factor
- // computation that is invariant to the focal distance, where
- // the scale delta remains constant if the touch velocity is constant.
- // Note: Because we calculate the scale here manually based on the
- // y-span, but the scale factor accounts for slop in the first previous
- // span, we manaully reproduce the behavior here for previous span y.
- float prev_y = first_scale
- ? config_.gesture_detector_config.touch_slop * 2
- : detector.GetPreviousSpanY();
- float dy = (detector.GetCurrentSpanY() - prev_y) * 0.5f;
- scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
- : 1.0f - kDoubleTapDragZoomSpeed,
- std::abs(dy));
- }
- GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE);
- pinch_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- pinch_details.set_scale(scale);
- pinch_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(pinch_details,
- e.GetPointerId(),
- e.GetToolType(),
- detector.GetEventTime(),
- detector.GetFocusX(),
- detector.GetFocusY(),
- detector.GetFocusX() + e.GetRawOffsetX(),
- detector.GetFocusY() + e.GetRawOffsetY(),
- e.GetPointerCount(),
- GetBoundingBox(e, pinch_details.type()),
- e.GetFlags()));
- return true;
- }
- // GestureListener implementation.
- bool OnDown(const MotionEvent& e) override {
- GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN);
- tap_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- tap_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(tap_details, e));
- // Return true to indicate that we want to handle touch.
- return true;
- }
- bool OnScroll(const MotionEvent& e1,
- const MotionEvent& e2,
- const MotionEvent& secondary_pointer_down,
- float raw_distance_x,
- float raw_distance_y) override {
- float distance_x = raw_distance_x;
- float distance_y = raw_distance_y;
- if (!scroll_event_sent_ && e2.GetPointerCount() < 3) {
- // Remove the touch slop region from the first scroll event to avoid a
- // jump. Touch slop isn't used for scroll gestures with greater than 2
- // pointers down, in those cases we don't subtract the slop.
- gfx::Vector2dF delta =
- ComputeFirstScrollDelta(e1, e2, secondary_pointer_down);
- distance_x = delta.x();
- distance_y = delta.y();
- }
- snap_scroll_controller_.UpdateSnapScrollMode(distance_x, distance_y);
- if (snap_scroll_controller_.IsSnappingScrolls()) {
- if (snap_scroll_controller_.IsSnapHorizontal())
- distance_y = 0;
- else
- distance_x = 0;
- }
- if (!distance_x && !distance_y)
- return true;
- if (!scroll_event_sent_) {
- // Note that scroll start hints are in distance traveled, where
- // scroll deltas are in the opposite direction.
- GestureEventDetails scroll_details(ET_GESTURE_SCROLL_BEGIN, -distance_x,
- -distance_y);
- scroll_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- scroll_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- // Scroll focus point always starts with the first touch down point.
- scroll_focus_point_.SetPoint(e1.GetX(), e1.GetY());
- // Use the co-ordinates from the touch down, as these co-ordinates are
- // used to determine which layer the scroll should affect.
- Send(CreateGesture(scroll_details, e2.GetPointerId(), e2.GetToolType(),
- e2.GetEventTime(), e1.GetX(), e1.GetY(), e1.GetRawX(),
- e1.GetRawY(), e2.GetPointerCount(),
- GetBoundingBox(e2, scroll_details.type()),
- e2.GetFlags()));
- DCHECK(scroll_event_sent_);
- }
- scroll_focus_point_.SetPoint(scroll_focus_point_.x() - raw_distance_x,
- scroll_focus_point_.y() - raw_distance_y);
- GestureEventDetails scroll_details(ET_GESTURE_SCROLL_UPDATE, -distance_x,
- -distance_y);
- scroll_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- scroll_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- const gfx::RectF bounding_box = GetBoundingBox(e2, scroll_details.type());
- const gfx::PointF raw_center =
- scroll_focus_point_ +
- gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
- Send(CreateGesture(scroll_details, e2.GetPointerId(), e2.GetToolType(),
- e2.GetEventTime(), scroll_focus_point_.x(),
- scroll_focus_point_.y(), raw_center.x(), raw_center.y(),
- e2.GetPointerCount(), bounding_box, e2.GetFlags()));
- return true;
- }
- bool OnFling(const MotionEvent& e1,
- const MotionEvent& e2,
- float velocity_x,
- float velocity_y) override {
- if (snap_scroll_controller_.IsSnappingScrolls()) {
- if (snap_scroll_controller_.IsSnapHorizontal()) {
- velocity_y = 0;
- } else {
- velocity_x = 0;
- }
- }
- if (!velocity_x && !velocity_y)
- return true;
- DCHECK(scroll_event_sent_);
- if (!scroll_event_sent_) {
- // The native side needs a ET_GESTURE_SCROLL_BEGIN before
- // ET_SCROLL_FLING_START to send the fling to the correct target.
- // The distance traveled in one second is a reasonable scroll start hint.
- GestureEventDetails scroll_details(
- ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
- scroll_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- scroll_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(scroll_details, e2));
- }
- GestureEventDetails fling_details(
- ET_SCROLL_FLING_START, velocity_x, velocity_y);
- fling_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- fling_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(fling_details, e2));
- return true;
- }
- bool OnSwipe(const MotionEvent& e1,
- const MotionEvent& e2,
- float velocity_x,
- float velocity_y) override {
- GestureEventDetails swipe_details(ET_GESTURE_SWIPE, velocity_x, velocity_y);
- swipe_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- swipe_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(swipe_details, e2));
- return true;
- }
- bool OnTwoFingerTap(const MotionEvent& e1, const MotionEvent& e2) override {
- // The location of the two finger tap event should be the location of the
- // primary pointer.
- GestureEventDetails two_finger_tap_details(
- ET_GESTURE_TWO_FINGER_TAP, e1.GetTouchMajor(), e1.GetTouchMajor());
- two_finger_tap_details.set_device_type(
- GestureDeviceType::DEVICE_TOUCHSCREEN);
- two_finger_tap_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(two_finger_tap_details,
- e2.GetPointerId(),
- e2.GetToolType(),
- e2.GetEventTime(),
- e1.GetX(),
- e1.GetY(),
- e1.GetRawX(),
- e1.GetRawY(),
- e2.GetPointerCount(),
- GetBoundingBox(e2, two_finger_tap_details.type()),
- e2.GetFlags()));
- return true;
- }
- void OnTapCancel(const MotionEvent& e) override {
- GestureEventDetails tap_cancel_details(ET_GESTURE_TAP_CANCEL);
- tap_cancel_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- tap_cancel_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(ET_GESTURE_TAP_CANCEL, e));
- }
- void OnShowPress(const MotionEvent& e) override {
- GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS);
- show_press_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- show_press_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- show_press_event_sent_ = true;
- Send(CreateGesture(show_press_details, e));
- }
- bool OnSingleTapUp(const MotionEvent& e, int tap_count) override {
- // This is a hack to address the issue where user hovers
- // over a link for longer than double_tap_timeout_, then
- // OnSingleTapConfirmed() is not triggered. But we still
- // want to trigger the tap event at UP. So we override
- // OnSingleTapUp() in this case. This assumes singleTapUp
- // gets always called before singleTapConfirmed.
- if (!ignore_single_tap_) {
- if (e.GetEventTime() - current_down_action_event_time_ >
- config_.gesture_detector_config.double_tap_timeout) {
- return OnSingleTapImpl(e, tap_count);
- } else if (!IsDoubleTapEnabled()) {
- // If double-tap has been disabled, there is no need to wait
- // for the double-tap timeout.
- return OnSingleTapImpl(e, tap_count);
- } else {
- // Notify Blink about this tapUp event anyway, when none of the above
- // conditions applied.
- Send(CreateTapGesture(ET_GESTURE_TAP_UNCONFIRMED, e, 1));
- }
- }
- if (e.GetAction() == MotionEvent::Action::UP &&
- !current_longpress_time_.is_null() &&
- !IsScaleGestureDetectionInProgress()) {
- GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP);
- long_tap_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- long_tap_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(long_tap_details, e));
- return true;
- }
- return false;
- }
- // DoubleTapListener implementation.
- bool OnSingleTapConfirmed(const MotionEvent& e) override {
- return OnSingleTapImpl(e, 1);
- }
- bool OnDoubleTap(const MotionEvent& e) override {
- return scale_gesture_detector_.OnDoubleTap(e);
- }
- bool OnDoubleTapEvent(const MotionEvent& e) override {
- switch (e.GetAction()) {
- case MotionEvent::Action::DOWN:
- gesture_detector_.set_press_and_hold_enabled(false);
- break;
- case MotionEvent::Action::UP:
- if (!IsPinchInProgress() && !IsScrollInProgress()) {
- Send(CreateTapGesture(ET_GESTURE_DOUBLE_TAP, e, 1));
- return true;
- }
- break;
- default:
- break;
- }
- return false;
- }
- void OnShortPress(const MotionEvent& e) override {
- DCHECK(!IsDoubleTapInProgress());
- GestureEventDetails short_press_details(ET_GESTURE_SHORT_PRESS);
- short_press_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- short_press_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(short_press_details, e));
- }
- void OnLongPress(const MotionEvent& e) override {
- DCHECK(!IsDoubleTapInProgress());
- SetIgnoreSingleTap(true);
- GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS);
- long_press_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- long_press_details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- Send(CreateGesture(long_press_details, e));
- }
- GestureEventData CreateGesture(const GestureEventDetails& details,
- int motion_event_id,
- MotionEvent::ToolType primary_tool_type,
- base::TimeTicks time,
- float x,
- float y,
- float raw_x,
- float raw_y,
- size_t touch_point_count,
- const gfx::RectF& bounding_box,
- int flags) const {
- return GestureEventData(details,
- motion_event_id,
- primary_tool_type,
- time,
- x,
- y,
- raw_x,
- raw_y,
- touch_point_count,
- bounding_box,
- flags,
- 0U);
- }
- GestureEventData CreateGesture(const GestureEventDetails& details,
- const MotionEvent& event) const {
- return GestureEventData(details, event.GetPointerId(), event.GetToolType(),
- event.GetEventTime(), event.GetX(), event.GetY(),
- event.GetRawX(), event.GetRawY(),
- event.GetPointerCount(),
- GetBoundingBox(event, details.type()),
- event.GetFlags(), event.GetUniqueEventId());
- }
- GestureEventData CreateGesture(EventType type,
- const MotionEvent& event) const {
- GestureEventDetails details(type);
- details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- return CreateGesture(details, event);
- }
- GestureEventData CreateTapGesture(EventType type,
- const MotionEvent& event,
- int tap_count) const {
- DCHECK_GE(tap_count, 0);
- GestureEventDetails details(type);
- details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- details.set_tap_count(tap_count);
- details.set_primary_unique_touch_event_id(
- current_down_action_unique_touch_event_id_);
- return CreateGesture(details, event);
- }
- gfx::RectF GetBoundingBox(const MotionEvent& event, EventType type) const {
- // Can't use gfx::RectF::Union, as it ignores touches with a radius of 0.
- float left = std::numeric_limits<float>::max();
- float top = std::numeric_limits<float>::max();
- float right = -std::numeric_limits<float>::max();
- float bottom = -std::numeric_limits<float>::max();
- for (size_t i = 0; i < event.GetPointerCount(); ++i) {
- float x, y, diameter;
- // Only for the show press and tap events, the bounding box is calculated
- // based on the touch start point and the maximum diameter before the
- // show press event is sent.
- if (type == ET_GESTURE_SHOW_PRESS || type == ET_GESTURE_TAP ||
- type == ET_GESTURE_TAP_UNCONFIRMED) {
- DCHECK_EQ(0U, i);
- diameter = max_diameter_before_show_press_;
- x = tap_down_point_.x();
- y = tap_down_point_.y();
- } else {
- diameter = event.GetTouchMajor(i);
- x = event.GetX(i);
- y = event.GetY(i);
- }
- x = x - diameter / 2;
- y = y - diameter / 2;
- left = std::min(left, x);
- right = std::max(right, x + diameter);
- top = std::min(top, y);
- bottom = std::max(bottom, y + diameter);
- }
- return gfx::RectF(left, top, right - left, bottom - top);
- }
- void SetDoubleTapEnabled(bool enabled) {
- DCHECK(!IsDoubleTapInProgress());
- gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
- }
- void SetMultiTouchZoomEnabled(bool enabled) {
- // Note that returning false from |OnScaleBegin()| or |OnScale()| prevents
- // the detector from emitting further scale updates for the current touch
- // sequence. Thus, if multitouch events are enabled in the middle of a
- // gesture, it will only take effect with the next gesture.
- ignore_multitouch_zoom_events_ = !enabled;
- }
- bool IsDoubleTapInProgress() const {
- return gesture_detector_.is_double_tapping() ||
- (IsScaleGestureDetectionInProgress() && InAnchoredScaleMode());
- }
- bool IsScrollInProgress() const { return scroll_event_sent_; }
- bool IsPinchInProgress() const { return pinch_event_sent_; }
- private:
- bool OnSingleTapImpl(const MotionEvent& e, int tap_count) {
- // Long taps in the edges of the screen have their events delayed by
- // ContentViewHolder for tab swipe operations. As a consequence of the delay
- // this method might be called after receiving the up event.
- // These corner cases should be ignored.
- if (ignore_single_tap_)
- return true;
- ignore_single_tap_ = true;
- Send(CreateTapGesture(ET_GESTURE_TAP, e, tap_count));
- return true;
- }
- bool IsScaleGestureDetectionInProgress() const {
- return scale_gesture_detector_.IsInProgress();
- }
- bool InAnchoredScaleMode() const {
- return scale_gesture_detector_.InAnchoredScaleMode();
- }
- bool IsDoubleTapEnabled() const {
- return gesture_detector_.has_doubletap_listener() &&
- client_->RequiresDoubleTapGestureEvents();
- }
- void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
- gfx::Vector2dF SubtractSlopRegion(const float dx, const float dy) {
- float distance = std::sqrt(dx * dx + dy * dy);
- float epsilon = 1e-3f;
- if (distance > epsilon) {
- float ratio =
- std::max(0.f, distance - config_.gesture_detector_config.touch_slop) /
- distance;
- gfx::Vector2dF delta(dx * ratio, dy * ratio);
- return delta;
- }
- gfx::Vector2dF delta(dx, dy);
- return delta;
- }
- // When any of the currently down pointers exceeds its slop region
- // for the first time, scroll delta is adjusted.
- // The new deltas are calculated for each pointer individually,
- // and the final scroll delta is the average over all delta values.
- gfx::Vector2dF ComputeFirstScrollDelta(
- const MotionEvent& ev1,
- const MotionEvent& ev2,
- const MotionEvent& secondary_pointer_down) {
- // If there are more than two down pointers, tapping is not possible,
- // so Slop region is not deducted.
- DCHECK(ev2.GetPointerCount() < 3);
- gfx::Vector2dF delta(0, 0);
- for (size_t i = 0; i < ev2.GetPointerCount(); i++) {
- const int pointer_id = ev2.GetPointerId(i);
- const MotionEvent* source_pointer_down_event =
- gesture_detector_.GetSourcePointerDownEvent(
- ev1, &secondary_pointer_down, pointer_id);
- if (!source_pointer_down_event)
- continue;
- int source_index =
- source_pointer_down_event->FindPointerIndexOfId(pointer_id);
- DCHECK_GE(source_index, 0);
- if (source_index < 0)
- continue;
- float dx = source_pointer_down_event->GetX(source_index) - ev2.GetX(i);
- float dy = source_pointer_down_event->GetY(source_index) - ev2.GetY(i);
- delta += SubtractSlopRegion(dx, dy);
- }
- delta.Scale(1.0 / ev2.GetPointerCount());
- return delta;
- }
- const GestureProvider::Config config_;
- const raw_ptr<GestureProviderClient> client_;
- const raw_ptr<GestureProvider> gesture_provider_;
- GestureDetector gesture_detector_;
- ScaleGestureDetector scale_gesture_detector_;
- SnapScrollController snap_scroll_controller_;
- // Keeps track of the event time of the first down action in current touch
- // sequence.
- base::TimeTicks current_down_action_event_time_;
- // Keeps track of the unique touch event id of the first down action in
- // current touch sequence.
- int current_down_action_unique_touch_event_id_;
- // Keeps track of the current GESTURE_LONG_PRESS event. If a context menu is
- // opened after a GESTURE_LONG_PRESS, this is used to insert a
- // GESTURE_TAP_CANCEL for removing any ::active styling.
- base::TimeTicks current_longpress_time_;
- // Completely silence multi-touch (pinch) scaling events. Used in WebView when
- // zoom support is turned off.
- bool ignore_multitouch_zoom_events_;
- // TODO(klobag): This is to avoid a bug in GestureDetector. With multi-touch,
- // always_in_tap_region_ is not reset. So when the last finger is up,
- // |OnSingleTapUp()| will be mistakenly fired.
- bool ignore_single_tap_;
- // Tracks whether {PINCH|SCROLL}_BEGIN events have been forwarded for the
- // current touch sequence.
- bool pinch_event_sent_;
- bool scroll_event_sent_;
- // Only track the maximum diameter before the show press event has been
- // sent and a tap must still be possible for this touch sequence.
- float max_diameter_before_show_press_;
- gfx::PointF tap_down_point_;
- // Tracks whether an ET_GESTURE_SHOW_PRESS event has been sent for this touch
- // sequence.
- bool show_press_event_sent_;
- // The scroll focus point is set to the first touch down point when scroll
- // begins and is later updated based on the delta of touch points.
- gfx::PointF scroll_focus_point_;
- };
- // GestureProvider
- GestureProvider::GestureProvider(const Config& config,
- GestureProviderClient* client)
- : double_tap_support_for_page_(true),
- double_tap_support_for_platform_(
- config.double_tap_support_for_platform_enabled),
- gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled) {
- DCHECK(client);
- DCHECK(!config.min_gesture_bounds_length ||
- !config.max_gesture_bounds_length ||
- config.min_gesture_bounds_length <= config.max_gesture_bounds_length);
- TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
- gesture_listener_ =
- std::make_unique<GestureListenerImpl>(config, client, this);
- UpdateDoubleTapDetectionSupport();
- }
- GestureProvider::~GestureProvider() {
- }
- bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
- TRACE_EVENT1("input",
- "GestureProvider::OnTouchEvent",
- "action",
- GetMotionEventActionName(event.GetAction()));
- DCHECK_NE(0u, event.GetPointerCount());
- // We record the histograms before the |CanHandle()| call below because we
- // want to check the event stream before gesture processing takes place. For
- // example, we want to see |MotionEvent::Action::UP| event even for a panning
- // gesture where the UP is not dispatched to content.
- uma_histogram_.RecordTouchEvent(event);
- if (!CanHandle(event))
- return false;
- OnTouchEventHandlingBegin(event);
- gesture_listener_->OnTouchEvent(event);
- OnTouchEventHandlingEnd(event);
- return true;
- }
- void GestureProvider::ResetDetection() {
- MotionEventGeneric generic_cancel_event(
- MotionEvent::Action::CANCEL, base::TimeTicks::Now(), PointerProperties());
- OnTouchEvent(generic_cancel_event);
- }
- void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
- gesture_listener_->SetMultiTouchZoomEnabled(enabled);
- }
- void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
- if (double_tap_support_for_platform_ == enabled)
- return;
- double_tap_support_for_platform_ = enabled;
- UpdateDoubleTapDetectionSupport();
- }
- void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
- if (double_tap_support_for_page_ == enabled)
- return;
- double_tap_support_for_page_ = enabled;
- UpdateDoubleTapDetectionSupport();
- }
- bool GestureProvider::IsScrollInProgress() const {
- return gesture_listener_->IsScrollInProgress();
- }
- bool GestureProvider::IsPinchInProgress() const {
- return gesture_listener_->IsPinchInProgress();
- }
- bool GestureProvider::IsDoubleTapInProgress() const {
- return gesture_listener_->IsDoubleTapInProgress();
- }
- void GestureProvider::SendSynthesizedEndEvents() {
- gesture_listener_->SendSynthesizedEndEvents();
- }
- bool GestureProvider::CanHandle(const MotionEvent& event) const {
- // Aura requires one cancel event per touch point, whereas Android requires
- // one cancel event per touch sequence. Thus we need to allow extra cancel
- // events.
- return current_down_event_ ||
- event.GetAction() == MotionEvent::Action::DOWN ||
- event.GetAction() == MotionEvent::Action::CANCEL;
- }
- void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
- switch (event.GetAction()) {
- case MotionEvent::Action::DOWN:
- current_down_event_ = event.Clone();
- if (gesture_begin_end_types_enabled_) {
- GestureEventDetails details(ET_GESTURE_BEGIN);
- details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- details.set_primary_unique_touch_event_id(event.GetUniqueEventId());
- gesture_listener_->Send(
- gesture_listener_->CreateGesture(details, event));
- }
- break;
- case MotionEvent::Action::POINTER_DOWN:
- if (gesture_begin_end_types_enabled_) {
- const int action_index = event.GetActionIndex();
- GestureEventDetails details(ET_GESTURE_BEGIN);
- details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- details.set_primary_unique_touch_event_id(event.GetUniqueEventId());
- gesture_listener_->Send(gesture_listener_->CreateGesture(
- details, event.GetPointerId(), event.GetToolType(),
- event.GetEventTime(), event.GetX(action_index),
- event.GetY(action_index), event.GetRawX(action_index),
- event.GetRawY(action_index), event.GetPointerCount(),
- gesture_listener_->GetBoundingBox(event, ET_GESTURE_BEGIN),
- event.GetFlags()));
- }
- break;
- case MotionEvent::Action::POINTER_UP:
- case MotionEvent::Action::UP:
- case MotionEvent::Action::CANCEL:
- case MotionEvent::Action::MOVE:
- break;
- case MotionEvent::Action::NONE:
- case MotionEvent::Action::HOVER_ENTER:
- case MotionEvent::Action::HOVER_EXIT:
- case MotionEvent::Action::HOVER_MOVE:
- case MotionEvent::Action::BUTTON_PRESS:
- case MotionEvent::Action::BUTTON_RELEASE:
- NOTREACHED();
- break;
- }
- }
- void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
- switch (event.GetAction()) {
- case MotionEvent::Action::UP:
- case MotionEvent::Action::CANCEL: {
- if (gesture_begin_end_types_enabled_) {
- GestureEventDetails details(ET_GESTURE_END);
- details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- if (current_down_event_) {
- details.set_primary_unique_touch_event_id(
- current_down_event_->GetUniqueEventId());
- }
- gesture_listener_->Send(
- gesture_listener_->CreateGesture(details, event));
- }
- if (event.GetAction() != MotionEvent::Action::CANCEL ||
- !GestureConfiguration::GetInstance()
- ->single_pointer_cancel_enabled() ||
- event.GetPointerCount() == 1)
- current_down_event_.reset();
- UpdateDoubleTapDetectionSupport();
- break;
- }
- case MotionEvent::Action::POINTER_UP:
- if (gesture_begin_end_types_enabled_) {
- GestureEventDetails details(ET_GESTURE_END);
- details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN);
- if (current_down_event_) {
- details.set_primary_unique_touch_event_id(
- current_down_event_->GetUniqueEventId());
- }
- gesture_listener_->Send(
- gesture_listener_->CreateGesture(details, event));
- }
- break;
- case MotionEvent::Action::DOWN:
- case MotionEvent::Action::POINTER_DOWN:
- case MotionEvent::Action::MOVE:
- break;
- case MotionEvent::Action::NONE:
- case MotionEvent::Action::HOVER_ENTER:
- case MotionEvent::Action::HOVER_EXIT:
- case MotionEvent::Action::HOVER_MOVE:
- case MotionEvent::Action::BUTTON_PRESS:
- case MotionEvent::Action::BUTTON_RELEASE:
- NOTREACHED();
- break;
- }
- }
- void GestureProvider::UpdateDoubleTapDetectionSupport() {
- // The GestureDetector requires that any provided DoubleTapListener remain
- // attached to it for the duration of a touch sequence. Defer any potential
- // null'ing of the listener until the sequence has ended.
- if (current_down_event_)
- return;
- const bool double_tap_enabled =
- double_tap_support_for_page_ && double_tap_support_for_platform_;
- gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
- }
- } // namespace ui
|