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(EventType type,
+ int motion_event_id,
base::TimeTicks time,
float x,
float y,
+ size_t touch_point_count,
+ const gfx::RectF& bounding_box,
const GestureEventDetails& details) {
- return GestureEventData(type, time, x, y, details);
+ return GestureEventData(type,
+ motion_event_id,
+ time,
+ x,
+ y,
+ static_cast<int>(touch_point_count),
+ bounding_box,
+ details);
}
GestureEventData CreateGesture(EventType type,
+ int motion_event_id,
base::TimeTicks time,
float x,
- float y) {
- return GestureEventData(type, time, x, y);
- }
+ float y,
+ size_t touch_point_count,
+ const gfx::RectF& bounding_box) {
+ return GestureEventData(type,
+ motion_event_id,
+ time,
+ x,
+ y,
+ static_cast<int>(touch_point_count),
+ bounding_box);
+}
GestureEventData CreateGesture(EventType type,
const MotionEvent& event,
const GestureEventDetails& details) {
- return CreateGesture(
- type, event.GetEventTime(), event.GetX(), event.GetY(), details);
+ return CreateGesture(type,
+ event.GetId(),
+ event.GetEventTime(),
+ event.GetX(),
+ event.GetY(),
+ event.GetPointerCount(),
+ GetBoundingBox(event),
+ details);
}
GestureEventData CreateGesture(EventType type,
const MotionEvent& event) {
- return CreateGesture(type, event.GetEventTime(), event.GetX(), event.GetY());
-}
-
-float Round(float f) {
- return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f);
+ return CreateGesture(type,
+ event.GetId(),
+ event.GetEventTime(),
+ event.GetX(),
+ event.GetY(),
+ event.GetPointerCount(),
+ GetBoundingBox(event));
}
GestureEventDetails CreateTapGestureDetails(EventType type,
// consistent with double tap behavior on a mobile viewport. See
// crbug.com/234986 for context.
GestureEventDetails tap_details(type, 1, 0);
- tap_details.set_bounding_box(
- gfx::RectF(event.GetTouchMajor(), event.GetTouchMajor()));
return tap_details;
}
// GestureProvider:::Config
-GestureProvider::Config::Config() : disable_click_delay(false) {}
+GestureProvider::Config::Config()
+ : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)),
+ disable_click_delay(false),
+ gesture_begin_end_types_enabled(false) {}
GestureProvider::Config::~Config() {}
GestureProvider* provider)
: scale_gesture_detector_(config, this),
provider_(provider),
- ignore_detector_events_(false),
+ ignore_multitouch_events_(false),
pinch_event_sent_(false) {}
bool OnTouchEvent(const MotionEvent& event) {
}
// ScaleGestureDetector::ScaleGestureListener implementation.
- virtual bool OnScaleBegin(const ScaleGestureDetector& detector) OVERRIDE {
- if (ignore_detector_events_)
+ 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) OVERRIDE {
+ virtual void OnScaleEnd(const ScaleGestureDetector& detector,
+ const MotionEvent& e) OVERRIDE {
if (!pinch_event_sent_)
return;
- provider_->Send(
- CreateGesture(ET_GESTURE_PINCH_END, detector.GetEventTime(), 0, 0));
+ provider_->Send(CreateGesture(ET_GESTURE_PINCH_END,
+ e.GetId(),
+ detector.GetEventTime(),
+ 0,
+ 0,
+ e.GetPointerCount(),
+ GetBoundingBox(e)));
pinch_event_sent_ = false;
}
- virtual bool OnScale(const ScaleGestureDetector& detector) OVERRIDE {
- if (ignore_detector_events_)
+ 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.GetFocusY(),
+ e.GetPointerCount(),
+ GetBoundingBox(e)));
+ }
+
+ 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, detector.GetScaleFactor(), 0);
+ GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE,
+ e.GetId(),
detector.GetEventTime(),
detector.GetFocusX(),
detector.GetFocusY(),
+ e.GetPointerCount(),
+ GetBoundingBox(e),
pinch_details));
return true;
}
- bool IsScaleGestureDetectionInProgress() const {
- return !ignore_detector_events_ && scale_gesture_detector_.IsInProgress();
+ void SetDoubleTapEnabled(bool enabled) {
+ DCHECK(!IsDoubleTapInProgress());
+ scale_gesture_detector_.SetQuickScaleEnabled(enabled);
}
- void set_ignore_detector_events(bool value) {
+ 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_detector_events_ = value;
+ 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 scaling events. Used in WebView when zoom support
- // is turned off.
- bool ignore_detector_events_;
+ // 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_;
public GestureDetector::DoubleTapListener {
public:
GestureListenerImpl(
+ const gfx::Display& display,
const GestureDetector::Config& gesture_detector_config,
- const SnapScrollController::Config& snap_scroll_controller_config,
bool disable_click_delay,
GestureProvider* provider)
: gesture_detector_(gesture_detector_config, this, this),
- snap_scroll_controller_(snap_scroll_controller_config),
+ snap_scroll_controller_(display),
provider_(provider),
- px_to_dp_(1.0f / snap_scroll_controller_config.device_scale_factor),
disable_click_delay_(disable_click_delay),
- scaled_touch_slop_(gesture_detector_config.scaled_touch_slop),
- scaled_touch_slop_square_(scaled_touch_slop_ * scaled_touch_slop_),
+ 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),
- double_tap_mode_(DOUBLE_TAP_MODE_NONE),
- double_tap_y_(0),
- double_tap_support_enabled_(true),
- double_tap_drag_zoom_anchor_x_(0),
- double_tap_drag_zoom_anchor_y_(0),
- last_raw_x_(0),
- last_raw_y_(0),
- accumulated_scroll_error_x_(0),
- accumulated_scroll_error_y_(0) {
- UpdateDoubleTapListener();
- }
+ seen_first_scroll_event_(false) {}
virtual ~GestureListenerImpl() {}
if (is_scale_gesture_detection_in_progress)
SetIgnoreSingleTap(true);
- if (e.GetAction() == MotionEvent::ACTION_POINTER_DOWN ||
- e.GetAction() == MotionEvent::ACTION_CANCEL) {
- EndDoubleTapDragIfNecessary(e);
- } else if (e.GetAction() == MotionEvent::ACTION_DOWN) {
- gesture_detector_.set_is_longpress_enabled(true);
- }
+ if (e.GetAction() == MotionEvent::ACTION_DOWN)
+ gesture_detector_.set_longpress_enabled(true);
return gesture_detector_.OnTouchEvent(e);
}
current_down_time_ = e.GetEventTime();
ignore_single_tap_ = false;
seen_first_scroll_event_ = false;
- last_raw_x_ = e.GetRawX();
- last_raw_y_ = e.GetRawY();
- accumulated_scroll_error_x_ = 0;
- accumulated_scroll_error_y_ = 0;
GestureEventDetails tap_details(ET_GESTURE_TAP_DOWN, 0, 0);
- tap_details.set_bounding_box(
- gfx::RectF(e.GetTouchMajor(), e.GetTouchMajor()));
provider_->Send(CreateGesture(ET_GESTURE_TAP_DOWN, e, tap_details));
// Return true to indicate that we want to handle touch.
std::sqrt(distance_x * distance_x + distance_y * distance_y);
double epsilon = 1e-3;
if (distance > epsilon) {
- double ratio = std::max(0., distance - scaled_touch_slop_) / distance;
+ double ratio = std::max(0., distance - touch_slop_) / distance;
distance_x *= ratio;
distance_y *= ratio;
}
}
}
- last_raw_x_ = e2.GetRawX();
- last_raw_y_ = e2.GetRawY();
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(ET_GESTURE_SCROLL_BEGIN,
+ e2.GetId(),
e2.GetEventTime(),
e1.GetX(),
e1.GetY(),
+ e2.GetPointerCount(),
+ GetBoundingBox(e2),
scroll_details));
}
- // distance_x and distance_y is the scrolling offset since last OnScroll.
- // Because we are passing integers to Blink, this could introduce
- // rounding errors. The rounding errors will accumulate overtime.
- // To solve this, we should be adding back the rounding errors each time
- // when we calculate the new offset.
- // TODO(jdduke): Determine if we can simpy use floating point deltas, as
- // WebGestureEvent also takes floating point deltas for GestureScrollUpdate.
- int dx = (int)(distance_x + accumulated_scroll_error_x_);
- int dy = (int)(distance_y + accumulated_scroll_error_y_);
- accumulated_scroll_error_x_ += (distance_x - dx);
- accumulated_scroll_error_y_ += (distance_y - dy);
-
- if (dx || dy) {
- GestureEventDetails scroll_details(ET_GESTURE_SCROLL_UPDATE, -dx, -dy);
+ if (distance_x || distance_y) {
+ GestureEventDetails scroll_details(
+ ET_GESTURE_SCROLL_UPDATE, -distance_x, -distance_y);
provider_->Send(
CreateGesture(ET_GESTURE_SCROLL_UPDATE, e2, scroll_details));
}
}
}
- provider_->Fling(
- e2.GetEventTime(), e1.GetX(), e1.GetY(), velocity_x, velocity_y);
+ 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_MULTIFINGER_SWIPE, velocity_x, velocity_y);
+ provider_->Send(
+ CreateGesture(ET_GESTURE_MULTIFINGER_SWIPE, e2, swipe_details));
return true;
}
virtual void OnShowPress(const MotionEvent& e) OVERRIDE {
GestureEventDetails show_press_details(ET_GESTURE_SHOW_PRESS, 0, 0);
- // TODO(jdduke): Expose minor axis length and rotation in |MotionEvent|.
- show_press_details.set_bounding_box(
- gfx::RectF(e.GetTouchMajor(), e.GetTouchMajor()));
provider_->Send(
CreateGesture(ET_GESTURE_SHOW_PRESS, e, show_press_details));
}
virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE {
- if (IsPointOutsideCurrentSlopRegion(e.GetRawX(), e.GetRawY())) {
- provider_->SendTapCancelIfNecessary(e);
- ignore_single_tap_ = true;
- return true;
- }
// 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
if (!ignore_single_tap_) {
if (e.GetEventTime() - current_down_time_ > double_tap_timeout_) {
return OnSingleTapConfirmed(e);
- } else if (IsDoubleTapDisabled() || disable_click_delay_) {
+ } 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);
virtual bool OnDoubleTapEvent(const MotionEvent& e) OVERRIDE {
switch (e.GetAction()) {
case MotionEvent::ACTION_DOWN:
- // Note that this will be called before the corresponding |onDown()|
- // of the same ACTION_DOWN event. Thus, the preceding TAP_DOWN
- // should be cancelled prior to sending a new one (in |onDown()|).
- double_tap_drag_zoom_anchor_x_ = e.GetX();
- double_tap_drag_zoom_anchor_y_ = e.GetY();
- double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS;
- // If a long-press fires during a double-tap, the GestureDetector
- // will stop feeding MotionEvents to |onDoubleTapEvent()|,
- // preventing double-tap drag zoom. Long press detection will be
- // re-enabled on the next ACTION_DOWN.
- gesture_detector_.set_is_longpress_enabled(false);
- break;
- case MotionEvent::ACTION_MOVE:
- if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS) {
- float distance_x = double_tap_drag_zoom_anchor_x_ - e.GetX();
- float distance_y = double_tap_drag_zoom_anchor_y_ - e.GetY();
-
- // Begin double-tap drag zoom mode if the move distance is
- // further than the threshold.
- if (IsDistanceGreaterThanTouchSlop(distance_x, distance_y)) {
- GestureEventDetails scroll_details(
- ET_GESTURE_SCROLL_BEGIN, -distance_x, -distance_y);
- provider_->Send(
- CreateGesture(ET_GESTURE_SCROLL_BEGIN, e, scroll_details));
- provider_->Send(
- CreateGesture(ET_GESTURE_PINCH_BEGIN,
- e.GetEventTime(),
- Round(double_tap_drag_zoom_anchor_x_),
- Round(double_tap_drag_zoom_anchor_y_)));
- double_tap_mode_ = DOUBLE_TAP_MODE_DRAG_ZOOM;
- }
- } else if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) {
- provider_->Send(CreateGesture(ET_GESTURE_SCROLL_UPDATE, e));
-
- float dy = double_tap_y_ - e.GetY();
- float scale = std::pow(dy > 0 ? 1.0f - kDoubleTapDragZoomSpeed
- : 1.0f + kDoubleTapDragZoomSpeed,
- std::abs(dy * px_to_dp_));
- GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0);
- provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE,
- e.GetEventTime(),
- Round(double_tap_drag_zoom_anchor_x_),
- Round(double_tap_drag_zoom_anchor_y_),
- pinch_details));
- }
+ gesture_detector_.set_longpress_enabled(false);
break;
+
case MotionEvent::ACTION_UP:
- if (double_tap_mode_ != DOUBLE_TAP_MODE_DRAG_ZOOM) {
- // Normal double-tap gesture.
+ if (!provider_->IsPinchInProgress() &&
+ !provider_->IsScrollInProgress()) {
provider_->Send(
CreateGesture(ET_GESTURE_DOUBLE_TAP,
e,
CreateTapGestureDetails(ET_GESTURE_DOUBLE_TAP, e)));
+ return true;
}
- EndDoubleTapDragIfNecessary(e);
- break;
- case MotionEvent::ACTION_CANCEL:
- EndDoubleTapDragIfNecessary(e);
break;
default:
- NOTREACHED() << "Invalid double-tap event.";
break;
}
- double_tap_y_ = e.GetY();
- return true;
+ return false;
}
virtual bool OnLongPress(const MotionEvent& e) OVERRIDE {
SetIgnoreSingleTap(true);
GestureEventDetails long_press_details(ET_GESTURE_LONG_PRESS, 0, 0);
- long_press_details.set_bounding_box(
- gfx::RectF(e.GetTouchMajor(), e.GetTouchMajor()));
provider_->Send(
CreateGesture(ET_GESTURE_LONG_PRESS, e, long_press_details));
return false;
}
- void SetDoubleTapSupportForPlatformEnabled(bool enabled) {
+ void SetDoubleTapEnabled(bool enabled) {
DCHECK(!IsDoubleTapInProgress());
- DoubleTapMode double_tap_mode =
- enabled ? DOUBLE_TAP_MODE_NONE : DOUBLE_TAP_MODE_DISABLED;
- if (double_tap_mode_ == double_tap_mode)
- return;
- double_tap_mode_ = double_tap_mode;
- UpdateDoubleTapListener();
- }
-
- void SetDoubleTapSupportForPageEnabled(bool enabled) {
- if (double_tap_support_enabled_ == enabled)
- return;
- double_tap_support_enabled_ = enabled;
- UpdateDoubleTapListener();
+ gesture_detector_.SetDoubleTapListener(enabled ? this : NULL);
}
- bool IsDoubleTapDisabled() const {
- return double_tap_mode_ == DOUBLE_TAP_MODE_DISABLED ||
- !double_tap_support_enabled_;
- }
-
- bool IsClickDelayDisabled() const { return disable_click_delay_; }
-
bool IsDoubleTapInProgress() const {
- return double_tap_mode_ != DOUBLE_TAP_MODE_DISABLED &&
- double_tap_mode_ != DOUBLE_TAP_MODE_NONE;
+ return gesture_detector_.is_double_tapping();
}
private:
- enum DoubleTapMode {
- DOUBLE_TAP_MODE_NONE,
- DOUBLE_TAP_MODE_DRAG_DETECTION_IN_PROGRESS,
- DOUBLE_TAP_MODE_DRAG_ZOOM,
- DOUBLE_TAP_MODE_DISABLED
- };
-
- bool IsPointOutsideCurrentSlopRegion(float x, float y) const {
- return IsDistanceGreaterThanTouchSlop(last_raw_x_ - x, last_raw_y_ - y);
- }
-
- bool IsDistanceGreaterThanTouchSlop(float distance_x,
- float distance_y) const {
- return distance_x * distance_x + distance_y * distance_y >
- scaled_touch_slop_square_;
- }
-
void SetIgnoreSingleTap(bool value) { ignore_single_tap_ = value; }
- void EndDoubleTapDragIfNecessary(const MotionEvent& event) {
- if (!IsDoubleTapInProgress())
- return;
- if (double_tap_mode_ == DOUBLE_TAP_MODE_DRAG_ZOOM) {
- provider_->Send(CreateGesture(ET_GESTURE_PINCH_END, event));
- provider_->Send(CreateGesture(ET_GESTURE_SCROLL_END, event));
- }
- double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
- UpdateDoubleTapListener();
- }
-
- void UpdateDoubleTapListener() {
- if (IsDoubleTapDisabled()) {
- // Defer nulling the DoubleTapListener until the double-tap gesture is
- // complete.
- if (IsDoubleTapInProgress())
- return;
- gesture_detector_.set_doubletap_listener(NULL);
- } else {
- gesture_detector_.set_doubletap_listener(this);
- }
+ bool IsDoubleTapEnabled() const {
+ return gesture_detector_.has_doubletap_listener();
}
GestureDetector gesture_detector_;
GestureProvider* const provider_;
- const float px_to_dp_;
-
// Whether the click delay should always be disabled by sending clicks for
// double-tap gestures.
const bool disable_click_delay_;
- const int scaled_touch_slop_;
-
- // Cache of square of the scaled touch slop so we don't have to calculate it
- // on every touch.
- const int scaled_touch_slop_square_;
+ const float touch_slop_;
const base::TimeDelta double_tap_timeout_;
// gesture.
bool seen_first_scroll_event_;
- // Indicate current double-tap mode state.
- int double_tap_mode_;
-
- // On double-tap this will store the y coordinates of the touch.
- float double_tap_y_;
-
- // The page's viewport and scale sometimes allow us to disable double-tap
- // gesture detection,
- // according to the logic in ContentViewCore.onRenderCoordinatesUpdated().
- bool double_tap_support_enabled_;
-
- // x, y coordinates for an Anchor on double-tap drag zoom.
- float double_tap_drag_zoom_anchor_x_;
- float double_tap_drag_zoom_anchor_y_;
-
- // Used to track the last rawX/Y coordinates for moves. This gives absolute
- // scroll distance.
- // Useful for full screen tracking.
- float last_raw_x_;
- float last_raw_y_;
-
- // Used to track the accumulated scroll error over time. This is used to
- // remove the
- // rounding error we introduced by passing integers to webkit.
- float accumulated_scroll_error_x_;
- float accumulated_scroll_error_y_;
-
DISALLOW_COPY_AND_ASSIGN(GestureListenerImpl);
};
GestureProvider::GestureProvider(const Config& config,
GestureProviderClient* client)
: client_(client),
- needs_show_press_event_(false),
- needs_tap_ending_event_(false),
touch_scroll_in_progress_(false),
- pinch_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) {
DCHECK(client);
InitGestureDetectors(config);
}
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 was_touch_scrolling_ = touch_scroll_in_progress_;
const bool in_scale_gesture =
scale_gesture_listener_->IsScaleGestureDetectionInProgress();
- if (event.GetAction() == MotionEvent::ACTION_DOWN) {
- current_down_event_ = event.Clone();
- touch_scroll_in_progress_ = false;
- needs_show_press_event_ = true;
- current_longpress_time_ = base::TimeTicks();
- SendTapCancelIfNecessary(event);
- }
-
- bool handled = gesture_listener_->OnTouchEvent(event, in_scale_gesture);
- handled |= scale_gesture_listener_->OnTouchEvent(event);
-
- if (event.GetAction() == MotionEvent::ACTION_UP ||
- event.GetAction() == MotionEvent::ACTION_CANCEL) {
- // "Last finger raised" could be an end to movement, but it should
- // only terminate scrolling if the event did not cause a fling.
- if (was_touch_scrolling_ && !handled)
- EndTouchScrollIfNecessary(event.GetEventTime(), true);
-
- // We shouldn't necessarily cancel a tap on ACTION_UP, as the double-tap
- // timeout may yet trigger a SINGLE_TAP.
- if (event.GetAction() == MotionEvent::ACTION_CANCEL)
- SendTapCancelIfNecessary(event);
-
- current_down_event_.reset();
- }
-
+ OnTouchEventHandlingBegin(event);
+ gesture_listener_->OnTouchEvent(event, in_scale_gesture);
+ scale_gesture_listener_->OnTouchEvent(event);
+ OnTouchEventHandlingEnd(event);
return true;
}
-void GestureProvider::ResetGestureDetectors() {
- if (!current_down_event_)
- return;
- scoped_ptr<MotionEvent> cancel_event = current_down_event_->Cancel();
- gesture_listener_->OnTouchEvent(*cancel_event, false);
- scale_gesture_listener_->OnTouchEvent(*cancel_event);
-}
-
-void GestureProvider::SetMultiTouchSupportEnabled(bool enabled) {
- scale_gesture_listener_->set_ignore_detector_events(!enabled);
+void GestureProvider::SetMultiTouchZoomSupportEnabled(bool enabled) {
+ scale_gesture_listener_->SetMultiTouchEnabled(enabled);
}
void GestureProvider::SetDoubleTapSupportForPlatformEnabled(bool enabled) {
- gesture_listener_->SetDoubleTapSupportForPlatformEnabled(enabled);
+ if (double_tap_support_for_platform_ == enabled)
+ return;
+ double_tap_support_for_platform_ = enabled;
+ UpdateDoubleTapDetectionSupport();
}
void GestureProvider::SetDoubleTapSupportForPageEnabled(bool enabled) {
- gesture_listener_->SetDoubleTapSupportForPageEnabled(enabled);
+ if (double_tap_support_for_page_ == enabled)
+ return;
+ double_tap_support_for_page_ = enabled;
+ UpdateDoubleTapDetectionSupport();
}
bool GestureProvider::IsScrollInProgress() const {
bool GestureProvider::IsPinchInProgress() const { return pinch_in_progress_; }
bool GestureProvider::IsDoubleTapInProgress() const {
- return gesture_listener_->IsDoubleTapInProgress();
-}
-
-bool GestureProvider::IsClickDelayDisabled() const {
- return gesture_listener_->IsClickDelayDisabled();
+ 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.gesture_detector_config,
- config.snap_scroll_controller_config,
+ 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(base::TimeTicks time,
- float x,
- float y,
+void GestureProvider::Fling(const MotionEvent& event,
float velocity_x,
float velocity_y) {
if (!velocity_x && !velocity_y) {
- EndTouchScrollIfNecessary(time, true);
+ EndTouchScrollIfNecessary(event, true);
return;
}
// start hint.
GestureEventDetails scroll_details(
ET_GESTURE_SCROLL_BEGIN, velocity_x, velocity_y);
- Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, time, x, y, scroll_details));
+ Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN, event, scroll_details));
}
- EndTouchScrollIfNecessary(time, false);
+ EndTouchScrollIfNecessary(event, false);
GestureEventDetails fling_details(
ET_SCROLL_FLING_START, velocity_x, velocity_y);
- Send(CreateGesture(ET_SCROLL_FLING_START, time, x, y, fling_details));
+ Send(CreateGesture(
+ ET_SCROLL_FLING_START, event, fling_details));
}
void GestureProvider::Send(const GestureEventData& gesture) {
gesture.type == ET_GESTURE_SHOW_PRESS);
switch (gesture.type) {
- case ET_GESTURE_TAP_DOWN:
- needs_tap_ending_event_ = true;
- break;
- case ET_GESTURE_TAP_UNCONFIRMED:
- needs_show_press_event_ = false;
- break;
- case ET_GESTURE_TAP:
- if (needs_show_press_event_)
- Send(CreateGesture(
- ET_GESTURE_SHOW_PRESS, gesture.time, gesture.x, gesture.y));
- needs_tap_ending_event_ = false;
- break;
- case ET_GESTURE_DOUBLE_TAP:
- needs_tap_ending_event_ = false;
- break;
- case ET_GESTURE_TAP_CANCEL:
- if (!needs_tap_ending_event_)
- return;
- needs_tap_ending_event_ = false;
- break;
- case ET_GESTURE_SHOW_PRESS:
- needs_show_press_event_ = false;
- break;
case ET_GESTURE_LONG_PRESS:
DCHECK(!scale_gesture_listener_->IsScaleGestureDetectionInProgress());
current_longpress_time_ = gesture.time;
break;
case ET_GESTURE_LONG_TAP:
- needs_tap_ending_event_ = false;
current_longpress_time_ = base::TimeTicks();
break;
case ET_GESTURE_SCROLL_BEGIN:
+ DCHECK(!touch_scroll_in_progress_);
touch_scroll_in_progress_ = true;
- SendTapCancelIfNecessary(*current_down_event_);
break;
case ET_GESTURE_SCROLL_END:
+ DCHECK(touch_scroll_in_progress_);
+ if (pinch_in_progress_)
+ Send(CreateGesture(ET_GESTURE_PINCH_END,
+ gesture.motion_event_id,
+ gesture.time,
+ gesture.x,
+ gesture.y,
+ gesture.details.touch_points(),
+ gesture.details.bounding_box()));
touch_scroll_in_progress_ = false;
break;
case ET_GESTURE_PINCH_BEGIN:
+ DCHECK(!pinch_in_progress_);
+ if (!touch_scroll_in_progress_)
+ Send(CreateGesture(ET_GESTURE_SCROLL_BEGIN,
+ gesture.motion_event_id,
+ gesture.time,
+ gesture.x,
+ gesture.y,
+ gesture.details.touch_points(),
+ gesture.details.bounding_box()));
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);
}
-void GestureProvider::SendTapCancelIfNecessary(const MotionEvent& event) {
- if (!needs_tap_ending_event_)
- return;
- current_longpress_time_ = base::TimeTicks();
- Send(CreateGesture(ET_GESTURE_TAP_CANCEL, event));
-}
-
bool GestureProvider::SendLongTapIfNecessary(const MotionEvent& event) {
if (event.GetAction() == MotionEvent::ACTION_UP &&
!current_longpress_time_.is_null() &&
!scale_gesture_listener_->IsScaleGestureDetectionInProgress()) {
- SendTapCancelIfNecessary(event);
GestureEventDetails long_tap_details(ET_GESTURE_LONG_TAP, 0, 0);
- long_tap_details.set_bounding_box(
- gfx::RectF(event.GetTouchMajor(), event.GetTouchMajor()));
Send(CreateGesture(ET_GESTURE_LONG_TAP, event, long_tap_details));
return true;
}
return false;
}
-void GestureProvider::EndTouchScrollIfNecessary(base::TimeTicks time,
+void GestureProvider::EndTouchScrollIfNecessary(const MotionEvent& event,
bool send_scroll_end_event) {
if (!touch_scroll_in_progress_)
return;
- touch_scroll_in_progress_ = false;
if (send_scroll_end_event)
- Send(CreateGesture(ET_GESTURE_SCROLL_END, time, 0, 0));
+ 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_)
+ Send(CreateGesture(ET_GESTURE_BEGIN, 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);
+
+ if (gesture_begin_end_types_enabled_)
+ Send(CreateGesture(ET_GESTURE_END, event));
+
+ 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