forked from mirrors/gecko-dev
Bug 892684 - Implement axis locking in AsyncPanZoomController [r=kats,botond]
This commit is contained in:
parent
f58ece2dd9
commit
1403178461
6 changed files with 177 additions and 16 deletions
|
|
@ -42,6 +42,9 @@ pref("gfx.azpc.fling_repaint_interval", "50"); // prefer 20 fps
|
|||
pref("gfx.axis.fling_friction", "0.002");
|
||||
pref("gfx.axis.fling_stopped_threshold", "0.2");
|
||||
|
||||
// 0 = free, 1 = standard, 2 = sticky
|
||||
pref("apzc.axis_lock_mode", 1);
|
||||
|
||||
// Enable Microsoft TSF support by default for imes.
|
||||
pref("intl.enable_tsf_support", true);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "gfxTypes.h" // for gfxFloat
|
||||
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
|
||||
#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
|
||||
#include "mozilla/Constants.h" // for M_PI
|
||||
#include "mozilla/EventForwards.h" // for nsEventStatus_*
|
||||
#include "mozilla/Preferences.h" // for Preferences
|
||||
#include "mozilla/ReentrantMonitor.h" // for ReentrantMonitorAutoEnter, etc
|
||||
|
|
@ -64,7 +65,27 @@ namespace layers {
|
|||
*/
|
||||
static float gTouchStartTolerance = 1.0f/16.0f;
|
||||
|
||||
static const float EPSILON = 0.0001;
|
||||
static const float EPSILON = 0.0001f;
|
||||
|
||||
/**
|
||||
* Angle from axis within which we stay axis-locked
|
||||
*/
|
||||
static const double AXIS_LOCK_ANGLE = M_PI / 6.0; // 30 degrees
|
||||
|
||||
/**
|
||||
* The distance in inches the user must pan before axis lock can be broken
|
||||
*/
|
||||
static const float AXIS_BREAKOUT_THRESHOLD = 1.0f/32.0f;
|
||||
|
||||
/**
|
||||
* The angle at which axis lock can be broken
|
||||
*/
|
||||
static const double AXIS_BREAKOUT_ANGLE = M_PI / 8.0; // 22.5 degrees
|
||||
|
||||
/**
|
||||
* The preferred axis locking style. See AxisLockMode for possible values.
|
||||
*/
|
||||
static int32_t gAxisLockMode = 0;
|
||||
|
||||
/**
|
||||
* Maximum amount of time while panning before sending a viewport change. This
|
||||
|
|
@ -153,6 +174,23 @@ static int gAsyncScrollTimeout = 300;
|
|||
*/
|
||||
static bool gAsyncZoomDisabled = false;
|
||||
|
||||
/**
|
||||
* Is aAngle within the given threshold of the horizontal axis?
|
||||
* @param aAngle an angle in radians in the range [0, pi]
|
||||
* @param aThreshold an angle in radians in the range [0, pi/2]
|
||||
*/
|
||||
static bool IsCloseToHorizontal(float aAngle, float aThreshold)
|
||||
{
|
||||
return (aAngle < aThreshold || aAngle > (M_PI - aThreshold));
|
||||
}
|
||||
|
||||
// As above, but for the vertical axis.
|
||||
static bool IsCloseToVertical(float aAngle, float aThreshold)
|
||||
{
|
||||
return (fabs(aAngle - (M_PI / 2)) < aThreshold);
|
||||
}
|
||||
|
||||
|
||||
static TimeStamp sFrameTime;
|
||||
|
||||
static TimeStamp
|
||||
|
|
@ -191,6 +229,7 @@ AsyncPanZoomController::InitializeGlobalState()
|
|||
Preferences::AddIntVarCache(&gAsyncScrollThrottleTime, "apzc.asyncscroll.throttle", gAsyncScrollThrottleTime);
|
||||
Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apzc.asyncscroll.timeout", gAsyncScrollTimeout);
|
||||
Preferences::AddBoolVarCache(&gAsyncZoomDisabled, "apzc.asynczoom.disabled", gAsyncZoomDisabled);
|
||||
Preferences::AddIntVarCache(&gAxisLockMode, "apzc.axis_lock_mode", gAxisLockMode);
|
||||
|
||||
gComputedTimingFunction = new ComputedTimingFunction();
|
||||
gComputedTimingFunction->Init(
|
||||
|
|
@ -272,6 +311,11 @@ AsyncPanZoomController::GetTouchStartTolerance()
|
|||
return gTouchStartTolerance;
|
||||
}
|
||||
|
||||
/* static */AsyncPanZoomController::AxisLockMode AsyncPanZoomController::GetAxisLockMode()
|
||||
{
|
||||
return static_cast<AxisLockMode>(gAxisLockMode);
|
||||
}
|
||||
|
||||
static CSSPoint
|
||||
WidgetSpaceToCompensatedViewportSpace(const ScreenPoint& aPoint,
|
||||
const CSSToScreenScale& aCurrentZoom)
|
||||
|
|
@ -295,7 +339,7 @@ nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent)
|
|||
// responding in a timely fashion, this only introduces a nearly constant few
|
||||
// hundred ms of lag.
|
||||
if (mFrameMetrics.mMayHaveTouchListeners && aEvent.mInputType == MULTITOUCH_INPUT &&
|
||||
(mState == NOTHING || mState == TOUCHING || mState == PANNING)) {
|
||||
(mState == NOTHING || mState == TOUCHING || IsPanningState(mState))) {
|
||||
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
|
||||
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
|
||||
SetState(WAITING_LISTENERS);
|
||||
|
|
@ -413,6 +457,8 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
|
|||
break;
|
||||
case TOUCHING:
|
||||
case PANNING:
|
||||
case PANNING_LOCKED_X:
|
||||
case PANNING_LOCKED_Y:
|
||||
case PINCHING:
|
||||
case WAITING_LISTENERS:
|
||||
NS_WARNING("Received impossible touch in OnTouchStart");
|
||||
|
|
@ -452,6 +498,8 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
|
|||
}
|
||||
|
||||
case PANNING:
|
||||
case PANNING_LOCKED_X:
|
||||
case PANNING_LOCKED_Y:
|
||||
TrackTouch(aEvent);
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
|
||||
|
|
@ -495,6 +543,8 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
|||
return nsEventStatus_eIgnore;
|
||||
|
||||
case PANNING:
|
||||
case PANNING_LOCKED_X:
|
||||
case PANNING_LOCKED_Y:
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
ScheduleComposite();
|
||||
|
|
@ -704,13 +754,37 @@ const gfx::Point AsyncPanZoomController::GetAccelerationVector() {
|
|||
}
|
||||
|
||||
void AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
|
||||
float dx = mX.PanDistance(),
|
||||
dy = mY.PanDistance();
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
float dx = mX.PanDistance(point.x);
|
||||
float dy = mY.PanDistance(point.y);
|
||||
|
||||
// When the touch move breaks through the pan threshold, reposition the touch down origin
|
||||
// so the page won't jump when we start panning.
|
||||
mX.StartTouch(point.x);
|
||||
mY.StartTouch(point.y);
|
||||
mLastEventTime = aEvent.mTime;
|
||||
|
||||
if (GetAxisLockMode() == FREE) {
|
||||
SetState(PANNING);
|
||||
return;
|
||||
}
|
||||
|
||||
double angle = atan2(dy, dx); // range [-pi, pi]
|
||||
angle = fabs(angle); // range [0, pi]
|
||||
|
||||
if (!mX.Scrollable() || !mY.Scrollable()) {
|
||||
SetState(PANNING);
|
||||
} else if (IsCloseToHorizontal(angle, AXIS_LOCK_ANGLE)) {
|
||||
mY.SetScrollingDisabled(true);
|
||||
SetState(PANNING_LOCKED_X);
|
||||
} else if (IsCloseToVertical(angle, AXIS_LOCK_ANGLE)) {
|
||||
mX.SetScrollingDisabled(true);
|
||||
SetState(PANNING_LOCKED_Y);
|
||||
} else {
|
||||
SetState(PANNING);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
|
||||
|
|
@ -775,6 +849,32 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
|
|||
return;
|
||||
}
|
||||
|
||||
// If we're axis-locked, check if the user is trying to break the lock
|
||||
if (GetAxisLockMode() == STICKY) {
|
||||
ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
|
||||
float dx = mX.PanDistance(point.x);
|
||||
float dy = mY.PanDistance(point.y);
|
||||
|
||||
double angle = atan2(dy, dx); // range [-pi, pi]
|
||||
angle = fabs(angle); // range [0, pi]
|
||||
|
||||
float breakThreshold = AXIS_BREAKOUT_THRESHOLD * APZCTreeManager::GetDPI();
|
||||
|
||||
if (fabs(dx) > breakThreshold || fabs(dy) > breakThreshold) {
|
||||
if (mState == PANNING_LOCKED_X) {
|
||||
if (!IsCloseToHorizontal(angle, AXIS_BREAKOUT_ANGLE)) {
|
||||
mY.SetScrollingDisabled(false);
|
||||
SetState(PANNING);
|
||||
}
|
||||
} else if (mState == PANNING_LOCKED_Y) {
|
||||
if (!IsCloseToVertical(angle, AXIS_BREAKOUT_ANGLE)) {
|
||||
mX.SetScrollingDisabled(false);
|
||||
SetState(PANNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateWithTouchAtDevicePoint(aEvent);
|
||||
|
||||
AttemptScroll(prevTouchPoint, touchPoint);
|
||||
|
|
@ -1361,14 +1461,18 @@ void AsyncPanZoomController::SetState(PanZoomState aNewState) {
|
|||
}
|
||||
|
||||
if (mGeckoContentController) {
|
||||
if (oldState == PANNING && aNewState != PANNING) {
|
||||
if (IsPanningState(oldState) && !IsPanningState(aNewState)) {
|
||||
mGeckoContentController->HandlePanEnd();
|
||||
} else if (oldState != PANNING && aNewState == PANNING) {
|
||||
} else if (!IsPanningState(oldState) && IsPanningState(aNewState)) {
|
||||
mGeckoContentController->HandlePanBegin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::IsPanningState(PanZoomState aState) {
|
||||
return (aState == PANNING || aState == PANNING_LOCKED_X || aState == PANNING_LOCKED_Y);
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::TimeoutTouchListeners() {
|
||||
mTouchListenerTimeoutTask = nullptr;
|
||||
ContentReceivedTouch(false);
|
||||
|
|
|
|||
|
|
@ -484,7 +484,11 @@ private:
|
|||
NOTHING, /* no touch-start events received */
|
||||
FLING, /* all touches removed, but we're still scrolling page */
|
||||
TOUCHING, /* one touch-start event received */
|
||||
|
||||
PANNING, /* panning the frame */
|
||||
PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */
|
||||
PANNING_LOCKED_Y, /* as above for Y axis */
|
||||
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
ANIMATING_ZOOM, /* animated zoom to a new rect */
|
||||
WAITING_LISTENERS, /* a state halfway between NOTHING and TOUCHING - the user has
|
||||
|
|
@ -492,6 +496,14 @@ private:
|
|||
prevented the default actions yet. we still need to abort animations. */
|
||||
};
|
||||
|
||||
enum AxisLockMode {
|
||||
FREE, /* No locking at all */
|
||||
STANDARD, /* Default axis locking mode that remains locked until pan ends*/
|
||||
STICKY, /* Allow lock to be broken, with hysteresis */
|
||||
};
|
||||
|
||||
static AxisLockMode GetAxisLockMode();
|
||||
|
||||
/**
|
||||
* Helper to set the current state. Holds the monitor before actually setting
|
||||
* it. If the monitor is already held by the current thread, it is safe to
|
||||
|
|
@ -499,6 +511,8 @@ private:
|
|||
*/
|
||||
void SetState(PanZoomState aState);
|
||||
|
||||
bool IsPanningState(PanZoomState mState);
|
||||
|
||||
uint64_t mLayersId;
|
||||
nsRefPtr<CompositorParent> mCompositorParent;
|
||||
TaskThrottler mPaintThrottler;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
@ -103,13 +103,14 @@ Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
|
|||
: mPos(0),
|
||||
mVelocity(0.0f),
|
||||
mAcceleration(0),
|
||||
mScrollingDisabled(false),
|
||||
mAsyncPanZoomController(aAsyncPanZoomController)
|
||||
{
|
||||
InitAxisPrefs();
|
||||
}
|
||||
|
||||
void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeDelta) {
|
||||
float newVelocity = (mPos - aPos) / aTimeDelta.ToMilliseconds();
|
||||
float newVelocity = mScrollingDisabled ? 0 : (mPos - aPos) / aTimeDelta.ToMilliseconds();
|
||||
|
||||
bool curVelocityBelowThreshold = fabsf(newVelocity) < gVelocityThreshold;
|
||||
bool directionChange = (mVelocity > 0) != (newVelocity > 0);
|
||||
|
|
@ -133,9 +134,15 @@ void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeD
|
|||
void Axis::StartTouch(int32_t aPos) {
|
||||
mStartPos = aPos;
|
||||
mPos = aPos;
|
||||
mScrollingDisabled = false;
|
||||
}
|
||||
|
||||
float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut) {
|
||||
if (mScrollingDisabled) {
|
||||
aOverscrollAmountOut = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fabsf(mVelocity) < gVelocityThreshold) {
|
||||
mAcceleration = 0;
|
||||
}
|
||||
|
|
@ -159,6 +166,10 @@ float Axis::PanDistance() {
|
|||
return fabsf(mPos - mStartPos);
|
||||
}
|
||||
|
||||
float Axis::PanDistance(float aPos) {
|
||||
return fabsf(aPos - mStartPos);
|
||||
}
|
||||
|
||||
void Axis::EndTouch() {
|
||||
mAcceleration++;
|
||||
|
||||
|
|
@ -182,6 +193,13 @@ void Axis::CancelTouch() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Axis::Scrollable() {
|
||||
if (mScrollingDisabled) {
|
||||
return false;
|
||||
}
|
||||
return GetCompositionLength() < GetPageLength();
|
||||
}
|
||||
|
||||
bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) {
|
||||
if (fabsf(mVelocity) <= gFlingStoppedThreshold) {
|
||||
// If the velocity is very low, just set it to 0 and stop the fling,
|
||||
|
|
@ -284,7 +302,7 @@ float Axis::ScaleWillOverscrollAmount(ScreenToScreenScale aScale, float aFocus)
|
|||
}
|
||||
|
||||
float Axis::GetVelocity() {
|
||||
return mVelocity;
|
||||
return mScrollingDisabled ? 0 : mVelocity;
|
||||
}
|
||||
|
||||
float Axis::GetAccelerationFactor() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 : */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
|
@ -69,10 +69,11 @@ public:
|
|||
|
||||
/**
|
||||
* Takes a requested displacement to the position of this axis, and adjusts
|
||||
* it to account for acceleration (which might increase the displacement)
|
||||
* and overscroll (which might decrease the displacement; this is to prevent
|
||||
* the viewport from overscrolling the page rect). If overscroll ocurred,
|
||||
* its amount is written to |aOverscrollAmountOut|.
|
||||
* it to account for acceleration (which might increase the displacement),
|
||||
* overscroll (which might decrease the displacement; this is to prevent the
|
||||
* viewport from overscrolling the page rect), and axis locking (which might
|
||||
* prevent any displacement from happening). If overscroll ocurred, its amount
|
||||
* is written to |aOverscrollAmountOut|.
|
||||
* The adjusted displacement is returned.
|
||||
*/
|
||||
float AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut);
|
||||
|
|
@ -84,6 +85,12 @@ public:
|
|||
*/
|
||||
float PanDistance();
|
||||
|
||||
/**
|
||||
* Gets the distance between the starting position of the touch supplied in
|
||||
* startTouch() and the supplied position.
|
||||
*/
|
||||
float PanDistance(float aPos);
|
||||
|
||||
/**
|
||||
* Applies friction during a fling, or cancels the fling if the velocity is
|
||||
* too low. Returns true if the fling should continue to another frame, or
|
||||
|
|
@ -92,6 +99,14 @@ public:
|
|||
*/
|
||||
bool FlingApplyFrictionOrCancel(const TimeDuration& aDelta);
|
||||
|
||||
/*
|
||||
* Returns true if the page is zoomed in to some degree along this axis such that scrolling is
|
||||
* possible and this axis has not been scroll locked while panning. Otherwise, returns false.
|
||||
*/
|
||||
bool Scrollable();
|
||||
|
||||
void SetScrollingDisabled(bool aDisabled) { mScrollingDisabled = aDisabled; }
|
||||
|
||||
/**
|
||||
* Gets the overscroll state of the axis in its current position.
|
||||
*/
|
||||
|
|
@ -184,6 +199,7 @@ protected:
|
|||
// they are flinging multiple times in a row very quickly, probably trying to
|
||||
// reach one of the extremes of the page.
|
||||
int32_t mAcceleration;
|
||||
bool mScrollingDisabled; // Whether movement on this axis is locked.
|
||||
AsyncPanZoomController* mAsyncPanZoomController;
|
||||
nsTArray<float> mVelocityQueue;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -273,6 +273,12 @@ pref("media.video_stats.enabled", true);
|
|||
// Whether to enable the audio writing APIs on the audio element
|
||||
pref("media.audio_data.enabled", true);
|
||||
|
||||
// Whether to lock touch scrolling to one axis at a time
|
||||
// 0 = FREE (No locking at all)
|
||||
// 1 = STANDARD (Once locked, remain locked until scrolling ends)
|
||||
// 2 = STICKY (Allow lock to be broken, with hysteresis)
|
||||
pref("apzc.axis_lock_mode", 0);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Whether to run in native HiDPI mode on machines with "Retina"/HiDPI display;
|
||||
// <= 0 : hidpi mode disabled, display will just use pixel-based upscaling
|
||||
|
|
|
|||
Loading…
Reference in a new issue