Skip to content

Commit

Permalink
Bug 1391770 - Don't allow a faraway second tap to start a one-touch-p…
Browse files Browse the repository at this point in the history
…inch gesture. r=botond

This patch adds a new tolerance pref, which controls how far the second touchdown
is allowed to be from the first touchdown when detecting a multi-tap gesture
such as double-tap or one-touch-pinch. This stops the one-touch-pinch code
from inadvertently triggering when the user does a tap followed by a second tap
far away from the first.

The default value for the new pref is 5x the touch-start tolerance pref. This
seems to provide a reasonable behaviour for me, although this value could
probably be tuned.

MozReview-Commit-ID: 63aAyGCbvoN
  • Loading branch information
staktrace committed Aug 19, 2017
1 parent 8882a39 commit 9c89681
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 4 deletions.
14 changes: 14 additions & 0 deletions gfx/layers/apz/src/AsyncPanZoomController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,14 @@ typedef GenericFlingAnimation FlingAnimation;
* \li\b apz.record_checkerboarding
* Whether or not to record detailed info on checkerboarding events.
*
* \li\b apz.second_tap_tolerance
* Constant describing the tolerance in distance we use, multiplied by the
* device DPI, within which a second tap is counted as part of a gesture
* continuing from the first tap. Making this larger allows the user more
* distance between the first and second taps in a "double tap" or "one touch
* pinch" gesture.\n
* Units: (real-world, i.e. screen) inches
*
* \li\b apz.test.logging_enabled
* Enable logging of APZ test data (see bug 961289).
*
Expand Down Expand Up @@ -832,6 +840,12 @@ AsyncPanZoomController::GetTouchStartTolerance()
return (gfxPrefs::APZTouchStartTolerance() * APZCTreeManager::GetDPI());
}

/* static */ScreenCoord
AsyncPanZoomController::GetSecondTapTolerance()
{
return (gfxPrefs::APZSecondTapTolerance() * APZCTreeManager::GetDPI());
}

/* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
{
return static_cast<AxisLockMode>(gfxPrefs::APZAxisLockMode());
Expand Down
6 changes: 6 additions & 0 deletions gfx/layers/apz/src/AsyncPanZoomController.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ class AsyncPanZoomController {
* distance, but it's the closest thing we currently have.
*/
static ScreenCoord GetTouchStartTolerance();
/**
* Same as GetTouchStartTolerance, but the tolerance for how close the second
* tap has to be to the first tap in order to be counted as part of a multi-tap
* gesture (double-tap or one-touch-pinch).
*/
static ScreenCoord GetSecondTapTolerance();

AsyncPanZoomController(uint64_t aLayersId,
APZCTreeManager* aTreeManager,
Expand Down
32 changes: 29 additions & 3 deletions gfx/layers/apz/src/GestureEventListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,20 @@ nsEventStatus GestureEventListener::HandleInputTouchSingleStart()
CreateMaxTapTimeoutTask();
break;
case GESTURE_FIRST_SINGLE_TOUCH_UP:
SetState(GESTURE_SECOND_SINGLE_TOUCH_DOWN);
if (SecondTapIsFar()) {
// If the second tap goes down far away from the first, then bail out
// of the gesture.
CancelLongTapTimeoutTask();
CancelMaxTapTimeoutTask();
mSingleTapSent = Nothing();
SetState(GESTURE_NONE);
} else {
// Otherwise, reset the touch start position so that, if this turns into
// a one-touch-pinch gesture, it uses the second tap's down position as
// the focus, rather than the first tap's.
mTouchStartPosition = mLastTouchInput.mTouches[0].mLocalScreenPoint;
SetState(GESTURE_SECOND_SINGLE_TOUCH_DOWN);
}
break;
default:
NS_WARNING("Unhandled state upon single touch start");
Expand Down Expand Up @@ -240,12 +253,25 @@ nsEventStatus GestureEventListener::HandleInputTouchMultiStart()
return rv;
}

bool GestureEventListener::MoveDistanceIsLarge()
bool GestureEventListener::MoveDistanceExceeds(ScreenCoord aThreshold) const
{
const ParentLayerPoint start = mLastTouchInput.mTouches[0].mLocalScreenPoint;
ParentLayerPoint delta = start - mTouchStartPosition;
ScreenPoint screenDelta = mAsyncPanZoomController->ToScreenCoordinates(delta, start);
return (screenDelta.Length() > AsyncPanZoomController::GetTouchStartTolerance());
return (screenDelta.Length() > aThreshold);
}

bool GestureEventListener::MoveDistanceIsLarge() const
{
return MoveDistanceExceeds(AsyncPanZoomController::GetTouchStartTolerance());
}

bool GestureEventListener::SecondTapIsFar() const
{
// Allow a little more room here, because the is actually lifting their finger
// off the screen before replacing it, and that tends to have more error than
// wiggling the finger while on the screen.
return MoveDistanceExceeds(AsyncPanZoomController::GetSecondTapTolerance());
}

nsEventStatus GestureEventListener::HandleInputTouchMove()
Expand Down
4 changes: 3 additions & 1 deletion gfx/layers/apz/src/GestureEventListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ class GestureEventListener final {

void TriggerSingleTapConfirmedEvent();

bool MoveDistanceIsLarge();
bool MoveDistanceExceeds(ScreenCoord aThreshold) const;
bool MoveDistanceIsLarge() const;
bool SecondTapIsFar() const;

/**
* Returns current vertical span, counting from the where the user first put
Expand Down
1 change: 1 addition & 0 deletions gfx/thebes/gfxPrefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ class gfxPrefs final
DECL_GFX_PREF(Live, "apz.popups.enabled", APZPopupsEnabled, bool, false);
DECL_GFX_PREF(Live, "apz.printtree", APZPrintTree, bool, false);
DECL_GFX_PREF(Live, "apz.record_checkerboarding", APZRecordCheckerboarding, bool, false);
DECL_GFX_PREF(Live, "apz.second_tap_tolerance", APZSecondTapTolerance, float, 0.5f);
DECL_GFX_PREF(Live, "apz.test.fails_with_native_injection", APZTestFailsWithNativeInjection, bool, false);
DECL_GFX_PREF(Live, "apz.test.logging_enabled", APZTestLoggingEnabled, bool, false);
DECL_GFX_PREF(Live, "apz.touch_move_tolerance", APZTouchMoveTolerance, float, 0.1f);
Expand Down
1 change: 1 addition & 0 deletions mobile/android/app/mobile.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ pref("apz.fling_friction", "0.004");
pref("apz.fling_stopped_threshold", "0.0");
pref("apz.max_velocity_inches_per_ms", "0.07");
pref("apz.overscroll.enabled", true);
pref("apz.second_tap_tolerance", "0.3");
pref("apz.touch_move_tolerance", "0.03");
pref("apz.touch_start_tolerance", "0.06");

Expand Down
1 change: 1 addition & 0 deletions modules/libpref/init/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ pref("apz.record_checkerboarding", true);
#else
pref("apz.record_checkerboarding", false);
#endif
pref("apz.second_tap_tolerance", "0.5");
pref("apz.test.logging_enabled", false);
pref("apz.touch_start_tolerance", "0.1");
pref("apz.touch_move_tolerance", "0.1");
Expand Down

0 comments on commit 9c89681

Please sign in to comment.