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