blob: ca68e3cf917d10775cc4fc1fffa3f1c7969b8422 [file] [log] [blame]
// 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 <cmath>
#include "base/auto_reset.h"
#include "base/debug/trace_event.h"
#include "ui/events/event_constants.h"
#include "ui/events/gesture_detection/gesture_event_data.h"
#include "ui/events/gesture_detection/motion_event.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_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";
}
return "";
}
gfx::RectF GetBoundingBox(const MotionEvent& event) {
gfx::RectF bounds;
for (size_t i = 0; i < event.GetPointerCount(); ++i) {
float diameter = event.GetTouchMajor(i);
bounds.Union(gfx::RectF(event.GetX(i) - diameter / 2,
event.GetY(i) - diameter / 2,
diameter,
diameter));
}
return bounds;
}
GestureEventData CreateGesture(const GestureEventDetails& details,
int motion_event_id,
base::TimeTicks time,
float x,
float y,
float raw_x,
float raw_y,
size_t touch_point_count,
const gfx::RectF& bounding_box) {
return GestureEventData(details,
motion_event_id,
time,
x,
y,
raw_x,
raw_y,
touch_point_count,
bounding_box);
}
GestureEventData CreateGesture(EventType type,
int motion_event_id,
base::TimeTicks time,
float x,
float y,
float raw_x,
float raw_y,
size_t touch_point_count,
const gfx::RectF& bounding_box) {
return GestureEventData(GestureEventDetails(type, 0, 0),
motion_event_id,
time,
x,
y,
raw_x,
raw_y,
touch_point_count,
bounding_box);
}
GestureEventData CreateGesture(const GestureEventDetails& details,
const MotionEvent& event) {
return GestureEventData(details,
event.GetId(),
event.GetEventTime(),
event.GetX(),
event.GetY(),
event.GetRawX(),
event.GetRawY(),
event.GetPointerCount(),
GetBoundingBox(event));
}
GestureEventData CreateGesture(EventType type, const MotionEvent& event) {
return CreateGesture(GestureEventDetails(type, 0, 0), event);
}
GestureEventDetails CreateTapGestureDetails(EventType type) {
// Set the tap count to 1 even for ET_GESTURE_DOUBLE_TAP, in order to be
// consistent with double tap behavior on a mobile viewport. See
// crbug.com/234986 for context.
GestureEventDetails tap_details(type, 1, 0);
return tap_details;
}
} // namespace
// GestureProvider:::Config
GestureProvider::Config::Config()
: display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)),
disable_click_delay(false),
gesture_begin_end_types_enabled(false),
min_gesture_bounds_length(0) {}
GestureProvider::Config::~Config() {}
// GestureProvider::ScaleGestureListener
class GestureProvider::ScaleGestureListenerImpl
: public ScaleGestureDetector::ScaleGestureListener {
public:
ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config,
GestureProvider* provider)
: scale_gesture_detector_(config, this),
provider_(provider),
ignore_multitouch_events_(false),
pinch_event_sent_(false),
min_pinch_update_span_delta_(config.min_pinch_update_span_delta) {}
bool OnTouchEvent(const MotionEvent& event) {
// TODO: Need to deal with multi-touch transition.
const bool in_scale_gesture = IsScaleGestureDetectionInProgress();
bool handled = scale_gesture_detector_.OnTouchEvent(event);
if (!in_scale_gesture &&
(event.GetAction() == MotionEvent::ACTION_UP ||
event.GetAction() == MotionEvent::ACTION_CANCEL)) {
return false;
}
return handled;
}
// ScaleGestureDetector::ScaleGestureListener implementation.
virtual bool OnScaleBegin(const ScaleGestureDetector& detector,
const MotionEvent& e) OVERRIDE {
if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
return false;
pinch_event_sent_ = false;
return true;
}
virtual void OnScaleEnd(const ScaleGestureDetector& detector,
const MotionEvent& e) OVERRIDE {
if (!pinch_event_sent_)
return;
provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, e));
pinch_event_sent_ = false;
}
virtual bool OnScale(const ScaleGestureDetector& detector,
const MotionEvent& e) OVERRIDE {
if (ignore_multitouch_events_ && !detector.InDoubleTapMode())
return false;
if (!pinch_event_sent_) {
pinch_event_sent_ = true;
provider_->Send(CreateGesture(ET_GESTURE_PINCH_BEGIN,
e.GetId(),
detector.GetEventTime(),
detector.GetFocusX(),
detector.GetFocusY(),
detector.GetFocusX() + e.GetRawOffsetX(),
detector.GetFocusY() + e.GetRawOffsetY(),
e.GetPointerCount(),
GetBoundingBox(e)));
}
if (std::abs(detector.GetCurrentSpan() - detector.GetPreviousSpan()) <
min_pinch_update_span_delta_) {
return false;
}
float scale = detector.GetScaleFactor();
if (scale == 1)
return true;
if (detector.InDoubleTapMode()) {
// 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.
float dy =
(detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f;
scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed
: 1.0f - kDoubleTapDragZoomSpeed,
std::abs(dy));
}
GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
provider_->Send(CreateGesture(pinch_details,
e.GetId(),
detector.GetEventTime(),
detector.GetFocusX(),
detector.GetFocusY(),
detector.GetFocusX() + e.GetRawOffsetX(),
detector.GetFocusY() + e.GetRawOffsetY(),
e.GetPointerCount(),
GetBoundingBox(e)));
return true;
}
void SetDoubleTapEnabled(bool enabled) {
DCHECK(!IsDoubleTapInProgress());
scale_gesture_detector_.SetQuickScaleEnabled(enabled);
}
void SetMultiTouchEnabled(bool enabled) {
// Note that returning false from OnScaleBegin / OnScale makes the
// gesture detector not to emit further scaling notifications
// related to this gesture. Thus, if detector events are enabled in
// the middle of the gesture, we don't need to do anything.
ignore_multitouch_events_ = !enabled;
}
bool IsDoubleTapInProgress() const {
return IsScaleGestureDetectionInProgress() && InDoubleTapMode();
}
bool IsScaleGestureDetectionInProgress() const {
return scale_gesture_detector_.IsInProgress();
}
private:
bool InDoubleTapMode() const {
return scale_gesture_detector_.InDoubleTapMode();
}
ScaleGestureDetector scale_gesture_detector_;
GestureProvider* const provider_;
// Completely silence multi-touch (pinch) scaling events. Used in WebView when
// zoom support is turned off.
bool ignore_multitouch_events_;
// Whether any pinch zoom event has been sent to native.
bool pinch_event_sent_;
// The minimum change in span required before this is considered a pinch. See
// crbug.com/373318.
float min_pinch_update_span_delta_;
DISALLOW_COPY_AND_ASSIGN(ScaleGestureListenerImpl);
};
// GestureProvider::GestureListener
class GestureProvider::GestureListenerImpl
: public GestureDetector::GestureListener,
public GestureDetector::DoubleTapListener {
public:
GestureListenerImpl(
const gfx::Display& display,
const GestureDetector::Config& gesture_detector_config,
bool disable_click_delay,
GestureProvider* provider)
: gesture_detector_(gesture_detector_config, this, this),
snap_scroll_controller_(display),
provider_(provider),
disable_click_delay_(disable_click_delay),
touch_slop_(gesture_detector_config.touch_slop),
double_tap_timeout_(gesture_detector_config.double_tap_timeout),
ignore_single_tap_(false),
seen_first_scroll_event_(false) {}
virtual ~GestureListenerImpl() {}
bool OnTouchEvent(const MotionEvent& e,
bool is_scale_gesture_detection_in_progress) {
snap_scroll_controller_.SetSnapScrollingMode(
e, is_scale_gesture_detection_in_progress);
if (is_scale_gesture_detection_in_progress)
SetIgnoreSingleTap(true);
if (e.GetAction() == MotionEvent::ACTION_DOWN)
gesture_detector_.set_longpress_enabled(true);
return gesture_detector_.OnTouchEvent(e);
}
// GestureDetector::GestureListener implementation.
virtual bool OnDown(const MotionEvent& e) OVERRIDE {
current_down_time_ = e.GetEventTime();
ignore_single_tap_ = false;
seen_first_scroll_event_ = false;
GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0);
provider_->Send(CreateGesture(tap_details, e));
// Return true to indicate that we want to handle touch.
return true;
}
virtual bool OnScroll(const MotionEvent& e1,
const MotionEvent& e2,
float raw_distance_x,
float raw_distance_y) OVERRIDE {
float distance_x = raw_distance_x;
float distance_y = raw_distance_y;
if (!seen_first_scroll_event_) {
// Remove the touch slop region from the first scroll event to avoid a
// jump.
seen_first_scroll_event_ = true;
double distance =
std::sqrt(distance_x * distance_x + distance_y * distance_y);
double epsilon = 1e-3;
if (distance > epsilon) {
double ratio = std::max(0., distance - touch_slop_) / distance;
distance_x *= ratio;
distance_y *= ratio;
}
}
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 (!provider_->IsScrollInProgress()) {
// Note that scroll start hints are in distance traveled, where
// scroll deltas are in the opposite direction.
GestureEventDetails scroll_details(
ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y);
// Use the co-ordinates from the touch down, as these co-ordinates are
// used to determine which layer the scroll should affect.
provider_->Send(CreateGesture(scroll_details,
e2.GetId(),
e2.GetEventTime(),
e1.GetX(),
e1.GetY(),
e1.GetRawX(),
e1.GetRawY(),
e2.GetPointerCount(),
GetBoundingBox(e2)));
}
if (distance_x || distance_y) {
const gfx::RectF bounding_box = GetBoundingBox(e2);
const gfx::PointF center = bounding_box.CenterPoint();
const gfx::PointF raw_center =
center + gfx::Vector2dF(e2.GetRawOffsetX(), e2.GetRawOffsetY());
GestureEventDetails scroll_details(
ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
provider_->Send(CreateGesture(scroll_details,
e2.GetId(),
e2.GetEventTime(),
center.x(),
center.y(),
raw_center.x(),
raw_center.y(),
e2.GetPointerCount(),
bounding_box));
}
return true;
}
virtual 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;
}
}
provider_->Fling(e2, velocity_x, velocity_y);
return true;
}
virtual 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);
provider_->Send(CreateGesture(swipe_details, e2));
return true;
}
virtual 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());
provider_->Send(CreateGesture(two_finger_tap_details,
e2.GetId(),
e2.GetEventTime(),
e1.GetX(),
e1.GetY(),
e1.GetRawX(),
e1.GetRawY(),
e2.GetPointerCount(),
GetBoundingBox(e2)));
return true;
}
virtual void OnShowPress(const MotionEvent& e) OVERRIDE {
GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0);
provider_->Send(CreateGesture(show_press_details, e));
}
virtual bool OnSingleTapUp(const MotionEvent& e) 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_time_ > double_tap_timeout_) {
return OnSingleTapConfirmed(e);
} else if (!IsDoubleTapEnabled() || disable_click_delay_) {
// If double-tap has been disabled, there is no need to wait
// for the double-tap timeout.
return OnSingleTapConfirmed(e);
} else {
// Notify Blink about this tapUp event anyway, when none of the above
// conditions applied.
provider_->Send(CreateGesture(
CreateTapGestureDetails(ET_GESTURE_TAP_UNCONFIRMED), e));
}
}
return provider_->SendLongTapIfNecessary(e);
}
// GestureDetector::DoubleTapListener implementation.
virtual bool OnSingleTapConfirmed(const MotionEvent& e) OVERRIDE {
// 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;
provider_->Send(CreateGesture(CreateTapGestureDetails(ET_GESTURE_TAP), e));
return true;
}
virtual bool OnDoubleTap(const MotionEvent& e) OVERRIDE { return false; }
virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
switch (e.GetAction()) {
case MotionEvent::ACTION_DOWN:
gesture_detector_.set_longpress_enabled(false);
break;
case MotionEvent::ACTION_UP:
if (!provider_->IsPinchInProgress() &&
!provider_->IsScrollInProgress()) {
provider_->Send(
CreateGesture(CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP), e));
return true;
}
break;
default:
break;
}
return false;
}
virtual bool OnLongPress(const MotionEvent& e) OVERRIDE {
DCHECK(!IsDoubleTapInProgress());
SetIgnoreSingleTap(true);
GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0);
provider_->Send(CreateGesture(long_press_details, e));
// Returning true puts the GestureDetector in "longpress" mode, disabling
// further scrolling. This is undesirable, as it is quite common for a
// longpress gesture to fire on content that won't trigger a context menu.
return false;
}
void SetDoubleTapEnabled(bool enabled) {
DCHECK(!IsDoubleTapInProgress());
gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
}
bool IsDoubleTapInProgress() const {
return gesture_detector_.is_double_tapping();
}
private:
void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
bool IsDoubleTapEnabled() const {
return gesture_detector_.has_doubletap_listener();
}
GestureDetector gesture_detector_;
SnapScrollController snap_scroll_controller_;
GestureProvider* const provider_;
// Whether the click delay should always be disabled by sending clicks for
// double-tap gestures.
const bool disable_click_delay_;
const float touch_slop_;
const base::TimeDelta double_tap_timeout_;
base::TimeTicks current_down_time_;
// 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_;
// Used to remove the touch slop from the initial scroll event in a scroll
// gesture.
bool seen_first_scroll_event_;
DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
};
// GestureProvider
GestureProvider::GestureProvider(const Config& config,
GestureProviderClient* client)
: client_(client),
touch_scroll_in_progress_(false),
pinch_in_progress_(false),
double_tap_support_for_page_(true),
double_tap_support_for_platform_(true),
gesture_begin_end_types_enabled_(config.gesture_begin_end_types_enabled),
min_gesture_bounds_length_(config.min_gesture_bounds_length) {
DCHECK(client);
InitGestureDetectors(config);
}
GestureProvider::~GestureProvider() {}
bool GestureProvider::OnTouchEvent(const MotionEvent& event) {
TRACE_EVENT1("input", "GestureProvider::OnTouchEvent",
"action", GetMotionEventActionName(event.GetAction()));
DCHECK_NE(0u, event.GetPointerCount());
if (!CanHandle(event))
return false;
const bool in_scale_gesture =
scale_gesture_listener_->IsScaleGestureDetectionInProgress();
OnTouchEventHandlingBegin(event);
gesture_listener_->OnTouchEvent(event, in_scale_gesture);
scale_gesture_listener_->OnTouchEvent(event);
OnTouchEventHandlingEnd(event);
return true;
}
void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
scale_gesture_listener_->SetMultiTouchEnabled(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 {
// TODO(wangxianzhu): Also return true when fling is active once the UI knows
// exactly when the fling ends.
return touch_scroll_in_progress_;
}
bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
bool GestureProvider::IsDoubleTapInProgress() const {
return gesture_listener_->IsDoubleTapInProgress() ||
scale_gesture_listener_->IsDoubleTapInProgress();
}
void GestureProvider::InitGestureDetectors(const Config& config) {
TRACE_EVENT0("input", "GestureProvider::InitGestureDetectors");
gesture_listener_.reset(
new GestureListenerImpl(config.display,
config.gesture_detector_config,
config.disable_click_delay,
this));
scale_gesture_listener_.reset(
new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this));
UpdateDoubleTapDetectionSupport();
}
bool GestureProvider::CanHandle(const MotionEvent& event) const {
return event.GetAction() == MotionEvent::ACTION_DOWN || current_down_event_;
}
void GestureProvider::Fling(const MotionEvent& event,
float velocity_x,
float velocity_y) {
if (!velocity_x && !velocity_y) {
EndTouchScrollIfNecessary(event, true);
return;
}
if (!touch_scroll_in_progress_) {
// The native side needs a ET_GESTURE_SCROLL_BEGIN before
// ET_SCROLL_FLING_START to send the fling to the correct target. Send if it
// has not sent. The distance traveled in one second is a reasonable scroll
// start hint.
GestureEventDetails scroll_details(
ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
Send(CreateGesture(scroll_details, event));
}
EndTouchScrollIfNecessary(event, false);
GestureEventDetails fling_details(
ET_SCROLL_FLING_START, velocity_x, velocity_y);
Send(CreateGesture(fling_details, event));
}
void GestureProvider::Send(GestureEventData gesture) {
DCHECK(!gesture.time.is_null());
// The only valid events that should be sent without an active touch sequence
// are SHOW_PRESS and TAP, potentially triggered by the double-tap
// delay timing out.
DCHECK(current_down_event_ || gesture.type() == ET_GESTURE_TAP ||
gesture.type() == ET_GESTURE_SHOW_PRESS);
// TODO(jdduke): Provide a way of skipping this clamping for stylus and/or
// mouse-based input, perhaps by exposing the source type on MotionEvent.
const gfx::RectF& gesture_bounds = gesture.details.bounding_box_f();
gesture.details.set_bounding_box(gfx::RectF(
gesture_bounds.x(),
gesture_bounds.y(),
std::max(min_gesture_bounds_length_, gesture_bounds.width()),
std::max(min_gesture_bounds_length_, gesture_bounds.height())));
switch (gesture.type()) {
case ET_GESTURE_LONG_PRESS:
DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
current_longpress_time_ = gesture.time;
break;
case ET_GESTURE_LONG_TAP:
current_longpress_time_ = base::TimeTicks();
break;
case ET_GESTURE_SCROLL_BEGIN:
DCHECK(!touch_scroll_in_progress_);
touch_scroll_in_progress_ = true;
break;
case ET_GESTURE_SCROLL_END:
DCHECK(touch_scroll_in_progress_);
if (pinch_in_progress_)
Send(GestureEventData(ET_GESTURE_PINCH_END, gesture));
touch_scroll_in_progress_ = false;
break;
case ET_GESTURE_PINCH_BEGIN:
DCHECK(!pinch_in_progress_);
if (!touch_scroll_in_progress_)
Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture));
pinch_in_progress_ = true;
break;
case ET_GESTURE_PINCH_END:
DCHECK(pinch_in_progress_);
pinch_in_progress_ = 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_in_progress_ || touch_scroll_in_progress_)
return;
default:
break;
};
client_->OnGestureEvent(gesture);
}
bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) {
if (event.GetAction() == MotionEvent::ACTION_UP &&
!current_longpress_time_.is_null() &&
!scale_gesture_listener_->IsScaleGestureDetectionInProgress()) {
GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0);
Send(CreateGesture(long_tap_details, event));
return true;
}
return false;
}
void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event,
bool send_scroll_end_event) {
if (!touch_scroll_in_progress_)
return;
if (send_scroll_end_event)
Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
touch_scroll_in_progress_ = false;
}
void GestureProvider::OnTouchEventHandlingBegin(const MotionEvent& event) {
switch (event.GetAction()) {
case MotionEvent::ACTION_DOWN:
current_down_event_ = event.Clone();
touch_scroll_in_progress_ = false;
pinch_in_progress_ = false;
current_longpress_time_ = base::TimeTicks();
if (gesture_begin_end_types_enabled_)
Send(CreateGesture(ET_GESTURE_BEGIN, event));
break;
case MotionEvent::ACTION_POINTER_DOWN:
if (gesture_begin_end_types_enabled_) {
const int action_index = event.GetActionIndex();
Send(CreateGesture(ET_GESTURE_BEGIN,
event.GetId(),
event.GetEventTime(),
event.GetX(action_index),
event.GetY(action_index),
event.GetRawX(action_index),
event.GetRawY(action_index),
event.GetPointerCount(),
GetBoundingBox(event)));
}
break;
case MotionEvent::ACTION_POINTER_UP:
case MotionEvent::ACTION_UP:
case MotionEvent::ACTION_CANCEL:
case MotionEvent::ACTION_MOVE:
break;
}
}
void GestureProvider::OnTouchEventHandlingEnd(const MotionEvent& event) {
switch (event.GetAction()) {
case MotionEvent::ACTION_UP:
case 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.
EndTouchScrollIfNecessary(event, true);
const gfx::RectF bounding_box = GetBoundingBox(event);
if (gesture_begin_end_types_enabled_) {
for (size_t i = 0; i < event.GetPointerCount(); ++i) {
Send(CreateGesture(ET_GESTURE_END,
event.GetId(),
event.GetEventTime(),
event.GetX(i),
event.GetY(i),
event.GetRawX(i),
event.GetRawY(i),
event.GetPointerCount() - i,
bounding_box));
}
}
current_down_event_.reset();
UpdateDoubleTapDetectionSupport();
break;
}
case MotionEvent::ACTION_POINTER_UP:
if (gesture_begin_end_types_enabled_)
Send(CreateGesture(ET_GESTURE_END, event));
break;
case MotionEvent::ACTION_DOWN:
case MotionEvent::ACTION_POINTER_DOWN:
case MotionEvent::ACTION_MOVE:
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);
scale_gesture_listener_->SetDoubleTapEnabled(double_tap_enabled);
}
} // namespace ui