diff options
author | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-12 05:51:37 +0000 |
---|---|---|
committer | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-12 05:51:37 +0000 |
commit | b2afd3d8ea8fa1fc0d8abb891a99d8ff2eabd30b (patch) | |
tree | 135b195cf9d7d4b9be0bc42970db81aba097a6a6 | |
parent | 283bd1c9807a6a16d595de2bf04f00d9c5bed008 (diff) | |
download | chromium_src-b2afd3d8ea8fa1fc0d8abb891a99d8ff2eabd30b.zip chromium_src-b2afd3d8ea8fa1fc0d8abb891a99d8ff2eabd30b.tar.gz chromium_src-b2afd3d8ea8fa1fc0d8abb891a99d8ff2eabd30b.tar.bz2 |
[Android] Use DIP coordinates with MotionEventAndroid
With this change, all MotionEvent implementations will use DIP coordinates,
eliminating confusion and making the world a better place. All assumptions of
integral coordinates in the gesture detection pipeline have been updated
appropriately.
BUG=332418
Review URL: https://codereview.chromium.org/220063002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@263493 0039d316-1c4b-4281-b951-d872f2087c98
19 files changed, 357 insertions, 356 deletions
diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc index 6cd82b8..2369f6f 100644 --- a/content/browser/android/content_view_core_impl.cc +++ b/content/browser/android/content_view_core_impl.cc @@ -366,8 +366,7 @@ void ContentViewCoreImpl::RenderViewReady() { } void ContentViewCoreImpl::OnGestureEvent(const ui::GestureEventData& gesture) { - SendGestureEvent( - CreateWebGestureEventFromGestureEventData(gesture, 1.f / dpi_scale())); + SendGestureEvent(CreateWebGestureEventFromGestureEventData(gesture)); } RenderWidgetHostViewAndroid* @@ -1056,7 +1055,8 @@ jboolean ContentViewCoreImpl::OnTouchEvent(JNIEnv* env, if (!rwhv) return false; - MotionEventAndroid event(env, + MotionEventAndroid event(1.f / dpi_scale(), + env, motion_event, time_ms, android_action, @@ -1143,8 +1143,7 @@ bool ContentViewCoreImpl::OnMotionEvent(const ui::MotionEvent& event) { return true; } - rwhv->SendTouchEvent( - CreateWebTouchEventFromMotionEvent(event, 1.f / dpi_scale())); + rwhv->SendTouchEvent(CreateWebTouchEventFromMotionEvent(event)); return true; } diff --git a/content/browser/renderer_host/input/motion_event_android.cc b/content/browser/renderer_host/input/motion_event_android.cc index eea14e9..c0f1721 100644 --- a/content/browser/renderer_host/input/motion_event_android.cc +++ b/content/browser/renderer_host/input/motion_event_android.cc @@ -64,26 +64,28 @@ base::TimeTicks FromAndroidTime(int64 time_ms) { } // namespace -MotionEventAndroid::MotionEventAndroid(JNIEnv* env, +MotionEventAndroid::MotionEventAndroid(float pix_to_dip, + JNIEnv* env, jobject event, jlong time_ms, jint android_action, jint pointer_count, jint history_size, jint action_index, - jfloat pos_x_0, - jfloat pos_y_0, - jfloat pos_x_1, - jfloat pos_y_1, + jfloat pos_x_0_pixels, + jfloat pos_y_0_pixels, + jfloat pos_x_1_pixels, + jfloat pos_y_1_pixels, jint pointer_id_0, jint pointer_id_1, - jfloat touch_major_0, - jfloat touch_major_1) + jfloat touch_major_0_pixels, + jfloat touch_major_1_pixels) : cached_time_(FromAndroidTime(time_ms)), cached_action_(FromAndroidAction(android_action)), cached_pointer_count_(pointer_count), cached_history_size_(history_size), cached_action_index_(action_index), + pix_to_dip_(pix_to_dip), should_recycle_(false) { DCHECK_GT(pointer_count, 0); DCHECK_GE(history_size, 0); @@ -91,32 +93,36 @@ MotionEventAndroid::MotionEventAndroid(JNIEnv* env, event_.Reset(env, event); DCHECK(event_.obj()); - cached_positions_[0] = gfx::PointF(pos_x_0, pos_y_0); - cached_positions_[1] = gfx::PointF(pos_x_1, pos_y_1); + cached_positions_[0] = ToDips(gfx::PointF(pos_x_0_pixels, pos_y_0_pixels)); + cached_positions_[1] = ToDips(gfx::PointF(pos_x_1_pixels, pos_y_1_pixels)); cached_pointer_ids_[0] = pointer_id_0; cached_pointer_ids_[1] = pointer_id_1; - cached_touch_majors_[0] = touch_major_0; - cached_touch_majors_[1] = touch_major_1; + cached_touch_majors_[0] = ToDips(touch_major_0_pixels); + cached_touch_majors_[1] = ToDips(touch_major_1_pixels); } -MotionEventAndroid::MotionEventAndroid(JNIEnv* env, jobject event) +MotionEventAndroid::MotionEventAndroid(float pix_to_dip, + JNIEnv* env, + jobject event) : cached_time_(FromAndroidTime(Java_MotionEvent_getEventTime(env, event))), - cached_action_(FromAndroidAction( - Java_MotionEvent_getActionMasked(env, event))), + cached_action_( + FromAndroidAction(Java_MotionEvent_getActionMasked(env, event))), cached_pointer_count_(Java_MotionEvent_getPointerCount(env, event)), cached_history_size_(Java_MotionEvent_getHistorySize(env, event)), cached_action_index_(Java_MotionEvent_getActionIndex(env, event)), + pix_to_dip_(pix_to_dip), should_recycle_(true) { event_.Reset(env, event); DCHECK(event_.obj()); for (size_t i = 0; i < MAX_POINTERS_TO_CACHE; ++i) { if (i < cached_pointer_count_) { - cached_positions_[i].set_x(Java_MotionEvent_getXF_I(env, event, i)); - cached_positions_[i].set_y(Java_MotionEvent_getYF_I(env, event, i)); + cached_positions_[i] = + ToDips(gfx::PointF(Java_MotionEvent_getXF_I(env, event, i), + Java_MotionEvent_getYF_I(env, event, i))); cached_pointer_ids_[i] = Java_MotionEvent_getPointerId(env, event, i); cached_touch_majors_[i] = - Java_MotionEvent_getTouchMajorF_I(env, event, i); + ToDips(Java_MotionEvent_getTouchMajorF_I(env, event, i)); } else { cached_pointer_ids_[i] = 0; cached_touch_majors_[i] = 0.f; @@ -131,6 +137,7 @@ MotionEventAndroid::MotionEventAndroid(const MotionEventAndroid& other) cached_pointer_count_(other.cached_pointer_count_), cached_history_size_(other.cached_history_size_), cached_action_index_(other.cached_action_index_), + pix_to_dip_(other.pix_to_dip_), should_recycle_(true) { DCHECK(event_.obj()); for (size_t i = 0; i < MAX_POINTERS_TO_CACHE; ++i) { @@ -153,9 +160,7 @@ MotionEventAndroid::Action MotionEventAndroid::GetAction() const { return cached_action_; } -int MotionEventAndroid::GetActionIndex() const { - return cached_action_index_; -} +int MotionEventAndroid::GetActionIndex() const { return cached_action_index_; } size_t MotionEventAndroid::GetPointerCount() const { return cached_pointer_count_; @@ -173,24 +178,24 @@ float MotionEventAndroid::GetX(size_t pointer_index) const { DCHECK_LT(pointer_index, cached_pointer_count_); if (pointer_index < MAX_POINTERS_TO_CACHE) return cached_positions_[pointer_index].x(); - return Java_MotionEvent_getXF_I( - AttachCurrentThread(), event_.obj(), pointer_index); + return ToDips(Java_MotionEvent_getXF_I( + AttachCurrentThread(), event_.obj(), pointer_index)); } float MotionEventAndroid::GetY(size_t pointer_index) const { DCHECK_LT(pointer_index, cached_pointer_count_); if (pointer_index < MAX_POINTERS_TO_CACHE) return cached_positions_[pointer_index].y(); - return Java_MotionEvent_getYF_I( - AttachCurrentThread(), event_.obj(), pointer_index); + return ToDips(Java_MotionEvent_getYF_I( + AttachCurrentThread(), event_.obj(), pointer_index)); } float MotionEventAndroid::GetTouchMajor(size_t pointer_index) const { DCHECK_LT(pointer_index, cached_pointer_count_); if (pointer_index < MAX_POINTERS_TO_CACHE) return cached_touch_majors_[pointer_index]; - return Java_MotionEvent_getTouchMajorF_I( - AttachCurrentThread(), event_.obj(), pointer_index); + return ToDips(Java_MotionEvent_getTouchMajorF_I( + AttachCurrentThread(), event_.obj(), pointer_index)); } float MotionEventAndroid::GetPressure(size_t pointer_index) const { @@ -213,23 +218,23 @@ base::TimeTicks MotionEventAndroid::GetHistoricalEventTime( AttachCurrentThread(), event_.obj(), historical_index)); } -float MotionEventAndroid::GetHistoricalTouchMajor(size_t pointer_index, - size_t historical_index) - const { - return Java_MotionEvent_getHistoricalTouchMajorF_I_I( - AttachCurrentThread(), event_.obj(), pointer_index, historical_index); +float MotionEventAndroid::GetHistoricalTouchMajor( + size_t pointer_index, + size_t historical_index) const { + return ToDips(Java_MotionEvent_getHistoricalTouchMajorF_I_I( + AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); } float MotionEventAndroid::GetHistoricalX(size_t pointer_index, size_t historical_index) const { - return Java_MotionEvent_getHistoricalXF_I_I( - AttachCurrentThread(), event_.obj(), pointer_index, historical_index); + return ToDips(Java_MotionEvent_getHistoricalXF_I_I( + AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); } float MotionEventAndroid::GetHistoricalY(size_t pointer_index, size_t historical_index) const { - return Java_MotionEvent_getHistoricalYF_I_I( - AttachCurrentThread(), event_.obj(), pointer_index, historical_index); + return ToDips(Java_MotionEvent_getHistoricalYF_I_I( + AttachCurrentThread(), event_.obj(), pointer_index, historical_index)); } scoped_ptr<ui::MotionEvent> MotionEventAndroid::Clone() const { @@ -237,18 +242,23 @@ scoped_ptr<ui::MotionEvent> MotionEventAndroid::Clone() const { } scoped_ptr<ui::MotionEvent> MotionEventAndroid::Cancel() const { - return scoped_ptr<MotionEvent>(new MotionEventAndroid( - AttachCurrentThread(), - Obtain(GetDownTime(), - GetEventTime(), - MotionEventAndroid::ACTION_CANCEL, - GetX(0), - GetY(0)).obj())); + // The input coordinates to |MotionEventAndroid| are always in device pixels, + // but the cached coordinates are in DIPs. + const gfx::PointF position_pixels = + gfx::ScalePoint(cached_positions_[0], 1.f / pix_to_dip_); + return scoped_ptr<MotionEvent>( + new MotionEventAndroid(pix_to_dip_, + AttachCurrentThread(), + Obtain(GetDownTime(), + GetEventTime(), + MotionEventAndroid::ACTION_CANCEL, + position_pixels.x(), + position_pixels.y()).obj())); } float MotionEventAndroid::GetTouchMinor(size_t pointer_index) const { - return Java_MotionEvent_getTouchMinorF_I( - AttachCurrentThread(), event_.obj(), pointer_index); + return ToDips(Java_MotionEvent_getTouchMinorF_I( + AttachCurrentThread(), event_.obj(), pointer_index)); } float MotionEventAndroid::GetOrientation() const { @@ -260,6 +270,14 @@ base::TimeTicks MotionEventAndroid::GetDownTime() const { Java_MotionEvent_getDownTime(AttachCurrentThread(), event_.obj())); } +float MotionEventAndroid::ToDips(float pixels) const { + return pixels * pix_to_dip_; +} + +gfx::PointF MotionEventAndroid::ToDips(const gfx::PointF& point_pixels) const { + return gfx::ScalePoint(point_pixels, pix_to_dip_); +} + // static bool MotionEventAndroid::RegisterMotionEventAndroid(JNIEnv* env) { return JNI_MotionEvent::RegisterNativesImpl(env); @@ -277,14 +295,14 @@ base::android::ScopedJavaLocalRef<jobject> MotionEventAndroid::Obtain( base::TimeTicks down_time, base::TimeTicks event_time, Action action, - float x, - float y) { + float x_pixels, + float y_pixels) { return Java_MotionEvent_obtainAVME_J_J_I_F_F_I(AttachCurrentThread(), ToAndroidTime(down_time), ToAndroidTime(event_time), ToAndroidAction(action), - x, - y, + x_pixels, + y_pixels, 0); } diff --git a/content/browser/renderer_host/input/motion_event_android.h b/content/browser/renderer_host/input/motion_event_android.h index bcc9048..2b7f45f 100644 --- a/content/browser/renderer_host/input/motion_event_android.h +++ b/content/browser/renderer_host/input/motion_event_android.h @@ -16,25 +16,28 @@ namespace content { // Implementation of ui::MotionEvent wrapping a native Android MotionEvent. +// All *input* coordinates are in device pixels (as with Android MotionEvent), +// while all *output* coordinates are in DIPs (as with WebTouchEvent). class MotionEventAndroid : public ui::MotionEvent { public: // Forcing the caller to provide all cached values upon construction // eliminates the need to perform a JNI call to retrieve values individually. - MotionEventAndroid(JNIEnv* env, + MotionEventAndroid(float pix_to_dip, + JNIEnv* env, jobject event, jlong time_ms, jint android_action, jint pointer_count, jint history_size, jint action_index, - jfloat pos_x_0, - jfloat pos_y_0, - jfloat pos_x_1, - jfloat pos_y_1, + jfloat pos_x_0_pixels, + jfloat pos_y_0_pixels, + jfloat pos_x_1_pixels, + jfloat pos_y_1_pixels, jint pointer_id_0, jint pointer_id_1, - jfloat touch_major_0, - jfloat touch_major_1); + jfloat touch_major_0_pixels, + jfloat touch_major_1_pixels); virtual ~MotionEventAndroid(); // ui::MotionEvent methods. @@ -77,15 +80,18 @@ class MotionEventAndroid : public ui::MotionEvent { base::TimeTicks down_time, base::TimeTicks event_time, Action action, - float x, - float y); + float x_pixels, + float y_pixels); private: MotionEventAndroid(); - MotionEventAndroid(JNIEnv* env, jobject event); + MotionEventAndroid(float pix_to_dip, JNIEnv* env, jobject event); MotionEventAndroid(const MotionEventAndroid&); MotionEventAndroid& operator=(const MotionEventAndroid&); + float ToDips(float pixels) const; + gfx::PointF ToDips(const gfx::PointF& pixels) const; + // Cache pointer coords, id's and major lengths for the most common // touch-related scenarios, i.e., scrolling and pinching. This prevents // redundant JNI fetches for the same bits. @@ -103,6 +109,10 @@ class MotionEventAndroid : public ui::MotionEvent { int cached_pointer_ids_[MAX_POINTERS_TO_CACHE]; float cached_touch_majors_[MAX_POINTERS_TO_CACHE]; + // Used to convert pixel coordinates from the Java-backed MotionEvent to + // DIP coordinates cached/returned by the MotionEventAndroid. + const float pix_to_dip_; + // Whether |event_| should be recycled on destruction. This will only be true // for those events generated via |Obtain(...)|. bool should_recycle_; diff --git a/content/browser/renderer_host/input/touch_event_queue.cc b/content/browser/renderer_host/input/touch_event_queue.cc index a6d8729..8662f4a 100644 --- a/content/browser/renderer_host/input/touch_event_queue.cc +++ b/content/browser/renderer_host/input/touch_event_queue.cc @@ -21,6 +21,11 @@ using ui::LatencyInfo; namespace content { namespace { +// Using a small epsilon when comparing slop distances allows pixel perfect +// slop determination when using fractional DIP coordinates (assuming the slop +// region and DPI scale are reasonably proportioned). +const float kSlopEpsilon = .05f; + typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( @@ -167,6 +172,8 @@ class TouchEventQueue::TouchTimeoutHandler { // Provides touchmove slop suppression for a single touch that remains within // a given slop region, unless the touchstart is preventDefault'ed. +// TODO(jdduke): Use a flag bundled with each TouchEvent declaring whether it +// has exceeded the slop region, removing duplicated slop determination logic. class TouchEventQueue::TouchMoveSlopSuppressor { public: TouchMoveSlopSuppressor(double slop_suppression_length_dips) @@ -288,8 +295,8 @@ TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, dispatching_touch_(false), touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), ack_timeout_enabled_(false), - touchmove_slop_suppressor_( - new TouchMoveSlopSuppressor(touchmove_suppression_length_dips)), + touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( + touchmove_suppression_length_dips + kSlopEpsilon)), absorbing_touch_moves_(false), touch_scrolling_mode_(mode) { DCHECK(client); diff --git a/content/browser/renderer_host/input/web_input_event_util.cc b/content/browser/renderer_host/input/web_input_event_util.cc index 37a0d60..8a7fd92 100644 --- a/content/browser/renderer_host/input/web_input_event_util.cc +++ b/content/browser/renderer_host/input/web_input_event_util.cc @@ -112,7 +112,7 @@ const char* GetKeyIdentifier(ui::KeyboardCode key_code) { case ui::VKEY_UP: return "Up"; case ui::VKEY_DELETE: - return "U+007F"; // Standard says that DEL becomes U+007F. + return "U+007F"; // Standard says that DEL becomes U+007F. case ui::VKEY_MEDIA_NEXT_TRACK: return "MediaNextTrack"; case ui::VKEY_MEDIA_PREV_TRACK: @@ -177,19 +177,17 @@ WebTouchPoint::State ToWebTouchPointState(MotionEvent::Action action, } WebTouchPoint CreateWebTouchPoint(const MotionEvent& event, - size_t pointer_index, - float scale) { + size_t pointer_index) { WebTouchPoint touch; touch.id = event.GetPointerId(pointer_index); touch.state = ToWebTouchPointState( event.GetAction(), static_cast<int>(pointer_index) == event.GetActionIndex()); - touch.position.x = event.GetX(pointer_index) * scale; - touch.position.y = event.GetY(pointer_index) * scale; + touch.position.x = event.GetX(pointer_index); + touch.position.y = event.GetY(pointer_index); // TODO(joth): Raw event co-ordinates. touch.screenPosition = touch.position; - touch.radiusX = touch.radiusY = - event.GetTouchMajor(pointer_index) * 0.5f * scale; + touch.radiusX = touch.radiusY = event.GetTouchMajor(pointer_index) * 0.5f; touch.force = event.GetPressure(pointer_index); return touch; @@ -207,14 +205,15 @@ void UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent* event, if (id) { base::strlcpy(event->keyIdentifier, id, sizeof(event->keyIdentifier) - 1); } else { - base::snprintf(event->keyIdentifier, sizeof(event->keyIdentifier), "U+%04X", + base::snprintf(event->keyIdentifier, + sizeof(event->keyIdentifier), + "U+%04X", base::ToUpperASCII(static_cast<int>(windows_key_code))); } } blink::WebTouchEvent CreateWebTouchEventFromMotionEvent( - const ui::MotionEvent& event, - float scale) { + const ui::MotionEvent& event) { blink::WebTouchEvent result; result.type = ToWebInputEventType(event.GetAction()); @@ -229,84 +228,75 @@ blink::WebTouchEvent CreateWebTouchEventFromMotionEvent( DCHECK_GT(result.touchesLength, 0U); for (size_t i = 0; i < result.touchesLength; ++i) - result.touches[i] = CreateWebTouchPoint(event, i, scale); + result.touches[i] = CreateWebTouchPoint(event, i); return result; } WebGestureEvent CreateWebGestureEventFromGestureEventData( - const ui::GestureEventData& data, - float scale) { + const ui::GestureEventData& data) { WebGestureEvent gesture; - gesture.x = data.x * scale; - gesture.y = data.y * scale; + gesture.x = data.x; + gesture.y = data.y; gesture.timeStampSeconds = (data.time - base::TimeTicks()).InSecondsF(); gesture.sourceDevice = WebGestureEvent::Touchscreen; switch (data.type) { case ui::ET_GESTURE_SHOW_PRESS: gesture.type = WebInputEvent::GestureShowPress; - gesture.data.showPress.width = - data.details.bounding_box_f().width() * scale; - gesture.data.showPress.height = - data.details.bounding_box_f().height() * scale; + gesture.data.showPress.width = data.details.bounding_box_f().width(); + gesture.data.showPress.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_DOUBLE_TAP: gesture.type = WebInputEvent::GestureDoubleTap; DCHECK_EQ(1, data.details.tap_count()); gesture.data.tap.tapCount = data.details.tap_count(); - gesture.data.tap.width = data.details.bounding_box_f().width() * scale; - gesture.data.tap.height = data.details.bounding_box_f().height() * scale; + gesture.data.tap.width = data.details.bounding_box_f().width(); + gesture.data.tap.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_TAP: gesture.type = WebInputEvent::GestureTap; DCHECK_EQ(1, data.details.tap_count()); gesture.data.tap.tapCount = data.details.tap_count(); - gesture.data.tap.width = data.details.bounding_box_f().width() * scale; - gesture.data.tap.height = data.details.bounding_box_f().height() * scale; + gesture.data.tap.width = data.details.bounding_box_f().width(); + gesture.data.tap.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_TAP_UNCONFIRMED: gesture.type = WebInputEvent::GestureTapUnconfirmed; DCHECK_EQ(1, data.details.tap_count()); gesture.data.tap.tapCount = data.details.tap_count(); - gesture.data.tap.width = data.details.bounding_box_f().width() * scale; - gesture.data.tap.height = data.details.bounding_box_f().height() * scale; + gesture.data.tap.width = data.details.bounding_box_f().width(); + gesture.data.tap.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_LONG_PRESS: gesture.type = WebInputEvent::GestureLongPress; - gesture.data.longPress.width = - data.details.bounding_box_f().width() * scale; - gesture.data.longPress.height = - data.details.bounding_box_f().height() * scale; + gesture.data.longPress.width = data.details.bounding_box_f().width(); + gesture.data.longPress.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_LONG_TAP: gesture.type = WebInputEvent::GestureLongTap; - gesture.data.longPress.width = - data.details.bounding_box_f().width() * scale; - gesture.data.longPress.height = - data.details.bounding_box_f().height() * scale; + gesture.data.longPress.width = data.details.bounding_box_f().width(); + gesture.data.longPress.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_SCROLL_BEGIN: gesture.type = WebInputEvent::GestureScrollBegin; - gesture.data.scrollBegin.deltaXHint = - data.details.scroll_x_hint() * scale; - gesture.data.scrollBegin.deltaYHint = - data.details.scroll_y_hint() * scale; + gesture.data.scrollBegin.deltaXHint = data.details.scroll_x_hint(); + gesture.data.scrollBegin.deltaYHint = data.details.scroll_y_hint(); break; case ui::ET_GESTURE_SCROLL_UPDATE: gesture.type = WebInputEvent::GestureScrollUpdate; - gesture.data.scrollUpdate.deltaX = data.details.scroll_x() * scale; - gesture.data.scrollUpdate.deltaY = data.details.scroll_y() * scale; - gesture.data.scrollUpdate.velocityX = data.details.velocity_x() * scale; - gesture.data.scrollUpdate.velocityY = data.details.velocity_y() * scale; + gesture.data.scrollUpdate.deltaX = data.details.scroll_x(); + gesture.data.scrollUpdate.deltaY = data.details.scroll_y(); + gesture.data.scrollUpdate.velocityX = data.details.velocity_x(); + gesture.data.scrollUpdate.velocityY = data.details.velocity_y(); break; case ui::ET_GESTURE_SCROLL_END: gesture.type = WebInputEvent::GestureScrollEnd; break; case ui::ET_SCROLL_FLING_START: gesture.type = WebInputEvent::GestureFlingStart; - gesture.data.flingStart.velocityX = data.details.velocity_x() * scale; - gesture.data.flingStart.velocityY = data.details.velocity_y() * scale; + gesture.data.flingStart.velocityX = data.details.velocity_x(); + gesture.data.flingStart.velocityY = data.details.velocity_y(); break; case ui::ET_SCROLL_FLING_CANCEL: gesture.type = WebInputEvent::GestureFlingCancel; @@ -326,10 +316,8 @@ WebGestureEvent CreateWebGestureEventFromGestureEventData( break; case ui::ET_GESTURE_TAP_DOWN: gesture.type = WebInputEvent::GestureTapDown; - gesture.data.tapDown.width = - data.details.bounding_box_f().width() * scale; - gesture.data.tapDown.height = - data.details.bounding_box_f().height() * scale; + gesture.data.tapDown.width = data.details.bounding_box_f().width(); + gesture.data.tapDown.height = data.details.bounding_box_f().height(); break; case ui::ET_GESTURE_BEGIN: case ui::ET_GESTURE_END: diff --git a/content/browser/renderer_host/input/web_input_event_util.h b/content/browser/renderer_host/input/web_input_event_util.h index f3d3c46..4143a1c 100644 --- a/content/browser/renderer_host/input/web_input_event_util.h +++ b/content/browser/renderer_host/input/web_input_event_util.h @@ -25,14 +25,12 @@ CONTENT_EXPORT void UpdateWindowsKeyCodeAndKeyIdentifier( // Creates a WebTouchEvent from |event|, scaling all size components from // |event| by |scale|. CONTENT_EXPORT blink::WebTouchEvent CreateWebTouchEventFromMotionEvent( - const ui::MotionEvent& event, - float scale); + const ui::MotionEvent& event); // Creates a WebGestureEvent from |event|, scaling all size components from // |event| by |scale|. CONTENT_EXPORT blink::WebGestureEvent CreateWebGestureEventFromGestureEventData( - const ui::GestureEventData& data, - float scale); + const ui::GestureEventData& data); } // namespace content diff --git a/ui/events/gesture_detection/gesture_config_helper.cc b/ui/events/gesture_detection/gesture_config_helper.cc index 86ad6c7..0039e2c 100644 --- a/ui/events/gesture_detection/gesture_config_helper.cc +++ b/ui/events/gesture_detection/gesture_config_helper.cc @@ -4,22 +4,14 @@ #include "ui/events/gesture_detection/gesture_config_helper.h" -namespace ui { - -GestureDetector::Config DefaultGestureDetectorConfig() { - return GestureDetector::Config(); -} +#include "ui/gfx/screen.h" -ScaleGestureDetector::Config DefaultScaleGestureDetectorConfig() { - return ScaleGestureDetector::Config(); -} - -SnapScrollController::Config DefaultSnapScrollControllerConfig() { - return SnapScrollController::Config(); -} +namespace ui { GestureProvider::Config DefaultGestureProviderConfig() { - return GestureProvider::Config(); + GestureProvider::Config config; + config.display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); + return config; } } // namespace ui diff --git a/ui/events/gesture_detection/gesture_config_helper.h b/ui/events/gesture_detection/gesture_config_helper.h index e37268d..0b54c36 100644 --- a/ui/events/gesture_detection/gesture_config_helper.h +++ b/ui/events/gesture_detection/gesture_config_helper.h @@ -9,19 +9,9 @@ #include "ui/events/gesture_detection/gesture_detector.h" #include "ui/events/gesture_detection/gesture_provider.h" #include "ui/events/gesture_detection/scale_gesture_detector.h" -#include "ui/events/gesture_detection/snap_scroll_controller.h" namespace ui { -GESTURE_DETECTION_EXPORT GestureDetector::Config -DefaultGestureDetectorConfig(); - -GESTURE_DETECTION_EXPORT ScaleGestureDetector::Config -DefaultScaleGestureDetectorConfig(); - -GESTURE_DETECTION_EXPORT SnapScrollController::Config -DefaultSnapScrollControllerConfig(); - GESTURE_DETECTION_EXPORT GestureProvider::Config DefaultGestureProviderConfig(); diff --git a/ui/events/gesture_detection/gesture_config_helper_android.cc b/ui/events/gesture_detection/gesture_config_helper_android.cc index 3ed6b77..e37650a 100644 --- a/ui/events/gesture_detection/gesture_config_helper_android.cc +++ b/ui/events/gesture_detection/gesture_config_helper_android.cc @@ -10,59 +10,57 @@ using gfx::ViewConfiguration; namespace ui { - +namespace { // TODO(jdduke): Adopt GestureConfiguration on Android, crbug/339203. -GestureDetector::Config DefaultGestureDetectorConfig() { +GestureDetector::Config DefaultGestureDetectorConfig( + const gfx::Display& display) { GestureDetector::Config config; config.longpress_timeout = base::TimeDelta::FromMilliseconds( ViewConfiguration::GetLongPressTimeoutInMs()); - config.showpress_timeout = base::TimeDelta::FromMilliseconds( - ViewConfiguration::GetTapTimeoutInMs()); + config.showpress_timeout = + base::TimeDelta::FromMilliseconds(ViewConfiguration::GetTapTimeoutInMs()); config.double_tap_timeout = base::TimeDelta::FromMilliseconds( ViewConfiguration::GetDoubleTapTimeoutInMs()); - config.scaled_touch_slop = ViewConfiguration::GetTouchSlopInPixels(); - config.scaled_double_tap_slop = ViewConfiguration::GetDoubleTapSlopInPixels(); - config.scaled_minimum_fling_velocity = - ViewConfiguration::GetMinimumFlingVelocityInPixelsPerSecond(); - config.scaled_maximum_fling_velocity = - ViewConfiguration::GetMaximumFlingVelocityInPixelsPerSecond(); + const float px_to_dp = 1.f / display.device_scale_factor(); + config.touch_slop = + ViewConfiguration::GetTouchSlopInPixels() * px_to_dp; + config.double_tap_slop = + ViewConfiguration::GetDoubleTapSlopInPixels() * px_to_dp; + config.minimum_fling_velocity = + ViewConfiguration::GetMinimumFlingVelocityInPixelsPerSecond() * px_to_dp; + config.maximum_fling_velocity = + ViewConfiguration::GetMaximumFlingVelocityInPixelsPerSecond() * px_to_dp; return config; } -ScaleGestureDetector::Config DefaultScaleGestureDetectorConfig() { +ScaleGestureDetector::Config DefaultScaleGestureDetectorConfig( + const gfx::Display& display) { ScaleGestureDetector::Config config; - config.gesture_detector_config = DefaultGestureDetectorConfig(); + config.gesture_detector_config = DefaultGestureDetectorConfig(display); config.quick_scale_enabled = true; + + const float px_to_dp = 1.f / display.device_scale_factor(); config.min_scaling_touch_major = - ViewConfiguration::GetMinScalingTouchMajorInPixels(); - config.min_scaling_span = ViewConfiguration::GetMinScalingSpanInPixels(); + ViewConfiguration::GetMinScalingTouchMajorInPixels() * px_to_dp; + config.min_scaling_span = + ViewConfiguration::GetMinScalingSpanInPixels() * px_to_dp; return config; } -SnapScrollController::Config DefaultSnapScrollControllerConfig() { - SnapScrollController::Config config; - - const gfx::Display& display = - gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); - - config.screen_width_pixels = display.GetSizeInPixel().width(); - config.screen_height_pixels = display.GetSizeInPixel().height(); - config.device_scale_factor = display.device_scale_factor(); - - return config; -} +} // namespace GestureProvider::Config DefaultGestureProviderConfig() { GestureProvider::Config config; - config.gesture_detector_config = DefaultGestureDetectorConfig(); - config.scale_gesture_detector_config = DefaultScaleGestureDetectorConfig(); - config.snap_scroll_controller_config = DefaultSnapScrollControllerConfig(); + config.display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); + config.gesture_detector_config = DefaultGestureDetectorConfig(config.display); + config.scale_gesture_detector_config = + DefaultScaleGestureDetectorConfig(config.display); config.gesture_begin_end_types_enabled = false; return config; } diff --git a/ui/events/gesture_detection/gesture_config_helper_aura.cc b/ui/events/gesture_detection/gesture_config_helper_aura.cc index 25cce58..e7845ac 100644 --- a/ui/events/gesture_detection/gesture_config_helper_aura.cc +++ b/ui/events/gesture_detection/gesture_config_helper_aura.cc @@ -8,6 +8,7 @@ #include "ui/gfx/screen.h" namespace ui { +namespace { GestureDetector::Config DefaultGestureDetectorConfig() { GestureDetector::Config config; @@ -18,13 +19,13 @@ GestureDetector::Config DefaultGestureDetectorConfig() { GestureConfiguration::show_press_delay_in_ms()); config.double_tap_timeout = base::TimeDelta::FromMilliseconds( GestureConfiguration::semi_long_press_time_in_seconds() * 1000.); - config.scaled_touch_slop = + config.touch_slop = GestureConfiguration::max_touch_move_in_pixels_for_click(); - config.scaled_double_tap_slop = + config.double_tap_slop = GestureConfiguration::max_distance_between_taps_for_double_tap(); - config.scaled_minimum_fling_velocity = + config.minimum_fling_velocity = GestureConfiguration::min_scroll_velocity(); - config.scaled_maximum_fling_velocity = + config.maximum_fling_velocity = GestureConfiguration::fling_velocity_cap(); return config; @@ -34,30 +35,19 @@ ScaleGestureDetector::Config DefaultScaleGestureDetectorConfig() { ScaleGestureDetector::Config config; config.gesture_detector_config = DefaultGestureDetectorConfig(); - config.min_scaling_touch_major = GestureConfiguration::default_radius() / 2; + config.min_scaling_touch_major = GestureConfiguration::default_radius() * 2; config.min_scaling_span = GestureConfiguration::min_distance_for_pinch_scroll_in_pixels(); return config; } -SnapScrollController::Config DefaultSnapScrollControllerConfig() { - SnapScrollController::Config config; - - const gfx::Display& display = - gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); - - config.screen_width_pixels = display.GetSizeInPixel().width(); - config.screen_height_pixels = display.GetSizeInPixel().height(); - config.device_scale_factor = display.device_scale_factor(); - - return config; -} +} // namespace GestureProvider::Config DefaultGestureProviderConfig() { GestureProvider::Config config; + config.display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); config.gesture_detector_config = DefaultGestureDetectorConfig(); config.scale_gesture_detector_config = DefaultScaleGestureDetectorConfig(); - config.snap_scroll_controller_config = DefaultSnapScrollControllerConfig(); config.gesture_begin_end_types_enabled = true; return config; } diff --git a/ui/events/gesture_detection/gesture_detector.cc b/ui/events/gesture_detection/gesture_detector.cc index 8cf76e6..1628c26 100644 --- a/ui/events/gesture_detection/gesture_detector.cc +++ b/ui/events/gesture_detection/gesture_detector.cc @@ -12,6 +12,15 @@ namespace ui { namespace { +// Using a small epsilon when comparing slop distances allows pixel perfect +// slop determination when using fractional DIP coordinates (assuming the slop +// region and DPI scale are reasonably proportioned). +const float kSlopEpsilon = .05f; + +// Minimum distance a scroll must have traveled from the last scroll/focal point +// to trigger an |OnScroll| callback. +const float kScrollEpsilon = .1f; + // Constants used by TimeoutGestureHandler. enum TimeoutEvent { SHOW_PRESS = 0, @@ -28,10 +37,10 @@ GestureDetector::Config::Config() : longpress_timeout(base::TimeDelta::FromMilliseconds(500)), showpress_timeout(base::TimeDelta::FromMilliseconds(180)), double_tap_timeout(base::TimeDelta::FromMilliseconds(300)), - scaled_touch_slop(8), - scaled_double_tap_slop(100), - scaled_minimum_fling_velocity(50), - scaled_maximum_fling_velocity(8000) {} + touch_slop(8), + double_tap_slop(100), + minimum_fling_velocity(50), + maximum_fling_velocity(8000) {} GestureDetector::Config::~Config() {} @@ -267,10 +276,10 @@ bool GestureDetector::OnTouchEvent(const MotionEvent& ev) { DCHECK(double_tap_listener_); handled |= double_tap_listener_->OnDoubleTapEvent(ev); } else if (always_in_tap_region_) { - const int delta_x = static_cast<int>(focus_x - down_focus_x_); - const int delta_y = static_cast<int>(focus_y - down_focus_y_); - int distance = (delta_x * delta_x) + (delta_y * delta_y); - if (distance > touch_slop_square_) { + const float delta_x = focus_x - down_focus_x_; + const float delta_y = focus_y - down_focus_y_; + const float distance_square = delta_x * delta_x + delta_y * delta_y; + if (distance_square > touch_slop_square_) { handled = listener_->OnScroll( *current_down_event_, ev, scroll_x, scroll_y); last_focus_x_ = focus_x; @@ -278,9 +287,10 @@ bool GestureDetector::OnTouchEvent(const MotionEvent& ev) { always_in_tap_region_ = false; timeout_handler_->Stop(); } - if (distance > double_tap_touch_slop_square_) + if (distance_square > double_tap_touch_slop_square_) always_in_bigger_tap_region_ = false; - } else if ((std::abs(scroll_x) >= 1) || (std::abs(scroll_y) >= 1)) { + } else if (std::abs(scroll_x) > kScrollEpsilon || + std::abs(scroll_y) > kScrollEpsilon) { handled = listener_->OnScroll(*current_down_event_, ev, scroll_x, scroll_y); last_focus_x_ = focus_x; @@ -340,15 +350,15 @@ bool GestureDetector::OnTouchEvent(const MotionEvent& ev) { void GestureDetector::Init(const Config& config) { DCHECK(listener_); - const int touch_slop = config.scaled_touch_slop; - const int double_tap_touch_slop = touch_slop; - const int double_tap_slop = config.scaled_double_tap_slop; - min_fling_velocity_ = config.scaled_minimum_fling_velocity; - max_fling_velocity_ = config.scaled_maximum_fling_velocity; + const float touch_slop = config.touch_slop + kSlopEpsilon; + const float double_tap_touch_slop = touch_slop; + const float double_tap_slop = config.double_tap_slop + kSlopEpsilon; touch_slop_square_ = touch_slop * touch_slop; double_tap_touch_slop_square_ = double_tap_touch_slop * double_tap_touch_slop; double_tap_slop_square_ = double_tap_slop * double_tap_slop; double_tap_timeout_ = config.double_tap_timeout; + min_fling_velocity_ = config.minimum_fling_velocity; + max_fling_velocity_ = config.maximum_fling_velocity; } void GestureDetector::OnShowPressTimeout() { @@ -401,8 +411,8 @@ bool GestureDetector::IsConsideredDoubleTap( double_tap_timeout_) return false; - int delta_x = static_cast<int>(first_down.GetX() - second_down.GetX()); - int delta_y = static_cast<int>(first_down.GetY() - second_down.GetY()); + const float delta_x = first_down.GetX() - second_down.GetX(); + const float delta_y = first_down.GetY() - second_down.GetY(); return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_); } diff --git a/ui/events/gesture_detection/gesture_detector.h b/ui/events/gesture_detection/gesture_detector.h index 29a004a..c09b31f 100644 --- a/ui/events/gesture_detection/gesture_detector.h +++ b/ui/events/gesture_detection/gesture_detector.h @@ -23,13 +23,23 @@ class GestureDetector { struct GESTURE_DETECTION_EXPORT Config { Config(); ~Config(); + base::TimeDelta longpress_timeout; base::TimeDelta showpress_timeout; base::TimeDelta double_tap_timeout; - int scaled_touch_slop; - int scaled_double_tap_slop; - int scaled_minimum_fling_velocity; - int scaled_maximum_fling_velocity; + + // Distance a touch can wander before a scroll will occur (in dips). + float touch_slop; + + // Distance the first touch can wander before it is no longer considered a + // double tap (in dips). + float double_tap_slop; + + // Minimum velocity to initiate a fling (in dips/second). + float minimum_fling_velocity; + + // Maximum velocity of an initiated fling (in dips/second). + float maximum_fling_velocity; }; class GestureListener { @@ -114,11 +124,11 @@ class GestureDetector { GestureListener* const listener_; DoubleTapListener* double_tap_listener_; - int touch_slop_square_; - int double_tap_touch_slop_square_; - int double_tap_slop_square_; - int min_fling_velocity_; - int max_fling_velocity_; + float touch_slop_square_; + float double_tap_touch_slop_square_; + float double_tap_slop_square_; + float min_fling_velocity_; + float max_fling_velocity_; base::TimeDelta double_tap_timeout_; bool still_down_; diff --git a/ui/events/gesture_detection/gesture_provider.cc b/ui/events/gesture_detection/gesture_provider.cc index 0acbd19..d932a02 100644 --- a/ui/events/gesture_detection/gesture_provider.cc +++ b/ui/events/gesture_detection/gesture_provider.cc @@ -94,7 +94,9 @@ GestureEventDetails CreateTapGestureDetails(EventType type, // GestureProvider:::Config GestureProvider::Config::Config() - : disable_click_delay(false), gesture_begin_end_types_enabled(false) {} + : display(gfx::Display::kInvalidDisplayID, gfx::Rect(1, 1)), + disable_click_delay(false), + gesture_begin_end_types_enabled(false) {} GestureProvider::Config::~Config() {} @@ -104,11 +106,9 @@ class GestureProvider::ScaleGestureListenerImpl : public ScaleGestureDetector::ScaleGestureListener { public: ScaleGestureListenerImpl(const ScaleGestureDetector::Config& config, - float device_scale_factor, GestureProvider* provider) : scale_gesture_detector_(config, this), provider_(provider), - px_to_dp_(1.0f / device_scale_factor), ignore_multitouch_events_(false), pinch_event_sent_(false) {} @@ -174,7 +174,7 @@ class GestureProvider::ScaleGestureListenerImpl (detector.GetCurrentSpanY() - detector.GetPreviousSpanY()) * 0.5f; scale = std::pow(scale > 1 ? 1.0f + kDoubleTapDragZoomSpeed : 1.0f - kDoubleTapDragZoomSpeed, - std::abs(dy * px_to_dp_)); + std::abs(dy)); } GestureEventDetails pinch_details(ET_GESTURE_PINCH_UPDATE, scale, 0); provider_->Send(CreateGesture(ET_GESTURE_PINCH_UPDATE, @@ -217,9 +217,6 @@ class GestureProvider::ScaleGestureListenerImpl GestureProvider* const provider_; - // TODO(jdduke): Remove this when all MotionEvent's use DIPs. - const float px_to_dp_; - // Completely silence multi-touch (pinch) scaling events. Used in WebView when // zoom support is turned off. bool ignore_multitouch_events_; @@ -237,23 +234,18 @@ class GestureProvider::GestureListenerImpl 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), 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), - last_raw_x_(0), - last_raw_y_(0), - accumulated_scroll_error_x_(0), - accumulated_scroll_error_y_(0) {} + seen_first_scroll_event_(false) {} virtual ~GestureListenerImpl() {} @@ -276,10 +268,6 @@ class GestureProvider::GestureListenerImpl 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( @@ -304,7 +292,7 @@ class GestureProvider::GestureListenerImpl 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; } @@ -318,8 +306,6 @@ class GestureProvider::GestureListenerImpl } } - 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. @@ -337,20 +323,9 @@ class GestureProvider::GestureListenerImpl 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)); } @@ -384,10 +359,6 @@ class GestureProvider::GestureListenerImpl } virtual bool OnSingleTapUp(const MotionEvent& e) OVERRIDE { - if (IsPointOutsideCurrentSlopRegion(e.GetRawX(), e.GetRawY())) { - 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 @@ -487,16 +458,6 @@ class GestureProvider::GestureListenerImpl } private: - 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; } bool IsDoubleTapEnabled() const { @@ -512,11 +473,7 @@ class GestureProvider::GestureListenerImpl // 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_; @@ -531,18 +488,6 @@ class GestureProvider::GestureListenerImpl // gesture. bool seen_first_scroll_event_; - // 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); }; @@ -627,15 +572,13 @@ bool GestureProvider::IsClickDelayDisabled() const { 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, - config.snap_scroll_controller_config.device_scale_factor, - this)); + scale_gesture_listener_.reset( + new ScaleGestureListenerImpl(config.scale_gesture_detector_config, this)); UpdateDoubleTapDetectionSupport(); } diff --git a/ui/events/gesture_detection/gesture_provider.h b/ui/events/gesture_detection/gesture_provider.h index b8e85b7..41f0990 100644 --- a/ui/events/gesture_detection/gesture_provider.h +++ b/ui/events/gesture_detection/gesture_provider.h @@ -11,6 +11,7 @@ #include "ui/events/gesture_detection/gesture_detector.h" #include "ui/events/gesture_detection/scale_gesture_detector.h" #include "ui/events/gesture_detection/snap_scroll_controller.h" +#include "ui/gfx/display.h" namespace ui { @@ -29,9 +30,9 @@ class GESTURE_DETECTION_EXPORT GestureProvider { struct GESTURE_DETECTION_EXPORT Config { Config(); ~Config(); + gfx::Display display; GestureDetector::Config gesture_detector_config; ScaleGestureDetector::Config scale_gesture_detector_config; - SnapScrollController::Config snap_scroll_controller_config; // If |disable_click_delay| is true and double-tap support is disabled, // there will be no delay before tap events. When double-tap support is diff --git a/ui/events/gesture_detection/gesture_provider_unittest.cc b/ui/events/gesture_detection/gesture_provider_unittest.cc index 04ba3fc..5d9c664 100644 --- a/ui/events/gesture_detection/gesture_provider_unittest.cc +++ b/ui/events/gesture_detection/gesture_provider_unittest.cc @@ -131,8 +131,8 @@ class GestureProviderTest : public testing::Test, public GestureProviderClient { return sConfig; } - int GetTouchSlop() const { - return GetDefaultConfig().gesture_detector_config.scaled_touch_slop; + float GetTouchSlop() const { + return GetDefaultConfig().gesture_detector_config.touch_slop; } base::TimeDelta GetLongPressTimeout() const { @@ -156,8 +156,8 @@ class GestureProviderTest : public testing::Test, public GestureProviderClient { void CheckScrollEventSequenceForEndActionType( MotionEvent::Action end_action_type) { base::TimeTicks event_time = base::TimeTicks::Now(); - const int scroll_to_x = kFakeCoordX + 100; - const int scroll_to_y = kFakeCoordY + 100; + const float scroll_to_x = kFakeCoordX + 100; + const float scroll_to_y = kFakeCoordY + 100; int motion_event_id = 0; MockMotionEvent event = @@ -533,8 +533,8 @@ TEST_F(GestureProviderTest, DoubleTapDragZoomBasic) { // Generate a scroll gesture and verify that the resulting scroll motion event // has both absolute and relative position information. TEST_F(GestureProviderTest, ScrollUpdateValues) { - const int delta_x = 16; - const int delta_y = 84; + const float delta_x = 16; + const float delta_y = 84; const base::TimeTicks event_time = TimeTicks::Now(); @@ -625,8 +625,8 @@ TEST_F(GestureProviderTest, FractionalScroll) { // Generate a scroll gesture and verify that the resulting scroll begin event // has the expected hint values. TEST_F(GestureProviderTest, ScrollBeginValues) { - const int delta_x = 13; - const int delta_y = 89; + const float delta_x = 13; + const float delta_y = 89; const base::TimeTicks event_time = TimeTicks::Now(); @@ -789,8 +789,8 @@ TEST_F(GestureProviderTest, NoGestureLongPressDuringDoubleTap) { // Verify that the touch slop region is removed from the first scroll delta to // avoid a jump when starting to scroll. TEST_F(GestureProviderTest, TouchSlopRemovedFromScroll) { - const int scaled_touch_slop = GetTouchSlop(); - const int scroll_delta = 5; + const float touch_slop = GetTouchSlop(); + const float scroll_delta = 5; base::TimeTicks event_time = base::TimeTicks::Now(); @@ -801,7 +801,7 @@ TEST_F(GestureProviderTest, TouchSlopRemovedFromScroll) { event = ObtainMotionEvent(event_time + kOneMicrosecond * 2, MotionEvent::ACTION_MOVE, kFakeCoordX, - kFakeCoordY + scaled_touch_slop + scroll_delta); + kFakeCoordY + touch_slop + scroll_delta); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); EXPECT_EQ(ET_GESTURE_SCROLL_UPDATE, GetMostRecentGestureEventType()); @@ -811,6 +811,56 @@ TEST_F(GestureProviderTest, TouchSlopRemovedFromScroll) { EXPECT_EQ(1, gesture.details.touch_points()); } +// Verify that movement within the touch slop region does not generate a scroll, +// and that the slop region is correct even when using fractional coordinates. +TEST_F(GestureProviderTest, NoScrollWithinTouchSlop) { + const float touch_slop = GetTouchSlop(); + const float scale_factor = 2.5f; + const int touch_slop_pixels = static_cast<int>(scale_factor * touch_slop); + + base::TimeTicks event_time = base::TimeTicks::Now(); + + MockMotionEvent event = + ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN); + EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); + + event = ObtainMotionEvent(event_time + kOneMicrosecond * 2, + MotionEvent::ACTION_MOVE, + kFakeCoordX + touch_slop_pixels / scale_factor, + kFakeCoordY); + EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); + EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); + + event = ObtainMotionEvent(event_time + kOneMicrosecond * 2, + MotionEvent::ACTION_MOVE, + kFakeCoordX, + kFakeCoordY + touch_slop_pixels / scale_factor); + EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); + EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); + + event = ObtainMotionEvent(event_time + kOneMicrosecond * 2, + MotionEvent::ACTION_MOVE, + kFakeCoordX - touch_slop_pixels / scale_factor, + kFakeCoordY); + EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); + EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); + + event = ObtainMotionEvent(event_time + kOneMicrosecond * 2, + MotionEvent::ACTION_MOVE, + kFakeCoordX, + kFakeCoordY - touch_slop_pixels / scale_factor); + EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); + EXPECT_FALSE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); + + event = + ObtainMotionEvent(event_time + kOneMicrosecond * 2, + MotionEvent::ACTION_MOVE, + kFakeCoordX, + kFakeCoordY + (touch_slop_pixels + 1.f) / scale_factor); + EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); + EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); +} + TEST_F(GestureProviderTest, NoDoubleTapWhenExplicitlyDisabled) { gesture_provider_->SetDoubleTapSupportForPlatformEnabled(false); @@ -1043,15 +1093,15 @@ TEST_F(GestureProviderTest, FixedPageScaleDuringDoubleTapDragZoom) { // Verify that pinch zoom sends the proper event sequence. TEST_F(GestureProviderTest, PinchZoom) { base::TimeTicks event_time = base::TimeTicks::Now(); - const int scaled_touch_slop = GetTouchSlop(); + const float touch_slop = GetTouchSlop(); int motion_event_id = 0; gesture_provider_->SetDoubleTapSupportForPageEnabled(false); gesture_provider_->SetDoubleTapSupportForPlatformEnabled(true); gesture_provider_->SetMultiTouchSupportEnabled(true); - int secondary_coord_x = kFakeCoordX + 20 * scaled_touch_slop; - int secondary_coord_y = kFakeCoordY + 20 * scaled_touch_slop; + int secondary_coord_x = kFakeCoordX + 20 * touch_slop; + int secondary_coord_y = kFakeCoordY + 20 * touch_slop; MockMotionEvent event = ObtainMotionEvent(event_time, MotionEvent::ACTION_DOWN); @@ -1075,8 +1125,8 @@ TEST_F(GestureProviderTest, PinchZoom) { EXPECT_EQ(1U, GetReceivedGestureCount()); EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points()); - secondary_coord_x += 5 * scaled_touch_slop; - secondary_coord_y += 5 * scaled_touch_slop; + secondary_coord_x += 5 * touch_slop; + secondary_coord_y += 5 * touch_slop; event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX, @@ -1095,8 +1145,8 @@ TEST_F(GestureProviderTest, PinchZoom) { EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_UPDATE)); - secondary_coord_x += 2 * scaled_touch_slop; - secondary_coord_y += 2 * scaled_touch_slop; + secondary_coord_x += 2 * touch_slop; + secondary_coord_y += 2 * touch_slop; event = ObtainMotionEvent(event_time, MotionEvent::ACTION_MOVE, kFakeCoordX, diff --git a/ui/events/gesture_detection/scale_gesture_detector.cc b/ui/events/gesture_detection/scale_gesture_detector.cc index bf8a0d3..f9e091b 100644 --- a/ui/events/gesture_detection/scale_gesture_detector.cc +++ b/ui/events/gesture_detection/scale_gesture_detector.cc @@ -17,6 +17,11 @@ using base::TimeTicks; namespace ui { namespace { +// Using a small epsilon when comparing slop distances allows pixel perfect +// slop determination when using fractional DPI coordinates (assuming the slop +// region and DPI scale are reasonably proportioned). +const float kSlopEpsilon = .05f; + const int kTouchStabilizeTimeMs = 128; const float kScaleFactor = .5f; @@ -26,9 +31,9 @@ const float kScaleFactor = .5f; // Note: These constants were taken directly from the default (unscaled) // versions found in Android's ViewConfiguration. ScaleGestureDetector::Config::Config() - : quick_scale_enabled(true), - min_scaling_touch_major(48), - min_scaling_span(200) {} + : min_scaling_touch_major(48), + min_scaling_span(200), + quick_scale_enabled(true) {} ScaleGestureDetector::Config::~Config() {} @@ -72,9 +77,10 @@ ScaleGestureDetector::ScaleGestureDetector(const Config& config, double_tap_mode_(DOUBLE_TAP_MODE_NONE), event_before_or_above_starting_gesture_event_(false) { DCHECK(listener_); - span_slop_ = config.gesture_detector_config.scaled_touch_slop * 2; + span_slop_ = + (config.gesture_detector_config.touch_slop + kSlopEpsilon) * 2; touch_min_major_ = config.min_scaling_touch_major; - min_span_ = config.min_scaling_span; + min_span_ = config.min_scaling_span + kSlopEpsilon; SetQuickScaleEnabled(config.quick_scale_enabled); } @@ -199,7 +205,7 @@ bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) { initial_span_ = prev_span_ = curr_span_ = span; } - const int min_span = InDoubleTapMode() ? span_slop_ : min_span_; + const float min_span = InDoubleTapMode() ? span_slop_ : min_span_; if (!in_progress_ && span >= min_span && (was_in_progress || std::abs(span - initial_span_) > span_slop_)) { prev_span_x_ = curr_span_x_ = span_x; diff --git a/ui/events/gesture_detection/scale_gesture_detector.h b/ui/events/gesture_detection/scale_gesture_detector.h index 2d83ca1..08b6288 100644 --- a/ui/events/gesture_detection/scale_gesture_detector.h +++ b/ui/events/gesture_detection/scale_gesture_detector.h @@ -24,9 +24,15 @@ class ScaleGestureDetector : public GestureDetector::SimpleGestureListener { Config(); ~Config(); GestureDetector::Config gesture_detector_config; + + // Minimum accepted value for TouchMajor while scaling (in dips). + float min_scaling_touch_major; + + // Minimum span needed to initiate a scaling gesture (in dips). + float min_scaling_span; + + // Whether double-tap drag scaling is enabled. bool quick_scale_enabled; - int min_scaling_touch_major; - int min_scaling_span; }; class ScaleGestureListener { @@ -120,8 +126,8 @@ class ScaleGestureDetector : public GestureDetector::SimpleGestureListener { base::TimeTicks curr_time_; base::TimeTicks prev_time_; bool in_progress_; - int span_slop_; - int min_span_; + float span_slop_; + float min_span_; // Bounds for recently seen values. float touch_upper_; @@ -129,7 +135,7 @@ class ScaleGestureDetector : public GestureDetector::SimpleGestureListener { float touch_history_last_accepted_; int touch_history_direction_; base::TimeTicks touch_history_last_accepted_time_; - int touch_min_major_; + float touch_min_major_; float double_tap_focus_x_; float double_tap_focus_y_; DoubleTapMode double_tap_mode_; diff --git a/ui/events/gesture_detection/snap_scroll_controller.cc b/ui/events/gesture_detection/snap_scroll_controller.cc index 85ac432..bde5a35 100644 --- a/ui/events/gesture_detection/snap_scroll_controller.cc +++ b/ui/events/gesture_detection/snap_scroll_controller.cc @@ -7,19 +7,33 @@ #include <cmath> #include "ui/events/gesture_detection/motion_event.h" +#include "ui/gfx/display.h" namespace ui { namespace { const int kSnapBound = 16; -} // namespace +const float kMinSnapChannelDistance = kSnapBound; +const float kMaxSnapChannelDistance = kMinSnapChannelDistance * 3.f; +const float kSnapChannelDipsPerScreenDip = kMinSnapChannelDistance / 480.f; + +float CalculateChannelDistance(const gfx::Display& display) { + if (display.bounds().IsEmpty()) + return kMinSnapChannelDistance; + + float screen_size = + std::abs(hypot(static_cast<float>(display.bounds().width()), + static_cast<float>(display.bounds().height()))); -SnapScrollController::Config::Config() - : screen_width_pixels(1), screen_height_pixels(1), device_scale_factor(1) {} + float snap_channel_distance = screen_size * kSnapChannelDipsPerScreenDip; + return std::max(kMinSnapChannelDistance, + std::min(kMaxSnapChannelDistance, snap_channel_distance)); +} + +} // namespace -SnapScrollController::Config::~Config() {} -SnapScrollController::SnapScrollController(const Config& config) - : channel_distance_(CalculateChannelDistance(config)), +SnapScrollController::SnapScrollController(const gfx::Display& display) + : channel_distance_(CalculateChannelDistance(display)), snap_scroll_mode_(SNAP_NONE), first_touch_x_(-1), first_touch_y_(-1), @@ -89,27 +103,4 @@ void SnapScrollController::SetSnapScrollingMode( } } -// static -float SnapScrollController::CalculateChannelDistance(const Config& config) { - float channel_distance = 16.f; - - const float screen_size = std::abs( - hypot((float)config.screen_width_pixels / config.device_scale_factor, - (float)config.screen_height_pixels / config.device_scale_factor)); - if (screen_size < 480.f) { - channel_distance = 16.f; - } else if (screen_size < 800.f) { - channel_distance = 22.f; - } else if (screen_size < 1120.f) { - channel_distance = 28.f; - } else { - channel_distance = 34.f; - } - channel_distance = channel_distance * config.device_scale_factor; - if (channel_distance < 16.f) - channel_distance = 16.f; - - return channel_distance; -} - } // namespace ui diff --git a/ui/events/gesture_detection/snap_scroll_controller.h b/ui/events/gesture_detection/snap_scroll_controller.h index ed8717b..753c2bc 100644 --- a/ui/events/gesture_detection/snap_scroll_controller.h +++ b/ui/events/gesture_detection/snap_scroll_controller.h @@ -8,6 +8,10 @@ #include "base/basictypes.h" #include "ui/events/gesture_detection/gesture_detection_export.h" +namespace gfx { +class Display; +} + namespace ui { class MotionEvent; @@ -17,15 +21,7 @@ class ZoomManager; // Controls the scroll snapping behavior based on scroll updates. class SnapScrollController { public: - struct GESTURE_DETECTION_EXPORT Config { - Config(); - ~Config(); - int screen_width_pixels; - int screen_height_pixels; - float device_scale_factor; - }; - - explicit SnapScrollController(const Config& config); + explicit SnapScrollController(const gfx::Display& display); ~SnapScrollController(); // Updates the snap scroll mode based on the given X and Y distance to be @@ -49,8 +45,6 @@ class SnapScrollController { SNAP_VERT }; - static float CalculateChannelDistance(const Config& config); - float channel_distance_; SnapMode snap_scroll_mode_; float first_touch_x_; |