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_Overscroll_h 8 #define mozilla_layers_Overscroll_h 9 10 #include "AsyncPanZoomAnimation.h" 11 #include "AsyncPanZoomController.h" 12 #include "mozilla/TimeStamp.h" 13 #include "nsThreadUtils.h" 14 15 namespace mozilla { 16 namespace layers { 17 18 // Animation used by GenericOverscrollEffect. 19 class OverscrollAnimation : public AsyncPanZoomAnimation { 20 public: OverscrollAnimation(AsyncPanZoomController & aApzc,const ParentLayerPoint & aVelocity,SideBits aOverscrollSideBits)21 OverscrollAnimation(AsyncPanZoomController& aApzc, 22 const ParentLayerPoint& aVelocity, 23 SideBits aOverscrollSideBits) 24 : mApzc(aApzc), mOverscrollSideBits(aOverscrollSideBits) { 25 MOZ_ASSERT( 26 (mOverscrollSideBits & SideBits::eTopBottom) != SideBits::eTopBottom && 27 (mOverscrollSideBits & SideBits::eLeftRight) != 28 SideBits::eLeftRight, 29 "Don't allow overscrolling on both sides at the same time"); 30 if ((aOverscrollSideBits & SideBits::eLeftRight) != SideBits::eNone) { 31 mApzc.mX.StartOverscrollAnimation(aVelocity.x); 32 } 33 if ((aOverscrollSideBits & SideBits::eTopBottom) != SideBits::eNone) { 34 mApzc.mY.StartOverscrollAnimation(aVelocity.y); 35 } 36 } ~OverscrollAnimation()37 virtual ~OverscrollAnimation() { 38 mApzc.mX.EndOverscrollAnimation(); 39 mApzc.mY.EndOverscrollAnimation(); 40 } 41 DoSample(FrameMetrics & aFrameMetrics,const TimeDuration & aDelta)42 virtual bool DoSample(FrameMetrics& aFrameMetrics, 43 const TimeDuration& aDelta) override { 44 // Can't inline these variables due to short-circuit evaluation. 45 bool continueX = mApzc.mX.IsOverscrollAnimationAlive() && 46 mApzc.mX.SampleOverscrollAnimation( 47 aDelta, mOverscrollSideBits & SideBits::eLeftRight); 48 bool continueY = mApzc.mY.IsOverscrollAnimationAlive() && 49 mApzc.mY.SampleOverscrollAnimation( 50 aDelta, mOverscrollSideBits & SideBits::eTopBottom); 51 if (!continueX && !continueY) { 52 // If we got into overscroll from a fling, that fling did not request a 53 // fling snap to avoid a resulting scrollTo from cancelling the overscroll 54 // animation too early. We do still want to request a fling snap, though, 55 // in case the end of the axis at which we're overscrolled is not a valid 56 // snap point, so we request one now. If there are no snap points, this 57 // will do nothing. If there are snap points, we'll get a scrollTo that 58 // snaps us back to the nearest valid snap point. The scroll snapping is 59 // done in a deferred task, otherwise the state change to NOTHING caused 60 // by the overscroll animation ending would clobber a possible state 61 // change to SMOOTH_SCROLL in ScrollSnap(). 62 mDeferredTasks.AppendElement( 63 NewRunnableMethod("layers::AsyncPanZoomController::ScrollSnap", 64 &mApzc, &AsyncPanZoomController::ScrollSnap)); 65 return false; 66 } 67 return true; 68 } 69 WantsRepaints()70 virtual bool WantsRepaints() override { return false; } 71 72 // Tell the overscroll animation about the pan momentum event. For each axis, 73 // the overscroll animation may start, stop, or continue managing that axis in 74 // response to the pan momentum event HandlePanMomentum(const ParentLayerPoint & aDisplacement)75 void HandlePanMomentum(const ParentLayerPoint& aDisplacement) { 76 float xOverscroll = mApzc.mX.GetOverscroll(); 77 if ((xOverscroll > 0 && aDisplacement.x > 0) || 78 (xOverscroll < 0 && aDisplacement.x < 0)) { 79 if (!mApzc.mX.IsOverscrollAnimationRunning()) { 80 // Start a new overscroll animation on this axis, if there is no 81 // overscroll animation running and if the pan momentum displacement 82 // the pan momentum displacement is the same direction of the current 83 // overscroll. 84 mApzc.mX.StartOverscrollAnimation(mApzc.mX.GetVelocity()); 85 mOverscrollSideBits |= 86 xOverscroll > 0 ? SideBits::eRight : SideBits::eLeft; 87 } 88 } else if ((xOverscroll > 0 && aDisplacement.x < 0) || 89 (xOverscroll < 0 && aDisplacement.x > 0)) { 90 // Otherwise, stop the animation in the direction so that it won't clobber 91 // subsequent pan momentum scrolling. 92 mApzc.mX.EndOverscrollAnimation(); 93 } 94 95 // Same as above but for Y axis. 96 float yOverscroll = mApzc.mY.GetOverscroll(); 97 if ((yOverscroll > 0 && aDisplacement.y > 0) || 98 (yOverscroll < 0 && aDisplacement.y < 0)) { 99 if (!mApzc.mY.IsOverscrollAnimationRunning()) { 100 mApzc.mY.StartOverscrollAnimation(mApzc.mY.GetVelocity()); 101 mOverscrollSideBits |= 102 yOverscroll > 0 ? SideBits::eBottom : SideBits::eTop; 103 } 104 } else if ((yOverscroll > 0 && aDisplacement.y < 0) || 105 (yOverscroll < 0 && aDisplacement.y > 0)) { 106 mApzc.mY.EndOverscrollAnimation(); 107 } 108 } 109 GetDirections()110 ScrollDirections GetDirections() const { 111 ScrollDirections directions; 112 if (mApzc.mX.IsOverscrollAnimationRunning()) { 113 directions += ScrollDirection::eHorizontal; 114 } 115 if (mApzc.mY.IsOverscrollAnimationRunning()) { 116 directions += ScrollDirection::eVertical; 117 } 118 return directions; 119 }; 120 AsOverscrollAnimation()121 OverscrollAnimation* AsOverscrollAnimation() override { return this; } 122 IsManagingXAxis()123 bool IsManagingXAxis() const { 124 return mApzc.mX.IsOverscrollAnimationRunning(); 125 } IsManagingYAxis()126 bool IsManagingYAxis() const { 127 return mApzc.mY.IsOverscrollAnimationRunning(); 128 } 129 130 private: 131 AsyncPanZoomController& mApzc; 132 SideBits mOverscrollSideBits; 133 }; 134 135 // Base class for different overscroll effects; 136 class OverscrollEffectBase { 137 public: 138 virtual ~OverscrollEffectBase() = default; 139 virtual void ConsumeOverscroll(ParentLayerPoint& aOverscroll, 140 ScrollDirections aOverscrolableDirections) = 0; 141 virtual void HandleFlingOverscroll(const ParentLayerPoint& aVelocity, 142 SideBits aOverscrollSideBits) = 0; 143 }; 144 145 // A generic overscroll effect, implemented by AsyncPanZoomController itself. 146 class GenericOverscrollEffect : public OverscrollEffectBase { 147 public: GenericOverscrollEffect(AsyncPanZoomController & aApzc)148 explicit GenericOverscrollEffect(AsyncPanZoomController& aApzc) 149 : mApzc(aApzc) {} 150 ConsumeOverscroll(ParentLayerPoint & aOverscroll,ScrollDirections aOverscrolableDirections)151 void ConsumeOverscroll(ParentLayerPoint& aOverscroll, 152 ScrollDirections aOverscrolableDirections) override { 153 if (mApzc.mScrollMetadata.PrefersReducedMotion()) { 154 return; 155 } 156 157 if (aOverscrolableDirections.contains(ScrollDirection::eHorizontal)) { 158 mApzc.mX.OverscrollBy(aOverscroll.x); 159 aOverscroll.x = 0; 160 } 161 162 if (aOverscrolableDirections.contains(ScrollDirection::eVertical)) { 163 mApzc.mY.OverscrollBy(aOverscroll.y); 164 aOverscroll.y = 0; 165 } 166 167 if (!aOverscrolableDirections.isEmpty()) { 168 mApzc.ScheduleComposite(); 169 } 170 } 171 HandleFlingOverscroll(const ParentLayerPoint & aVelocity,SideBits aOverscrollSideBits)172 void HandleFlingOverscroll(const ParentLayerPoint& aVelocity, 173 SideBits aOverscrollSideBits) override { 174 if (mApzc.mScrollMetadata.PrefersReducedMotion()) { 175 return; 176 } 177 178 mApzc.StartOverscrollAnimation(aVelocity, aOverscrollSideBits); 179 } 180 181 private: 182 AsyncPanZoomController& mApzc; 183 }; 184 185 // A widget-specific overscroll effect, implemented by the widget via 186 // GeckoContentController. 187 class WidgetOverscrollEffect : public OverscrollEffectBase { 188 public: WidgetOverscrollEffect(AsyncPanZoomController & aApzc)189 explicit WidgetOverscrollEffect(AsyncPanZoomController& aApzc) 190 : mApzc(aApzc) {} 191 ConsumeOverscroll(ParentLayerPoint & aOverscroll,ScrollDirections aOverscrolableDirections)192 void ConsumeOverscroll(ParentLayerPoint& aOverscroll, 193 ScrollDirections aOverscrolableDirections) override { 194 RefPtr<GeckoContentController> controller = 195 mApzc.GetGeckoContentController(); 196 if (controller && !aOverscrolableDirections.isEmpty()) { 197 controller->UpdateOverscrollOffset(mApzc.GetGuid(), aOverscroll.x, 198 aOverscroll.y, mApzc.IsRootContent()); 199 aOverscroll = ParentLayerPoint(); 200 } 201 } 202 HandleFlingOverscroll(const ParentLayerPoint & aVelocity,SideBits aOverscrollSideBits)203 void HandleFlingOverscroll(const ParentLayerPoint& aVelocity, 204 SideBits aOverscrollSideBits) override { 205 RefPtr<GeckoContentController> controller = 206 mApzc.GetGeckoContentController(); 207 if (controller) { 208 controller->UpdateOverscrollVelocity(mApzc.GetGuid(), aVelocity.x, 209 aVelocity.y, mApzc.IsRootContent()); 210 } 211 } 212 213 private: 214 AsyncPanZoomController& mApzc; 215 }; 216 217 } // namespace layers 218 } // namespace mozilla 219 220 #endif // mozilla_layers_Overscroll_h 221