Bug 776226: Implement axis locking when panning along one axis r=roc

This commit is contained in:
Doug Sherk 2012-07-22 23:49:07 -04:00
parent 9c6ec8dfc6
commit 64c27116c3
4 changed files with 83 additions and 21 deletions

View File

@ -17,6 +17,10 @@
namespace mozilla {
namespace layers {
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
static const float EPSILON = 0.0001;
/**
@ -37,6 +41,11 @@ static const PRInt32 FLING_REPAINT_INTERVAL = 75;
*/
static const float MIN_SKATE_SPEED = 0.5f;
/**
* Angle from axis within which we stay axis-locked.
*/
static const float AXIS_LOCK_ANGLE = M_PI / 6.0;
AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoContentController,
GestureBehavior aGestures)
: mGeckoContentController(aGeckoContentController),
@ -214,10 +223,6 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
}
nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
nsIntPoint point = touch.mScreenPoint;
PRInt32 xPos = point.x, yPos = point.y;
switch (mState) {
case FLING:
case NOTHING:
@ -226,14 +231,12 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
return nsEventStatus_eIgnore;
case TOUCHING: {
float panThreshold = 1.0f/16.0f * mDPI;
if (PanDistance(aEvent) < panThreshold) {
float panThreshold = 1.0f/2.0f * mDPI;
UpdateWithTouchAtDevicePoint(aEvent);
if (PanDistance() < panThreshold) {
return nsEventStatus_eIgnore;
}
mLastRepaint = aEvent.mTime;
mX.StartTouch(xPos);
mY.StartTouch(yPos);
mState = PANNING;
StartPanning(aEvent);
return nsEventStatus_eConsumeNoDefault;
}
@ -428,12 +431,7 @@ nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent)
return nsEventStatus_eIgnore;
}
float AsyncPanZoomController::PanDistance(const MultiTouchInput& aEvent) {
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
nsIntPoint point = touch.mScreenPoint;
PRInt32 xPos = point.x, yPos = point.y;
mX.UpdateWithTouchAtDevicePoint(xPos, TimeDuration(0));
mY.UpdateWithTouchAtDevicePoint(yPos, TimeDuration(0));
float AsyncPanZoomController::PanDistance() {
return NS_hypot(mX.PanDistance(), mY.PanDistance()) * mFrameMetrics.mResolution.width;
}
@ -444,7 +442,28 @@ const nsPoint AsyncPanZoomController::GetVelocityVector() {
);
}
void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
void AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
float dx = mX.PanDistance(),
dy = mY.PanDistance();
double angle = atan2(dy, dx); // range [-pi, pi]
angle = fabs(angle); // range [0, pi]
mX.StartTouch(touch.mScreenPoint.x);
mY.StartTouch(touch.mScreenPoint.y);
mState = PANNING;
mLastRepaint = aEvent.mTime;
if (angle < AXIS_LOCK_ANGLE || angle > (M_PI - AXIS_LOCK_ANGLE)) {
mY.LockPanning();
} else if (fabsf(angle - M_PI / 2) < AXIS_LOCK_ANGLE) {
mX.LockPanning();
}
}
void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
SingleTouchData& touch = GetFirstSingleTouch(aEvent);
nsIntPoint point = touch.mScreenPoint;
PRInt32 xPos = point.x, yPos = point.y;
@ -455,10 +474,22 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
return;
}
mX.UpdateWithTouchAtDevicePoint(xPos, timeDelta);
mY.UpdateWithTouchAtDevicePoint(yPos, timeDelta);
}
void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
TimeDuration timeDelta = TimeDuration().FromMilliseconds(aEvent.mTime - mLastEventTime);
// Probably a duplicate event, just throw it away.
if (timeDelta.ToMilliseconds() <= EPSILON) {
return;
}
UpdateWithTouchAtDevicePoint(aEvent);
{
MonitorAutoLock monitor(mMonitor);
mX.UpdateWithTouchAtDevicePoint(xPos, timeDelta);
mY.UpdateWithTouchAtDevicePoint(yPos, timeDelta);
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.

View File

@ -264,7 +264,7 @@ protected:
* current touch (this only makes sense if a touch is currently happening and
* OnTouchMove() is being invoked).
*/
float PanDistance(const MultiTouchInput& aEvent);
float PanDistance();
/**
* Gets a vector of the velocities of each axis.
@ -278,6 +278,18 @@ protected:
*/
SingleTouchData& GetFirstSingleTouch(const MultiTouchInput& aEvent);
/**
* Sets up anything needed for panning. This may lock one of the axes if the
* angle of movement is heavily skewed towards it.
*/
void StartPanning(const MultiTouchInput& aStartPoint);
/**
* Wrapper for Axis::UpdateWithTouchAtDevicePoint(). Calls this function for
* both axes and factors in the time delta from the last update.
*/
void UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent);
/**
* Does any panning required due to a new touch event.
*/

View File

@ -47,12 +47,17 @@ static const float FLING_STOPPED_THRESHOLD = 0.01f;
Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
: mPos(0.0f),
mVelocity(0.0f),
mAsyncPanZoomController(aAsyncPanZoomController)
mAsyncPanZoomController(aAsyncPanZoomController),
mLockPanning(false)
{
}
void Axis::UpdateWithTouchAtDevicePoint(PRInt32 aPos, const TimeDuration& aTimeDelta) {
if (mLockPanning) {
return;
}
float newVelocity = (mPos - aPos) / aTimeDelta.ToMilliseconds();
bool curVelocityIsLow = fabsf(newVelocity) < 0.01f;
@ -75,6 +80,7 @@ void Axis::StartTouch(PRInt32 aPos) {
mStartPos = aPos;
mPos = aPos;
mVelocity = 0.0f;
mLockPanning = false;
}
PRInt32 Axis::GetDisplacementForDuration(float aScale, const TimeDuration& aDelta) {
@ -95,6 +101,10 @@ void Axis::StopTouch() {
mVelocity = 0.0f;
}
void Axis::LockPanning() {
mLockPanning = true;
}
bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) {
if (fabsf(mVelocity) <= FLING_STOPPED_THRESHOLD) {
// If the velocity is very low, just set it to 0 and stop the fling,

View File

@ -58,6 +58,14 @@ public:
*/
void StopTouch();
/**
* Sets axis locking. This prevents any panning along this axis. If the
* current touch point is updated and the axis is locked, the velocity will
* not be recalculated. Any already-existing velocity will however stay the
* same.
*/
void LockPanning();
/**
* Gets displacement that should have happened since the previous touch.
* Note: Does not reset the displacement. It gets recalculated on the next
@ -164,6 +172,7 @@ protected:
PRInt32 mStartPos;
float mVelocity;
nsRefPtr<AsyncPanZoomController> mAsyncPanZoomController;
bool mLockPanning;
};
class AxisX : public Axis {