Bug 1249040 - Allow wheel scrolls to accumulate in the presence of scroll snapping. r=kats
MozReview-Commit-ID: EUyGvkoyu8I
--- a/gfx/layers/AxisPhysicsMSDModel.cpp
+++ b/gfx/layers/AxisPhysicsMSDModel.cpp
@@ -53,17 +53,17 @@ AxisPhysicsMSDModel::Acceleration(const
double spring_force = (mDestination - aState.p) * mSpringConstant;
double damp_force = -aState.v * mDampingRatio * mSpringConstantSqrtXTwo;
return spring_force + damp_force;
}
double
-AxisPhysicsMSDModel::GetDestination()
+AxisPhysicsMSDModel::GetDestination() const
{
return mDestination;
}
void
AxisPhysicsMSDModel::SetDestination(double aDestination)
{
mDestination = aDestination;
--- a/gfx/layers/AxisPhysicsMSDModel.h
+++ b/gfx/layers/AxisPhysicsMSDModel.h
@@ -22,17 +22,17 @@ public:
double aInitialVelocity, double aSpringConstant,
double aDampingRatio);
~AxisPhysicsMSDModel();
/**
* Gets the raw destination of this axis at this moment.
*/
- double GetDestination();
+ double GetDestination() const;
/**
* Sets the raw destination of this axis at this moment.
*/
void SetDestination(double aDestination);
/**
* Returns true when the position is close to the destination and the
--- a/gfx/layers/apz/src/AsyncPanZoomAnimation.h
+++ b/gfx/layers/apz/src/AsyncPanZoomAnimation.h
@@ -13,16 +13,17 @@
#include "mozilla/Vector.h"
#include "FrameMetrics.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace layers {
class WheelScrollAnimation;
+class SmoothScrollAnimation;
class AsyncPanZoomAnimation {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomAnimation)
public:
explicit AsyncPanZoomAnimation()
{ }
@@ -47,16 +48,19 @@ public:
*/
Vector<Task*> TakeDeferredTasks() {
return Move(mDeferredTasks);
}
virtual WheelScrollAnimation* AsWheelScrollAnimation() {
return nullptr;
}
+ virtual SmoothScrollAnimation* AsSmoothScrollAnimation() {
+ return nullptr;
+ }
virtual bool WantsRepaints() {
return true;
}
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~AsyncPanZoomAnimation()
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -727,17 +727,17 @@ public:
}
/**
* Advances a smooth scroll simulation based on the time passed in |aDelta|.
* This should be called whenever sampling the content transform for this
* frame. Returns true if the smooth scroll should be advanced by one frame,
* or false if the smooth scroll has ended.
*/
- bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) {
+ bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override {
nsPoint oneParentLayerPixel =
CSSPoint::ToAppUnits(ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom());
if (mXAxisModel.IsFinished(oneParentLayerPixel.x) &&
mYAxisModel.IsFinished(oneParentLayerPixel.y)) {
// Set the scroll offset to the exact destination. If we allow the scroll
// offset to end up being a bit off from the destination, we can get
// artefacts like "scroll to the next snap point in this direction"
// scrolling to the snap point we're already supposed to be at.
@@ -824,16 +824,25 @@ public:
return true;
}
void SetDestination(const nsPoint& aNewDestination) {
mXAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.x));
mYAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.y));
}
+ CSSPoint GetDestination() const {
+ return CSSPoint::FromAppUnits(
+ nsPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()));
+ }
+
+ SmoothScrollAnimation* AsSmoothScrollAnimation() override {
+ return this;
+ }
+
private:
AsyncPanZoomController& mApzc;
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
};
/*static*/ void
AsyncPanZoomController::InitializeGlobalState()
{
@@ -1802,23 +1811,26 @@ nsEventStatus AsyncPanZoomController::On
if (delta.x == 0 && delta.y == 0) {
// Avoid spurious state changes and unnecessary work
return nsEventStatus_eIgnore;
}
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
(uint32_t) ScrollInputMethodForWheelDeltaType(aEvent.mDeltaType));
- // Wheel events from "clicky" mouse wheels trigger scroll snapping to the
- // next snap point. Check for this, and adjust the delta to take into
- // account the snap point.
- bool scrollSnapping = MaybeAdjustDeltaForScrollSnapping(delta, aEvent);
switch (aEvent.mScrollMode) {
case ScrollWheelInput::SCROLLMODE_INSTANT: {
+
+ // Wheel events from "clicky" mouse wheels trigger scroll snapping to the
+ // next snap point. Check for this, and adjust the delta to take into
+ // account the snap point.
+ CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+ MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition);
+
ScreenPoint distance = ToScreenCoordinates(
ParentLayerPoint(fabs(delta.x), fabs(delta.y)), aEvent.mLocalOrigin);
CancelAnimation();
OverscrollHandoffState handoffState(
*mInputQueue->CurrentWheelBlock()->GetOverscrollHandoffChain(),
distance,
@@ -1838,43 +1850,53 @@ nsEventStatus AsyncPanZoomController::On
}
case ScrollWheelInput::SCROLLMODE_SMOOTH: {
// The lock must be held across the entire update operation, so the
// compositor doesn't end the animation before we get a chance to
// update it.
ReentrantMonitorAutoEnter lock(mMonitor);
- if (scrollSnapping) {
- // If we're scroll snapping use a smooth scroll animation to get
+ // Perform scroll snapping if appropriate.
+ CSSPoint startPosition = mFrameMetrics.GetScrollOffset();
+ // If we're already in a wheel scroll or smooth scroll animation,
+ // the delta is applied to its destination, not to the current
+ // scroll position. Take this into account when finding a snap point.
+ if (mState == WHEEL_SCROLL) {
+ startPosition = mAnimation->AsWheelScrollAnimation()->GetDestination();
+ } else if (mState == SMOOTH_SCROLL) {
+ startPosition = mAnimation->AsSmoothScrollAnimation()->GetDestination();
+ }
+ if (MaybeAdjustDeltaForScrollSnapping(aEvent, delta, startPosition)) {
+ // If we're scroll snapping, use a smooth scroll animation to get
// the desired physics. Note that SmoothScrollTo() will re-use an
// existing smooth scroll animation if there is one.
- CSSPoint snapPoint = mFrameMetrics.GetScrollOffset() + (delta / mFrameMetrics.GetZoom());
- SmoothScrollTo(snapPoint);
- } else {
- // Otherwise, use a wheel scroll animation, also reusing one if possible.
- if (mState != WHEEL_SCROLL) {
- CancelAnimation();
- SetState(WHEEL_SCROLL);
-
- nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
- StartAnimation(new WheelScrollAnimation(
- *this, initialPosition, aEvent.mDeltaType));
- }
-
- nsPoint deltaInAppUnits =
- CSSPoint::ToAppUnits(delta / mFrameMetrics.GetZoom());
- // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
- // appunits/second
- nsPoint velocity =
- CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
-
- WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation();
- animation->Update(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y));
+ SmoothScrollTo(startPosition);
+ break;
}
+
+ // Otherwise, use a wheel scroll animation, also reusing one if possible.
+ if (mState != WHEEL_SCROLL) {
+ CancelAnimation();
+ SetState(WHEEL_SCROLL);
+
+ nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
+ StartAnimation(new WheelScrollAnimation(
+ *this, initialPosition, aEvent.mDeltaType));
+ }
+
+ nsPoint deltaInAppUnits =
+ CSSPoint::ToAppUnits(delta / mFrameMetrics.GetZoom());
+ // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
+ // appunits/second
+ nsPoint velocity =
+ CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), mY.GetVelocity())) * 1000.0f;
+
+ WheelScrollAnimation* animation = mAnimation->AsWheelScrollAnimation();
+ animation->Update(aEvent.mTimeStamp, deltaInAppUnits, nsSize(velocity.x, velocity.y));
break;
}
}
return nsEventStatus_eConsumeNoDefault;
}
void
@@ -3975,33 +3997,35 @@ void AsyncPanZoomController::ScrollSnapT
(float)mFrameMetrics.GetScrollOffset().y,
(float)predictedDestination.x, (float)predictedDestination.y);
ScrollSnapNear(predictedDestination);
}
}
bool AsyncPanZoomController::MaybeAdjustDeltaForScrollSnapping(
- ParentLayerPoint& aDelta, const ScrollWheelInput& aEvent)
+ const ScrollWheelInput& aEvent,
+ ParentLayerPoint& aDelta,
+ CSSPoint& aStartPosition)
{
// Don't scroll snap for pixel scrolls. This matches the main thread
// behaviour in EventStateManager::DoScrollText().
if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_PIXEL) {
return false;
}
ReentrantMonitorAutoEnter lock(mMonitor);
- CSSPoint scrollOffset = mFrameMetrics.GetScrollOffset();
CSSToParentLayerScale2D zoom = mFrameMetrics.GetZoom();
CSSPoint destination = mFrameMetrics.CalculateScrollRange().ClampPoint(
- scrollOffset + (aDelta / zoom));
+ aStartPosition + (aDelta / zoom));
nsIScrollableFrame::ScrollUnit unit =
ScrollWheelInput::ScrollUnitForDeltaType(aEvent.mDeltaType);
if (Maybe<CSSPoint> snapPoint = FindSnapPointNear(destination, unit)) {
- aDelta = (*snapPoint - scrollOffset) * zoom;
+ aDelta = (*snapPoint - aStartPosition) * zoom;
+ aStartPosition = *snapPoint;
return true;
}
return false;
}
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -1124,19 +1124,23 @@ private:
/* ===================================================================
* The functions in this section are used for CSS scroll snapping.
*/
// If |aEvent| should trigger scroll snapping, adjust |aDelta| to reflect
// the snapping (that is, make it a delta that will take us to the desired
- // snap point). Returns true iff. the delta was so adjusted.
- bool MaybeAdjustDeltaForScrollSnapping(ParentLayerPoint& aDelta,
- const ScrollWheelInput& aEvent);
+ // snap point). The delta is interpreted as being relative to
+ // |aStartPosition|, and if a target snap point is found, |aStartPosition|
+ // is also updated, to the value of the snap point.
+ // Returns true iff. a target snap point was found.
+ bool MaybeAdjustDeltaForScrollSnapping(const ScrollWheelInput& aEvent,
+ ParentLayerPoint& aDelta,
+ CSSPoint& aStartPosition);
// Snap to a snap position nearby the current scroll position, if appropriate.
void ScrollSnap();
// Snap to a snap position nearby the destination predicted based on the
// current velocity, if appropriate.
void ScrollSnapToDestination();
--- a/gfx/layers/apz/src/WheelScrollAnimation.h
+++ b/gfx/layers/apz/src/WheelScrollAnimation.h
@@ -27,16 +27,20 @@ public:
bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
void Update(TimeStamp aTime, nsPoint aDelta, const nsSize& aCurrentVelocity);
WheelScrollAnimation* AsWheelScrollAnimation() override {
return this;
}
+ CSSPoint GetDestination() const {
+ return CSSPoint::FromAppUnits(mFinalDestination);
+ }
+
private:
void InitPreferences(TimeStamp aTime);
private:
AsyncPanZoomController& mApzc;
nsPoint mFinalDestination;
ScrollWheelInput::ScrollDeltaType mDeltaType;
};