mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 965871 - Implement overscroll handoff for flings. r=kats
This commit is contained in:
parent
17c73ca346
commit
ff7c37b52e
@ -403,7 +403,7 @@ APZCTreeManager::ReceiveInputEvent(const InputData& aEvent,
|
||||
// then null it out so we don't keep a dangling reference and leak things.
|
||||
if (mTouchCount == 0) {
|
||||
mApzcForInputBlock = nullptr;
|
||||
mOverscrollHandoffChain.clear();
|
||||
ClearOverscrollHandoffChain();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -517,7 +517,7 @@ APZCTreeManager::ProcessTouchEvent(WidgetTouchEvent& aEvent,
|
||||
}
|
||||
if (mTouchCount == 0) {
|
||||
mApzcForInputBlock = nullptr;
|
||||
mOverscrollHandoffChain.clear();
|
||||
ClearOverscrollHandoffChain();
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -677,18 +677,58 @@ APZCTreeManager::ClearTree()
|
||||
mRootApzc = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a displacement from the screen coordinates of a source APZC to
|
||||
* the screen coordinates of a target APZC.
|
||||
* @param aTreeManager the tree manager for the APZC tree containing |aSource|
|
||||
* and |aTarget|
|
||||
* @param aSource the source APZC
|
||||
* @param aTarget the target APZC
|
||||
* @param aStartPoint the start point of the displacement
|
||||
* @param aEndPoint the end point of the displacement
|
||||
*/
|
||||
static void
|
||||
TransformDisplacement(APZCTreeManager* aTreeManager,
|
||||
AsyncPanZoomController* aSource,
|
||||
AsyncPanZoomController* aTarget,
|
||||
ScreenPoint& aStartPoint,
|
||||
ScreenPoint& aEndPoint) {
|
||||
gfx3DMatrix transformToApzc;
|
||||
gfx3DMatrix transformToGecko; // ignored
|
||||
|
||||
// Convert start and end points to untransformed screen coordinates.
|
||||
aTreeManager->GetInputTransforms(aSource, transformToApzc, transformToGecko);
|
||||
ApplyTransform(&aStartPoint, transformToApzc.Inverse());
|
||||
ApplyTransform(&aEndPoint, transformToApzc.Inverse());
|
||||
|
||||
// Convert start and end points to aTarget's transformed screen coordinates.
|
||||
aTreeManager->GetInputTransforms(aTarget, transformToApzc, transformToGecko);
|
||||
ApplyTransform(&aStartPoint, transformToApzc);
|
||||
ApplyTransform(&aEndPoint, transformToApzc);
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStartPoint, ScreenPoint aEndPoint,
|
||||
uint32_t aOverscrollHandoffChainIndex)
|
||||
{
|
||||
// If we have reached the end of the overscroll handoff chain, there is
|
||||
// nothing more to scroll, so we ignore the rest of the pan gesture.
|
||||
if (aOverscrollHandoffChainIndex >= mOverscrollHandoffChain.length()) {
|
||||
// Nothing more to scroll - ignore the rest of the pan gesture.
|
||||
return;
|
||||
nsRefPtr<AsyncPanZoomController> next;
|
||||
{
|
||||
// Grab tree lock to protect mOverscrollHandoffChain from concurrent
|
||||
// access from the input and compositor threads.
|
||||
// Release it before calling TransformDisplacement() as that grabs the
|
||||
// lock itself.
|
||||
MonitorAutoLock lock(mTreeLock);
|
||||
|
||||
// If we have reached the end of the overscroll handoff chain, there is
|
||||
// nothing more to scroll, so we ignore the rest of the pan gesture.
|
||||
if (aOverscrollHandoffChainIndex >= mOverscrollHandoffChain.length()) {
|
||||
// Nothing more to scroll - ignore the rest of the pan gesture.
|
||||
return;
|
||||
}
|
||||
|
||||
next = mOverscrollHandoffChain[aOverscrollHandoffChainIndex];
|
||||
}
|
||||
|
||||
nsRefPtr<AsyncPanZoomController> next = mOverscrollHandoffChain[aOverscrollHandoffChainIndex];
|
||||
if (next == nullptr)
|
||||
return;
|
||||
|
||||
@ -698,18 +738,7 @@ APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStar
|
||||
// scroll grabbing to grab the scroll from it), don't bother doing the
|
||||
// transformations in that case.
|
||||
if (next != aPrev) {
|
||||
gfx3DMatrix transformToApzc;
|
||||
gfx3DMatrix transformToGecko; // ignored
|
||||
|
||||
// Convert start and end points to untransformed screen coordinates.
|
||||
GetInputTransforms(aPrev, transformToApzc, transformToGecko);
|
||||
ApplyTransform(&aStartPoint, transformToApzc.Inverse());
|
||||
ApplyTransform(&aEndPoint, transformToApzc.Inverse());
|
||||
|
||||
// Convert start and end points to next's transformed screen coordinates.
|
||||
GetInputTransforms(next, transformToApzc, transformToGecko);
|
||||
ApplyTransform(&aStartPoint, transformToApzc);
|
||||
ApplyTransform(&aEndPoint, transformToApzc);
|
||||
TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint);
|
||||
}
|
||||
|
||||
// Scroll |next|. If this causes overscroll, it will call DispatchScroll()
|
||||
@ -717,9 +746,69 @@ APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStar
|
||||
next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffChainIndex);
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVelocity)
|
||||
{
|
||||
// Build the overscroll handoff chain. This is necessary because it is
|
||||
// otherwise built on touch-start and cleared on touch-end, and a fling
|
||||
// happens after touch-end. Note that, unlike DispatchScroll() which is
|
||||
// called on every touch-move during overscroll panning,
|
||||
// HandleFlingOverscroll() is only called once during a fling handoff,
|
||||
// so it's not worth trying to avoid building the handoff chain here.
|
||||
BuildOverscrollHandoffChain(aPrev);
|
||||
|
||||
nsRefPtr<AsyncPanZoomController> next; // will be used outside monitor block
|
||||
{
|
||||
// Grab tree lock to protect mOverscrollHandoffChain from concurrent
|
||||
// access from the input and compositor threads.
|
||||
// Release it before calling GetInputTransforms() as that grabs the
|
||||
// lock itself.
|
||||
MonitorAutoLock lock(mTreeLock);
|
||||
|
||||
// Find |aPrev| in the handoff chain.
|
||||
uint32_t i;
|
||||
for (i = 0; i < mOverscrollHandoffChain.length(); ++i) {
|
||||
if (mOverscrollHandoffChain[i] == aPrev) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next APZC in the handoff chain, if any.
|
||||
if (i + 1 < mOverscrollHandoffChain.length()) {
|
||||
next = mOverscrollHandoffChain[i + 1];
|
||||
}
|
||||
|
||||
// Clear the handoff chain so we don't maintain references to APZCs
|
||||
// unnecessarily.
|
||||
mOverscrollHandoffChain.clear();
|
||||
}
|
||||
|
||||
// Nothing to hand off fling to.
|
||||
if (next == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The fling's velocity needs to be transformed from the screen coordinates
|
||||
// of |aPrev| to the screen coordinates of |next|. To transform a velocity
|
||||
// correctly, we need to convert it to a displacement. For now, we do this
|
||||
// by anchoring it to a start point of (0, 0).
|
||||
// TODO: For this to be correct in the presence of 3D transforms, we should
|
||||
// use the end point of the touch that started the fling as the start point
|
||||
// rather than (0, 0).
|
||||
ScreenPoint startPoint; // (0, 0)
|
||||
ScreenPoint endPoint = startPoint + aVelocity;
|
||||
TransformDisplacement(this, aPrev, next, startPoint, endPoint);
|
||||
ScreenPoint transformedVelocity = endPoint - startPoint;
|
||||
|
||||
// Tell |next| to start a fling with the transformed velocity.
|
||||
next->TakeOverFling(transformedVelocity);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
APZCTreeManager::FlushRepaintsForOverscrollHandoffChain()
|
||||
{
|
||||
MonitorAutoLock lock(mTreeLock); // to access mOverscrollHandoffChain
|
||||
if (mOverscrollHandoffChain.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
@ -799,6 +888,10 @@ APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomControll
|
||||
// handoff order can be different, so we build a chain of APZCs in the
|
||||
// order in which scroll will be handed off to them.
|
||||
|
||||
// Grab tree lock to protect mOverscrollHandoffChain from concurrent
|
||||
// access between the input and compositor threads.
|
||||
MonitorAutoLock lock(mTreeLock);
|
||||
|
||||
mOverscrollHandoffChain.clear();
|
||||
|
||||
// Start with the child -> parent chain.
|
||||
@ -822,6 +915,13 @@ APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomControll
|
||||
CompareByScrollPriority());
|
||||
}
|
||||
|
||||
void
|
||||
APZCTreeManager::ClearOverscrollHandoffChain()
|
||||
{
|
||||
MonitorAutoLock lock(mTreeLock);
|
||||
mOverscrollHandoffChain.clear();
|
||||
}
|
||||
|
||||
AsyncPanZoomController*
|
||||
APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
|
@ -255,10 +255,22 @@ public:
|
||||
* - TM.DispatchScroll() calls A.AttemptScroll() (since A is at index 2 in the chain)
|
||||
* - A.AttemptScroll() scrolls A. If there is overscroll, it calls TM.DispatchScroll() with index = 3.
|
||||
* - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain.
|
||||
*
|
||||
* Note: this should be used for panning only. For handing off overscroll for
|
||||
* a fling, use HandOffFling().
|
||||
*/
|
||||
void DispatchScroll(AsyncPanZoomController* aAPZC, ScreenPoint aStartPoint, ScreenPoint aEndPoint,
|
||||
uint32_t aOverscrollHandoffChainIndex);
|
||||
|
||||
/**
|
||||
* This is a callback for AsyncPanZoomController to call when it wants to
|
||||
* hand off overscroll from a fling.
|
||||
* @param aApzc the APZC that is handing off the fling
|
||||
* @param aVelocity the current velocity of the fling, in |aApzc|'s screen
|
||||
* pixels per millisecond
|
||||
*/
|
||||
void HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity);
|
||||
|
||||
bool FlushRepaintsForOverscrollHandoffChain();
|
||||
|
||||
protected:
|
||||
@ -295,6 +307,7 @@ private:
|
||||
nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent, ScrollableLayerGuid* aOutTargetGuid);
|
||||
void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
|
||||
const ZoomConstraints& aConstraints);
|
||||
void ClearOverscrollHandoffChain();
|
||||
|
||||
/**
|
||||
* Recursive helper function to build the APZC tree. The tree of APZC instances has
|
||||
@ -320,6 +333,7 @@ private:
|
||||
* isolation (that is, if its tree pointers are not being accessed or mutated). The
|
||||
* lock also needs to be held when accessing the mRootApzc instance variable, as that
|
||||
* is considered part of the APZC tree management state.
|
||||
* Finally, the lock needs to be held when accessing mOverscrollHandoffChain.
|
||||
* IMPORTANT: See the note about lock ordering at the top of this file. */
|
||||
mozilla::Monitor mTreeLock;
|
||||
nsRefPtr<AsyncPanZoomController> mRootApzc;
|
||||
|
@ -346,10 +346,9 @@ GetFrameTime() {
|
||||
|
||||
class FlingAnimation: public AsyncPanZoomAnimation {
|
||||
public:
|
||||
FlingAnimation(AxisX& aX, AxisY& aY)
|
||||
FlingAnimation(AsyncPanZoomController& aApzc)
|
||||
: AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gFlingRepaintInterval))
|
||||
, mX(aX)
|
||||
, mY(aY)
|
||||
, mApzc(aApzc)
|
||||
{}
|
||||
/**
|
||||
* Advances a fling by an interpolated amount based on the passed in |aDelta|.
|
||||
@ -361,8 +360,7 @@ public:
|
||||
const TimeDuration& aDelta);
|
||||
|
||||
private:
|
||||
AxisX& mX;
|
||||
AxisY& mY;
|
||||
AsyncPanZoomController& mApzc;
|
||||
};
|
||||
|
||||
class ZoomAnimation: public AsyncPanZoomAnimation {
|
||||
@ -772,7 +770,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
||||
mX.EndTouch();
|
||||
mY.EndTouch();
|
||||
SetState(FLING);
|
||||
StartAnimation(new FlingAnimation(mX, mY));
|
||||
StartAnimation(new FlingAnimation(*this));
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
|
||||
case PINCHING:
|
||||
@ -1189,6 +1187,15 @@ void AsyncPanZoomController::AttemptScroll(const ScreenPoint& aStartPoint,
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
|
||||
// We may have a pre-existing velocity for whatever reason (for example,
|
||||
// a previously handed off fling). We don't want to clobber that.
|
||||
mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
|
||||
mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
|
||||
SetState(FLING);
|
||||
StartAnimation(new FlingAnimation(*this));
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
|
||||
uint32_t aOverscrollHandoffChainIndex) {
|
||||
// Make a local copy of the tree manager pointer and check if it's not
|
||||
@ -1248,25 +1255,77 @@ ScreenIntPoint& AsyncPanZoomController::GetFirstTouchScreenPoint(const MultiTouc
|
||||
|
||||
bool FlingAnimation::Sample(FrameMetrics& aFrameMetrics,
|
||||
const TimeDuration& aDelta) {
|
||||
bool shouldContinueFlingX = mX.FlingApplyFrictionOrCancel(aDelta),
|
||||
shouldContinueFlingY = mY.FlingApplyFrictionOrCancel(aDelta);
|
||||
|
||||
// If the fling is handed off to our APZC from a child, on the first call to
|
||||
// Sample() aDelta might be negative because it's computed as the sample time
|
||||
// from SampleContentTransformForFrame() minus our APZC's mLastSampleTime
|
||||
// which is the time the child handed off the fling from its call to
|
||||
// SampleContentTransformForFrame() with the same sample time. If we allow
|
||||
// the negative aDelta to be processed, it will yield a displacement in the
|
||||
// direction opposite to the fling, which can cause us to overscroll and
|
||||
// hand off the fling to _our_ parent, which effectively kills the fling.
|
||||
if (aDelta.ToMilliseconds() <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shouldContinueFlingX = mApzc.mX.FlingApplyFrictionOrCancel(aDelta),
|
||||
shouldContinueFlingY = mApzc.mY.FlingApplyFrictionOrCancel(aDelta);
|
||||
// If we shouldn't continue the fling, let's just stop and repaint.
|
||||
if (!shouldContinueFlingX && !shouldContinueFlingY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CSSPoint overscroll; // overscroll is ignored for flings
|
||||
ScreenPoint offset(aDelta.ToMilliseconds() * mX.GetVelocity(),
|
||||
aDelta.ToMilliseconds() * mY.GetVelocity());
|
||||
// AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
|
||||
// Since we need to hand off the velocity to the tree manager in such a case,
|
||||
// we save it here. Would be ScreenVector instead of ScreenPoint if we had
|
||||
// vector classes.
|
||||
ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
|
||||
|
||||
ScreenPoint offset = velocity * aDelta.ToMilliseconds();
|
||||
|
||||
// Inversely scale the offset by the resolution (when you're zoomed further in,
|
||||
// the same swipe should move you a shorter distance).
|
||||
CSSPoint cssOffset = offset / aFrameMetrics.mZoom;
|
||||
CSSPoint overscroll;
|
||||
aFrameMetrics.mScrollOffset += CSSPoint(
|
||||
mX.AdjustDisplacement(cssOffset.x, overscroll.x),
|
||||
mY.AdjustDisplacement(cssOffset.y, overscroll.y)
|
||||
mApzc.mX.AdjustDisplacement(cssOffset.x, overscroll.x),
|
||||
mApzc.mY.AdjustDisplacement(cssOffset.y, overscroll.y)
|
||||
);
|
||||
|
||||
// If the fling has caused us to reach the end of our scroll range, hand
|
||||
// off the fling to the next APZC in the overscroll handoff chain.
|
||||
if (!IsZero(overscroll)) {
|
||||
// We may have reached the end of the scroll range along one axis but
|
||||
// not the other. In such a case we only want to hand off the relevant
|
||||
// component of the fling.
|
||||
if (FuzzyEqualsMultiplicative(overscroll.x, 0.0f)) {
|
||||
velocity.x = 0;
|
||||
} else if (FuzzyEqualsMultiplicative(overscroll.y, 0.0f)) {
|
||||
velocity.y = 0;
|
||||
}
|
||||
|
||||
// To hand off the fling, we call APZCTreeManager::HandleFlingOverscroll()
|
||||
// which starts a new fling in the next APZC in the handoff chain with
|
||||
// the same velocity. For simplicity, the actual overscroll of the current
|
||||
// sample is discarded rather than being handed off. The compositor should
|
||||
// sample animations sufficiently frequently that this is not noticeable.
|
||||
|
||||
// Make a local copy of the tree manager pointer and check if it's not
|
||||
// null before calling HandleFlingOverscroll(). This is necessary because
|
||||
// Destroy(), which nulls out mTreeManager, could be called concurrently.
|
||||
APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
|
||||
if (treeManagerLocal) {
|
||||
// APZC is holding mMonitor, so directly calling HandleFlingOverscroll()
|
||||
// (which acquires the tree lock) would violate the lock ordering. Instead
|
||||
// we schedule HandleFlingOverscroll() to be called after mMonitor is
|
||||
// released.
|
||||
mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
|
||||
&APZCTreeManager::HandOffFling,
|
||||
&mApzc,
|
||||
velocity));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ class PCompositorParent;
|
||||
class ViewTransform;
|
||||
class APZCTreeManager;
|
||||
class AsyncPanZoomAnimation;
|
||||
class FlingAnimation;
|
||||
|
||||
/**
|
||||
* Controller for all panning and zooming logic. Any time a user input is
|
||||
@ -301,6 +302,12 @@ public:
|
||||
void AttemptScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
|
||||
uint32_t aOverscrollHandoffChainIndex = 0);
|
||||
|
||||
/**
|
||||
* Take over a fling with the given velocity from another APZC. Used for
|
||||
* during overscroll handoff for a fling.
|
||||
*/
|
||||
void TakeOverFling(ScreenPoint aVelocity);
|
||||
|
||||
/**
|
||||
* Returns allowed touch behavior for the given point on the scrollable layer.
|
||||
* Internally performs a kind of hit testing based on the regions constructed
|
||||
@ -748,6 +755,7 @@ private:
|
||||
RefPtr<AsyncPanZoomAnimation> mAnimation;
|
||||
|
||||
friend class Axis;
|
||||
friend class FlingAnimation;
|
||||
|
||||
/* The functions and members in this section are used to build a tree
|
||||
* structure out of APZC instances. This tree can only be walked or
|
||||
|
@ -254,6 +254,10 @@ float Axis::GetVelocity() {
|
||||
return mAxisLocked ? 0 : mVelocity;
|
||||
}
|
||||
|
||||
void Axis::SetVelocity(float aVelocity) {
|
||||
mVelocity = aVelocity;
|
||||
}
|
||||
|
||||
float Axis::GetCompositionEnd() {
|
||||
return GetOrigin() + GetCompositionLength();
|
||||
}
|
||||
|
@ -128,6 +128,16 @@ public:
|
||||
*/
|
||||
float GetVelocity();
|
||||
|
||||
/**
|
||||
* Sets the raw velocity of this axis at this moment.
|
||||
* Intended to be called only when the axis "takes over" a velocity from
|
||||
* another APZC, in which case there are no touch points available to call
|
||||
* UpdateWithTouchAtDevicePoint. In other circumstances,
|
||||
* UpdateWithTouchAtDevicePoint should be used and the velocity calculated
|
||||
* there.
|
||||
*/
|
||||
void SetVelocity(float aVelocity);
|
||||
|
||||
/**
|
||||
* Gets the overscroll state of the axis given an additional displacement.
|
||||
* That is to say, if the given displacement is applied, this will tell you
|
||||
|
Loading…
Reference in New Issue
Block a user