Bug 944938 - Add event structs and APZ event handling for pan gesture events that can be used for Mac touchpad scrolling. r=kats

This commit is contained in:
Markus Stange 2014-06-07 00:49:49 +02:00
parent 3a6289bbd6
commit a986c2df8c
4 changed files with 244 additions and 25 deletions

View File

@ -445,6 +445,27 @@ APZCTreeManager::ReceiveInputEvent(const InputData& aEvent,
}
}
break;
} case PANGESTURE_INPUT: {
const PanGestureInput& panInput = aEvent.AsPanGestureInput();
bool inOverscrolledApzc = false;
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
&inOverscrolledApzc);
if (apzc) {
if (panInput.mType == PanGestureInput::PANGESTURE_START ||
panInput.mType == PanGestureInput::PANGESTURE_MOMENTUMSTART) {
BuildOverscrollHandoffChain(apzc);
}
apzc->GetGuid(aOutTargetGuid);
GetInputTransforms(apzc, transformToApzc, transformToGecko);
PanGestureInput inputForApzc(panInput);
ApplyTransform(&(inputForApzc.mPanStartPoint), transformToApzc);
result = apzc->ReceiveInputEvent(inputForApzc);
if (panInput.mType == PanGestureInput::PANGESTURE_END ||
panInput.mType == PanGestureInput::PANGESTURE_MOMENTUMEND) {
ClearOverscrollHandoffChain();
}
}
break;
} case PINCHGESTURE_INPUT: {
const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
bool inOverscrolledApzc = false;

View File

@ -714,6 +714,21 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent)
}
break;
}
case PANGESTURE_INPUT: {
const PanGestureInput& panGestureInput = aEvent.AsPanGestureInput();
switch (panGestureInput.mType) {
case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, true); break;
case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
default: NS_WARNING("Unhandled pan gesture"); break;
}
break;
}
default: NS_WARNING("Unhandled input event"); break;
}
@ -1085,6 +1100,93 @@ AsyncPanZoomController::ConvertToGecko(const ScreenPoint& aPoint, CSSPoint* aOut
return false;
}
nsEventStatus AsyncPanZoomController::OnPanMayBegin(const PanGestureInput& aEvent) {
APZC_LOG("%p got a pan-maybegin in state %d\n", this, mState);
mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
CancelAnimation();
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEvent) {
APZC_LOG("%p got a pan-cancelled in state %d\n", this, mState);
mX.CancelTouch();
mY.CancelTouch();
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent) {
APZC_LOG("%p got a pan-begin in state %d\n", this, mState);
mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
mY.StartTouch(aEvent.mPanStartPoint.y, aEvent.mTime);
if (GetAxisLockMode() == FREE) {
SetState(PANNING);
return nsEventStatus_eConsumeNoDefault;
}
float dx = aEvent.mPanDisplacement.x, dy = aEvent.mPanDisplacement.y;
double angle = atan2(dy, dx); // range [-pi, pi]
angle = fabs(angle); // range [0, pi]
HandlePanning(angle);
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
// We need to update the axis velocity in order to get a useful display port
// size and position. We need to do so even if this is a momentum pan (i.e.
// aFingersOnTouchpad == false); in that case the "with touch" part is not
// really appropriate, so we may want to rethink this at some point.
mX.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.x, aEvent.mTime);
mY.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.y, aEvent.mTime);
HandlePanningUpdate(aEvent.mPanDisplacement.x, aEvent.mPanDisplacement.y);
CallDispatchScroll(aEvent.mPanStartPoint, aEvent.mPanStartPoint + aEvent.mPanDisplacement, 0);
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
APZC_LOG("%p got a pan-end in state %d\n", this, mState);
mX.EndTouch(aEvent.mTime);
mY.EndTouch(aEvent.mTime);
RequestContentRepaint();
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& aEvent) {
APZC_LOG("%p got a pan-momentumstart in state %d\n", this, mState);
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPanMomentumEnd(const PanGestureInput& aEvent) {
APZC_LOG("%p got a pan-momentumend in state %d\n", this, mState);
// We need to reset the velocity to zero. We don't really have a "touch"
// here because the touch has already ended long before the momentum
// animation started, but I guess it doesn't really matter for now.
mX.CancelTouch();
mY.CancelTouch();
RequestContentRepaint();
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
APZC_LOG("%p got a long-press in state %d\n", this, mState);
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
@ -1235,6 +1337,7 @@ void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBe
}
void AsyncPanZoomController::HandlePanning(double aAngle) {
ReentrantMonitorAutoEnter lock(mMonitor);
if (!gfxPrefs::APZCrossSlideEnabled() && (!mX.CanScrollNow() || !mY.CanScrollNow())) {
SetState(PANNING);
} else if (IsCloseToHorizontal(aAngle, AXIS_LOCK_ANGLE)) {
@ -1258,6 +1361,31 @@ void AsyncPanZoomController::HandlePanning(double aAngle) {
}
}
void AsyncPanZoomController::HandlePanningUpdate(float aDX, float aDY) {
// If we're axis-locked, check if the user is trying to break the lock
if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
double angle = atan2(aDY, aDX); // range [-pi, pi]
angle = fabs(angle); // range [0, pi]
float breakThreshold = AXIS_BREAKOUT_THRESHOLD * APZCTreeManager::GetDPI();
if (fabs(aDX) > breakThreshold || fabs(aDY) > breakThreshold) {
if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
if (!IsCloseToHorizontal(angle, AXIS_BREAKOUT_ANGLE)) {
mY.SetAxisLocked(false);
SetState(PANNING);
}
} else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
if (!IsCloseToVertical(angle, AXIS_BREAKOUT_ANGLE)) {
mX.SetAxisLocked(false);
SetState(PANNING);
}
}
}
}
}
nsEventStatus AsyncPanZoomController::StartPanning(const MultiTouchInput& aEvent) {
ReentrantMonitorAutoEnter lock(mMonitor);
@ -1435,31 +1563,9 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
ScreenIntPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
ScreenIntPoint touchPoint = GetFirstTouchScreenPoint(aEvent);
// If we're axis-locked, check if the user is trying to break the lock
if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
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 || mState == CROSS_SLIDING_X) {
if (!IsCloseToHorizontal(angle, AXIS_BREAKOUT_ANGLE)) {
mY.SetAxisLocked(false);
SetState(PANNING);
}
} else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
if (!IsCloseToVertical(angle, AXIS_BREAKOUT_ANGLE)) {
mX.SetAxisLocked(false);
SetState(PANNING);
}
}
}
}
float dx = mX.PanDistance(touchPoint.x);
float dy = mY.PanDistance(touchPoint.y);
HandlePanningUpdate(dx, dy);
UpdateWithTouchAtDevicePoint(aEvent);

