summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/Axis.h
blob: 94195b621e39deb5a1962e5cc28db89d6e8d6cd4 (plain)
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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
/* -*- 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(CSSCoord aValue1, CSSCoord 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);

  /**
   * Helper enum class for specifying if EndTouch() should clear the axis lock.
   */
  enum class ClearAxisLock { Yes, No };

  /**
   * Notify this Axis that a touch has ended gracefully. This may perform
   * recalculations of the axis velocity.
   */
  void EndTouch(TimeStamp aTimestamp, ClearAxisLock aClearAxisLock);

  /**
   * 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& aDisplacementOut,
                          ParentLayerCoord& 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;

  /**
   * Restore the amount by which this axis is overscrolled to the specified
   * amount. This is for test-related use; overscrolling as a result of user
   * input should happen via OverscrollBy().
   */
  void RestoreOverscroll(ParentLayerCoord aOverscroll);

  /**
   * 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, |aOverscrollSideBits| is
   * the direction where the overscroll happens on this axis.
   */
  bool SampleOverscrollAnimation(const TimeDuration& aDelta,
                                 SideBits aOverscrollSideBits);

  /**
   * Stop an overscroll animation.
   */
  void EndOverscrollAnimation();

  /**
   * Return whether this axis is overscrolled in either direction.
   */
  bool IsOverscrolled() const;

  /**
   * Return true if this axis is overscrolled but its scroll offset
   * has changed in a way that makes the oversrolled state no longer
   * valid (for example, it is overscrolled at the top but the
   * scroll offset is no longer zero).
   */
  bool IsInInvalidOverscroll() const;

  /**
   * Clear any overscroll amount on this axis.
   */
  void ClearOverscroll();

  /**
   * Returns whether the overscroll animation is alive.
   */
  bool IsOverscrollAnimationAlive() const;

  /**
   * Returns whether the overscroll animation is running.
   * Note that unlike the above IsOverscrollAnimationAlive, this function
   * returns false even if the animation is still there but is very close to
   * the destination position and its velocity is quite low, i.e. it's time to
   * finish.
   */
  bool IsOverscrollAnimationRunning() const;

  /**
   * 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(CSSCoord aDelta) const;
  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;

  bool IsScrolledToStart() const;
  bool IsScrolledToEnd() const;

  ParentLayerCoord GetPos() const { return mPos; }

  bool OverscrollBehaviorAllowsHandoff() const;
  bool OverscrollBehaviorAllowsOverscrollEffect() const;

  virtual CSSToParentLayerScale GetAxisScale(
      const CSSToParentLayerScale2D& aScale) const = 0;
  virtual CSSCoord GetPointOffset(const CSSPoint& aPoint) const = 0;
  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 float GetTransformScale(
      const AsyncTransformComponentMatrix& aMatrix) const = 0;
  virtual ParentLayerCoord GetTransformTranslation(
      const AsyncTransformComponentMatrix& aMatrix) const = 0;
  virtual void PostScale(AsyncTransformComponentMatrix& aMatrix,
                         float aScale) const = 0;
  virtual void PostTranslate(AsyncTransformComponentMatrix& aMatrix,
                             ParentLayerCoord aTranslation) 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;

  // Do not use this function directly, use
  // AsyncPanZoomController::GetAllowedHandoffDirections instead.
  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);
  CSSToParentLayerScale GetAxisScale(
      const CSSToParentLayerScale2D& aScale) const override;
  CSSCoord GetPointOffset(const CSSPoint& aPoint) const override;
  ParentLayerCoord GetPointOffset(
      const ParentLayerPoint& aPoint) const override;
  ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
  ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
  float GetTransformScale(
      const AsyncTransformComponentMatrix& aMatrix) const override;
  ParentLayerCoord GetTransformTranslation(
      const AsyncTransformComponentMatrix& aMatrix) const override;
  void PostScale(AsyncTransformComponentMatrix& aMatrix,
                 float aScale) const override;
  void PostTranslate(AsyncTransformComponentMatrix& aMatrix,
                     ParentLayerCoord aTranslation) const override;
  ScreenPoint MakePoint(ScreenCoord aCoord) const override;
  const char* Name() const override;
  bool CanScrollTo(Side aSide) const;
  SideBits ScrollableDirections() const;

 private:
  OverscrollBehavior GetOverscrollBehavior() const override;
};

class AxisY : public Axis {
 public:
  explicit AxisY(AsyncPanZoomController* mAsyncPanZoomController);
  CSSCoord GetPointOffset(const CSSPoint& aPoint) const override;
  ParentLayerCoord GetPointOffset(
      const ParentLayerPoint& aPoint) const override;
  CSSToParentLayerScale GetAxisScale(
      const CSSToParentLayerScale2D& aScale) const override;
  ParentLayerCoord GetRectLength(const ParentLayerRect& aRect) const override;
  ParentLayerCoord GetRectOffset(const ParentLayerRect& aRect) const override;
  float GetTransformScale(
      const AsyncTransformComponentMatrix& aMatrix) const override;
  ParentLayerCoord GetTransformTranslation(
      const AsyncTransformComponentMatrix& aMatrix) const override;
  void PostScale(AsyncTransformComponentMatrix& aMatrix,
                 float aScale) const override;
  void PostTranslate(AsyncTransformComponentMatrix& aMatrix,
                     ParentLayerCoord aTranslation) const override;
  ScreenPoint MakePoint(ScreenCoord aCoord) const override;
  const char* Name() const override;
  bool CanScrollTo(Side aSide) const;
  bool CanVerticalScrollWithDynamicToolbar() const;
  SideBits ScrollableDirections() const;
  SideBits ScrollableDirectionsWithDynamicToolbar(
      const ScreenMargin& aFixedLayerMargins) const;

 private:
  OverscrollBehavior GetOverscrollBehavior() const override;
  ParentLayerCoord GetCompositionLengthWithoutDynamicToolbar() const;
  bool HasDynamicToolbar() const;
};

}  // namespace layers
}  // namespace mozilla

#endif