1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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/. */
#ifndef mozilla_layers_Axis_h
#define mozilla_layers_Axis_h
#include <sys/types.h> // for int32_t
#include "APZUtils.h"
#include "AxisPhysicsMSDModel.h"
#include "mozilla/DataMutex.h" // for DataMutex
#include "mozilla/gfx/Types.h" // for Side
#include "mozilla/TimeStamp.h" // for TimeDuration
#include "nsTArray.h" // for nsTArray
#include "Units.h"
namespace mozilla {
namespace layers {
const float EPSILON = 0.0001f;
/**
* Compare two coordinates for equality, accounting for rounding error.
* Use both FuzzyEqualsAdditive() with COORDINATE_EPISLON, which accounts for
* things like the error introduced by rounding during a round-trip to app
* units, and FuzzyEqualsMultiplicative(), which accounts for accumulated error
* due to floating-point operations (which can be larger than COORDINATE_EPISLON
* for sufficiently large coordinate values).
*/
bool FuzzyEqualsCoordinate(float aValue1, float aValue2);
struct FrameMetrics;
class AsyncPanZoomController;
/**
* Interface for computing velocities along the axis based on
* position samples.
*/
class VelocityTracker {
public:
virtual ~VelocityTracker() = default;
/**
* Start tracking velocity along this axis, starting with the given
* initial position and corresponding timestamp.
*/
virtual void StartTracking(ParentLayerCoord aPos, TimeStamp aTimestamp) = 0;
/**
* Record a new position along this axis, at the given timestamp.
* Returns the average velocity between the last sample and this one, or
* or Nothing() if a reasonable average cannot be computed.
*/
virtual Maybe<float> AddPosition(ParentLayerCoord aPos,
TimeStamp aTimestamp) = 0;
/**
* Compute an estimate of the axis's current velocity, based on recent
* position samples. It's up to implementation how many samples to consider
* and how to perform the computation.
* If the tracker doesn't have enough samples to compute a result, it
* may return Nothing{}.
*/
virtual Maybe<float> ComputeVelocity(TimeStamp aTimestamp) = 0;
/**
* Clear all state in the velocity tracker.
*/
virtual void Clear() = 0;
};
/**
* Helper class to maintain each axis of movement (X,Y) for panning and zooming.
* Note that everything here is specific to one axis; that is, the X axis knows
* nothing about the Y axis and vice versa.
*/
class Axis {
public:
explicit Axis(AsyncPanZoomController* aAsyncPanZoomController);
/**
* Notify this Axis that a new touch has been received, including a timestamp
* for when the touch was received. This triggers a recalculation of velocity.
* This can also used for pan gesture events. For those events, |aPos| is
* an invented position corresponding to the mouse position plus any
* accumulated displacements over the course of the pan gesture.
*/
void UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos,
TimeStamp aTimestamp);
public:
/**
* Notify this Axis that a touch has begun, i.e. the user has put their finger
* on the screen but has not yet tried to pan.
*/
void StartTouch(ParentLayerCoord aPos, TimeStamp aTimestamp);
/**
* Notify this Axis that a touch has ended gracefully. This may perform
* recalculations of the axis velocity.
*/
void EndTouch(TimeStamp aTimestamp);
/**
* Notify this Axis that the gesture has ended forcefully. Useful for stopping
* flings when a user puts their finger down in the middle of one (i.e. to
* stop a previous touch including its fling so that a new one can take its
* place).
*/
void CancelGesture();
/**
* Takes a requested displacement to the position of this axis, and adjusts it
* to account for 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 |aDisplacementOut| parameter is set to the adjusted displacement, and
* the function returns true if and only if internal overscroll amounts were
* changed.
*/
bool AdjustDisplacement(ParentLayerCoord aDisplacement,
/* ParentLayerCoord */ float& aDisplacementOut,
/* ParentLayerCoord */ float& aOverscrollAmountOut,
bool aForceOverscroll = false);
/**
* Overscrolls this axis by the requested amount in the requested direction.
* The axis must be at the end of its scroll range in this direction.
*/
void OverscrollBy(ParentLayerCoord aOverscroll);
/**
* Return the amount of overscroll on this axis, in ParentLayer pixels.
*
* If this amount is nonzero, the relevant component of
* mAsyncPanZoomController->Metrics().mScrollOffset must be at its
* extreme allowed value in the relevant direction (that is, it must be at
* its maximum value if we are overscrolled at our composition length, and
* at its minimum value if we are overscrolled at the origin).
*/
ParentLayerCoord GetOverscroll() const;
/**
* Start an overscroll animation with the given initial velocity.
*/
void StartOverscrollAnimation(float aVelocity);
/**
* Sample the snap-back animation to relieve overscroll.
* |aDelta| is the time since the last sample.
*/
bool SampleOverscrollAnimation(const TimeDuration& aDelta);
/**
* Stop an overscroll animation.
*/
void EndOverscrollAnimation();
/**
* Return whether this axis is overscrolled in either direction.
*/
bool IsOverscrolled() const;
/**
* Clear any overscroll amount on this axis.
*/
void ClearOverscroll();
/**
* Gets the starting position of the touch supplied in StartTouch().
*/
ParentLayerCoord PanStart() const;
/**
* Gets the distance between the starting position of the touch supplied in
* StartTouch() and the current touch from the last
* UpdateWithTouchAtDevicePoint().
*/
ParentLayerCoord PanDistance() const;
/**
* Gets the distance between the starting position of the touch supplied in
* StartTouch() and the supplied position.
*/
ParentLayerCoord PanDistance(ParentLayerCoord aPos) const;
/**
* Returns true if the page has room to be scrolled along this axis.
*/
bool CanScroll() const;
/**
* Returns whether this axis can scroll any more in a particular direction.
*/
bool CanScroll(ParentLayerCoord aDelta) const;
/**
* Returns true if the page has room to be scrolled along this axis
* and this axis is not scroll-locked.
*/
bool CanScrollNow() const;
/**
* Clamp a point to the page's scrollable bounds. That is, a scroll
* destination to the returned point will not contain any overscroll.
*/
CSSCoord ClampOriginToScrollableRect(CSSCoord aOrigin) const;
void SetAxisLocked(bool aAxisLocked) { mAxisLocked = aAxisLocked; }
/**
* Gets the raw velocity of this axis at this moment.
*/
float GetVelocity() const;
/**
* 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);
/**
* If a displacement will overscroll the axis, this returns the amount and in
* what direction.
*/
ParentLayerCoord DisplacementWillOverscrollAmount(
ParentLayerCoord aDisplacement) const;
/**
* If a scale will overscroll the axis, this returns the amount and in what
* direction.
*
* |aFocus| is the point at which the scale is focused at. We will offset the
* scroll offset in such a way that it remains in the same place on the page
* relative.
*
* Note: Unlike most other functions in Axis, this functions operates in
* CSS coordinates so there is no confusion as to whether the
* ParentLayer coordinates it operates in are before or after the scale
* is applied.
*/
CSSCoord ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const;
/**
* Checks if an axis will overscroll in both directions by computing the
* content rect and checking that its height/width (depending on the axis)
* does not overextend past the viewport.
*
* This gets called by ScaleWillOverscroll().
*/
bool ScaleWillOverscrollBothSides(float aScale) const;
/**
* Returns true if movement on this axis is locked.
*/
bool IsAxisLocked() const;
ParentLayerCoord GetOrigin() const;
ParentLayerCoord GetCompositionLength() const;
ParentLayerCoord GetPageStart() const;
ParentLayerCoord GetPageLength() const;
ParentLayerCoord GetCompositionEnd() const;
ParentLayerCoord GetPageEnd() const;
ParentLayerCoord GetScrollRangeEnd() const;
ParentLayerCoord GetPos() const { return mPos; }
bool OverscrollBehaviorAllowsHandoff() const;
bool OverscrollBehaviorAllowsOverscrollEffect() const;
virtual ParentLayerCoord GetPointOffset(
const ParentLayerPoint& aPoint) const = 0;
virtual ParentLayerCoord GetRectLength(
const ParentLayerRect& aRect) const = 0;
virtual ParentLayerCoord GetRectOffset(
const ParentLayerRect& aRect) const = 0;
virtual CSSToParentLayerScale GetScaleForAxis(
const CSSToParentLayerScale2D& aScale) const = 0;
virtual ScreenPoint MakePoint(ScreenCoord aCoord) const = 0;
const void* OpaqueApzcPointer() const { return mAsyncPanZoomController; }
virtual const char* Name() const = 0;
// Convert a velocity from global inches/ms into ParentLayerCoords/ms.
float ToLocalVelocity(float aVelocityInchesPerMs) const;
protected:
// A position along the axis, used during input event processing to
// track velocities (and for touch gestures, to track the length of
// the gesture). For touch events, this represents the position of
// the finger (or in the case of two-finger scrolling, the midpoint
// of the two fingers). For pan gesture events, this represents an
// invented position corresponding to the mouse position at the start
// of the pan, plus deltas representing the displacement of the pan.
ParentLayerCoord mPos;
ParentLayerCoord mStartPos;
// The velocity can be accessed from multiple threads (e.g. APZ
// controller thread and APZ sampler thread), so needs to be
// protected by a mutex.
// Units: ParentLayerCoords per millisecond
mutable DataMutex<float> mVelocity;
bool mAxisLocked; // Whether movement on this axis is locked.
AsyncPanZoomController* mAsyncPanZoomController;
// The amount by which we are overscrolled; see GetOverscroll().
ParentLayerCoord mOverscroll;
// The mass-spring-damper model for overscroll physics.
AxisPhysicsMSDModel mMSDModel;
// Used to track velocity over a series of input events and compute
// a resulting velocity to use for e.g. starting a fling animation.
// This member can only be accessed on the controller/UI thread.
UniquePtr<VelocityTracker> mVelocityTracker;
float DoGetVelocity() const;
void DoSetVelocity(float aVelocity);
const FrameMetrics& GetFrameMetrics() const;
const ScrollMetadata& GetScrollMetadata() const;
virtual OverscrollBehavior GetOverscrollBehavior() const = 0;
// Adjust a requested overscroll amount for resistance, yielding a smaller
// actual overscroll amount.
ParentLayerCoord ApplyResistance(ParentLayerCoord aOverscroll) const;
// Helper function for SampleOverscrollAnimation().
void StepOverscrollAnimation(double aStepDurationMilliseconds);
};
class AxisX : public Axis {
public:
explicit AxisX(AsyncPanZoomController* mAsyncPanZoomController);
ParentLayerCoord GetPointOffset(
const ParentLayerPoint& aPoint) const override;
ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
CSSToParentLayerScale GetScaleForAxis(
const CSSToParentLayerScale2D& aScale) const override;
ScreenPoint MakePoint(ScreenCoord aCoord) const override;
const char* Name() const override;
bool CanScrollTo(Side aSide) const;
private:
OverscrollBehavior GetOverscrollBehavior() const override;
};
class AxisY : public Axis {
public:
explicit AxisY(AsyncPanZoomController* mAsyncPanZoomController);
ParentLayerCoord GetPointOffset(
const ParentLayerPoint& aPoint) const override;
ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
CSSToParentLayerScale GetScaleForAxis(
const CSSToParentLayerScale2D& aScale) const override;
ScreenPoint MakePoint(ScreenCoord aCoord) const override;
const char* Name() const override;
bool CanScrollTo(Side aSide) const;
bool CanScrollDownwardsWithDynamicToolbar() const;
private:
OverscrollBehavior GetOverscrollBehavior() const override;
ParentLayerCoord GetCompositionLengthWithoutDynamicToolbar() const;
};
} // namespace layers
} // namespace mozilla
#endif
|