View File

@ -387,6 +387,17 @@ protected:
*/
nsEventStatus OnScaleEnd(const PinchGestureInput& aEvent);
/**
* Helper methods for handling pan events.
*/
nsEventStatus OnPanMayBegin(const PanGestureInput& aEvent);
nsEventStatus OnPanCancelled(const PanGestureInput& aEvent);
nsEventStatus OnPanBegin(const PanGestureInput& aEvent);
nsEventStatus OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad);
nsEventStatus OnPanEnd(const PanGestureInput& aEvent);
nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent);
nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent);
/**
* Helper methods for long press gestures.
*/
@ -471,6 +482,11 @@ protected:
*/
void HandlePanning(double angle);
/**
* Update the panning state and axis locks.
*/
void HandlePanningUpdate(float aDX, float aDY);
/**
* Sets up anything needed for panning. This takes us out of the "TOUCHING"
* state and starts actually panning us.

View File

@ -19,11 +19,13 @@ namespace mozilla {
enum InputType
{
MULTITOUCH_INPUT,
PANGESTURE_INPUT,
PINCHGESTURE_INPUT,
TAPGESTURE_INPUT
};
class MultiTouchInput;
class PanGestureInput;
class PinchGestureInput;
class TapGestureInput;
@ -56,6 +58,7 @@ public:
Modifiers modifiers;
INPUTDATA_AS_CHILD_TYPE(MultiTouchInput, MULTITOUCH_INPUT)
INPUTDATA_AS_CHILD_TYPE(PanGestureInput, PANGESTURE_INPUT)
INPUTDATA_AS_CHILD_TYPE(PinchGestureInput, PINCHGESTURE_INPUT)
INPUTDATA_AS_CHILD_TYPE(TapGestureInput, TAPGESTURE_INPUT)
@ -184,6 +187,79 @@ public:
nsTArray<SingleTouchData> mTouches;
};
/**
* Encapsulation class for pan events, can be used off-main-thread.
* These events are currently only used for scrolling on desktop.
*/
class PanGestureInput : public InputData
{
public:
enum PanGestureType
{
// MayStart: Dispatched before any actual panning has occurred but when a
// pan gesture is probably about to start, for example when the user
// starts touching the touchpad. Should interrupt any ongoing APZ
// animation and can be used to trigger scrollability indicators (e.g.
// flashing overlay scrollbars).
PANGESTURE_MAYSTART,
// Cancelled: Dispatched after MayStart when no pan gesture is going to
// happen after all, for example when the user lifts their fingers from a
// touchpad without having done any scrolling.
PANGESTURE_CANCELLED,
// Start: A pan gesture is starting.
// For devices that do not support the MayStart event type, this event can
// be used to interrupt ongoing APZ animations.
PANGESTURE_START,
// Pan: The actual pan motion by mPanDisplacement.
PANGESTURE_PAN,
// End: The pan gesture has ended, for example because the user has lifted
// their fingers from a touchpad after scrolling.
// Any potential momentum events fire after this event.
PANGESTURE_END,
// The following momentum event types are used in order to control the pan
// momentum animation. Using these instead of our own animation ensures
// that the animation curve is OS native and that the animation stops
// reliably if it is cancelled by the user.
// MomentumStart: Dispatched between the End event of the actual
// user-controlled pan, and the first MomentumPan event of the momentum
// animation.
PANGESTURE_MOMENTUMSTART,
// MomentumPan: The actual momentum motion by mPanDisplacement.
PANGESTURE_MOMENTUMPAN,
// MomentumEnd: The momentum animation has ended, for example because the
// momentum velocity has gone below the stopping threshold, or because the
// user has stopped the animation by putting their fingers on a touchpad.
PANGESTURE_MOMENTUMEND
};
PanGestureInput(PanGestureType aType,
uint32_t aTime,
TimeStamp aTimeStamp,
const ScreenPoint& aPanStartPoint,
const ScreenPoint& aPanDisplacement,
Modifiers aModifiers)
: InputData(PANGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
mType(aType),
mPanStartPoint(aPanStartPoint),
mPanDisplacement(aPanDisplacement)
{
}
PanGestureType mType;
ScreenPoint mPanStartPoint;
// Only non-zero if mType is PANGESTURE_PAN or PANGESTURE_MOMENTUMPAN.
ScreenPoint mPanDisplacement;
};
/**
* Encapsulation class for pinch events. In general, these will be generated by
* a gesture listener by looking at SingleTouchData/MultiTouchInput instances and