Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et 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/. */
7 #include "Axis.h"
8 #include <math.h> // for fabsf, pow, powf
9 #include <algorithm> // for max
10 #include "AsyncPanZoomController.h" // for AsyncPanZoomController
11 #include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager
12 #include "FrameMetrics.h" // for FrameMetrics
13 #include "mozilla/Attributes.h" // for MOZ_FINAL
14 #include "mozilla/Preferences.h" // for Preferences
15 #include "mozilla/gfx/Rect.h" // for RoundedIn
16 #include "mozilla/mozalloc.h" // for operator new
17 #include "nsMathUtils.h" // for NS_lround
18 #include "nsThreadUtils.h" // for NS_DispatchToMainThread, etc
19 #include "nscore.h" // for NS_IMETHOD
20 #include "gfxPrefs.h" // for the preferences
22 namespace mozilla {
23 namespace layers {
25 Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
26 : mPos(0),
27 mVelocity(0.0f),
28 mAxisLocked(false),
29 mAsyncPanZoomController(aAsyncPanZoomController)
30 {
31 }
33 void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeDelta) {
34 float newVelocity = mAxisLocked ? 0 : (mPos - aPos) / aTimeDelta.ToMilliseconds();
35 if (gfxPrefs::APZMaxVelocity() > 0.0f) {
36 newVelocity = std::min(newVelocity, gfxPrefs::APZMaxVelocity() * APZCTreeManager::GetDPI());
37 }
39 mVelocity = newVelocity;
40 mPos = aPos;
42 // Limit queue size pased on pref
43 mVelocityQueue.AppendElement(mVelocity);
44 if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) {
45 mVelocityQueue.RemoveElementAt(0);
46 }
47 }
49 void Axis::StartTouch(int32_t aPos) {
50 mStartPos = aPos;
51 mPos = aPos;
52 mAxisLocked = false;
53 }
55 float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut) {
56 if (mAxisLocked) {
57 aOverscrollAmountOut = 0;
58 return 0;
59 }
61 float displacement = aDisplacement;
63 // If this displacement will cause an overscroll, throttle it. Can potentially
64 // bring it to 0 even if the velocity is high.
65 if (DisplacementWillOverscroll(displacement) != OVERSCROLL_NONE) {
66 // No need to have a velocity along this axis anymore; it won't take us
67 // anywhere, so we're just spinning needlessly.
68 mVelocity = 0.0f;
69 aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement);
70 displacement -= aOverscrollAmountOut;
71 }
72 return displacement;
73 }
75 float Axis::PanDistance() {
76 return fabsf(mPos - mStartPos);
77 }
79 float Axis::PanDistance(float aPos) {
80 return fabsf(aPos - mStartPos);
81 }
83 void Axis::EndTouch() {
84 // Calculate the mean velocity and empty the queue.
85 int count = mVelocityQueue.Length();
86 if (count) {
87 mVelocity = 0;
88 while (!mVelocityQueue.IsEmpty()) {
89 mVelocity += mVelocityQueue[0];
90 mVelocityQueue.RemoveElementAt(0);
91 }
92 mVelocity /= count;
93 }
94 }
96 void Axis::CancelTouch() {
97 mVelocity = 0.0f;
98 while (!mVelocityQueue.IsEmpty()) {
99 mVelocityQueue.RemoveElementAt(0);
100 }
101 }
103 bool Axis::Scrollable() {
104 if (mAxisLocked) {
105 return false;
106 }
107 return GetCompositionLength() < GetPageLength();
108 }
110 bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) {
111 if (fabsf(mVelocity) <= gfxPrefs::APZFlingStoppedThreshold()) {
112 // If the velocity is very low, just set it to 0 and stop the fling,
113 // otherwise we'll just asymptotically approach 0 and the user won't
114 // actually see any changes.
115 mVelocity = 0.0f;
116 return false;
117 } else {
118 mVelocity *= pow(1.0f - gfxPrefs::APZFlingFriction(), float(aDelta.ToMilliseconds()));
119 }
120 return true;
121 }
123 Axis::Overscroll Axis::GetOverscroll() {
124 // If the current pan takes the window to the left of or above the current
125 // page rect.
126 bool minus = GetOrigin() < GetPageStart();
127 // If the current pan takes the window to the right of or below the current
128 // page rect.
129 bool plus = GetCompositionEnd() > GetPageEnd();
130 if (minus && plus) {
131 return OVERSCROLL_BOTH;
132 }
133 if (minus) {
134 return OVERSCROLL_MINUS;
135 }
136 if (plus) {
137 return OVERSCROLL_PLUS;
138 }
139 return OVERSCROLL_NONE;
140 }
142 float Axis::GetExcess() {
143 switch (GetOverscroll()) {
144 case OVERSCROLL_MINUS: return GetOrigin() - GetPageStart();
145 case OVERSCROLL_PLUS: return GetCompositionEnd() - GetPageEnd();
146 case OVERSCROLL_BOTH: return (GetCompositionEnd() - GetPageEnd()) +
147 (GetPageStart() - GetOrigin());
148 default: return 0;
149 }
150 }
152 Axis::Overscroll Axis::DisplacementWillOverscroll(float aDisplacement) {
153 // If the current pan plus a displacement takes the window to the left of or
154 // above the current page rect.
155 bool minus = GetOrigin() + aDisplacement < GetPageStart();
156 // If the current pan plus a displacement takes the window to the right of or
157 // below the current page rect.
158 bool plus = GetCompositionEnd() + aDisplacement > GetPageEnd();
159 if (minus && plus) {
160 return OVERSCROLL_BOTH;
161 }
162 if (minus) {
163 return OVERSCROLL_MINUS;
164 }
165 if (plus) {
166 return OVERSCROLL_PLUS;
167 }
168 return OVERSCROLL_NONE;
169 }
171 float Axis::DisplacementWillOverscrollAmount(float aDisplacement) {
172 switch (DisplacementWillOverscroll(aDisplacement)) {
173 case OVERSCROLL_MINUS: return (GetOrigin() + aDisplacement) - GetPageStart();
174 case OVERSCROLL_PLUS: return (GetCompositionEnd() + aDisplacement) - GetPageEnd();
175 // Don't handle overscrolled in both directions; a displacement can't cause
176 // this, it must have already been zoomed out too far.
177 default: return 0;
178 }
179 }
181 float Axis::ScaleWillOverscrollAmount(float aScale, float aFocus) {
182 float originAfterScale = (GetOrigin() + aFocus) - (aFocus / aScale);
184 bool both = ScaleWillOverscrollBothSides(aScale);
185 bool minus = originAfterScale < GetPageStart();
186 bool plus = (originAfterScale + (GetCompositionLength() / aScale)) > GetPageEnd();
188 if ((minus && plus) || both) {
189 // If we ever reach here it's a bug in the client code.
190 MOZ_ASSERT(false, "In an OVERSCROLL_BOTH condition in ScaleWillOverscrollAmount");
191 return 0;
192 }
193 if (minus) {
194 return originAfterScale - GetPageStart();
195 }
196 if (plus) {
197 return originAfterScale + (GetCompositionLength() / aScale) - GetPageEnd();
198 }
199 return 0;
200 }
202 float Axis::GetVelocity() {
203 return mAxisLocked ? 0 : mVelocity;
204 }
206 void Axis::SetVelocity(float aVelocity) {
207 mVelocity = aVelocity;
208 }
210 float Axis::GetCompositionEnd() const {
211 return GetOrigin() + GetCompositionLength();
212 }
214 float Axis::GetPageEnd() const {
215 return GetPageStart() + GetPageLength();
216 }
218 float Axis::GetOrigin() const {
219 CSSPoint origin = mAsyncPanZoomController->GetFrameMetrics().GetScrollOffset();
220 return GetPointOffset(origin);
221 }
223 float Axis::GetCompositionLength() const {
224 const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
225 return GetRectLength(metrics.CalculateCompositedRectInCssPixels());
226 }
228 float Axis::GetPageStart() const {
229 CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
230 return GetRectOffset(pageRect);
231 }
233 float Axis::GetPageLength() const {
234 CSSRect pageRect = mAsyncPanZoomController->GetFrameMetrics().mScrollableRect;
235 return GetRectLength(pageRect);
236 }
238 bool Axis::ScaleWillOverscrollBothSides(float aScale) {
239 const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
241 CSSToParentLayerScale scale(metrics.GetZoomToParent().scale * aScale);
242 CSSRect cssCompositionBounds = metrics.mCompositionBounds / scale;
244 return GetRectLength(metrics.mScrollableRect) < GetRectLength(cssCompositionBounds);
245 }
247 bool Axis::HasRoomToPan() const {
248 return GetOrigin() > GetPageStart()
249 || GetCompositionEnd() < GetPageEnd();
250 }
253 AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController)
254 : Axis(aAsyncPanZoomController)
255 {
257 }
259 float AxisX::GetPointOffset(const CSSPoint& aPoint) const
260 {
261 return aPoint.x;
262 }
264 float AxisX::GetRectLength(const CSSRect& aRect) const
265 {
266 return aRect.width;
267 }
269 float AxisX::GetRectOffset(const CSSRect& aRect) const
270 {
271 return aRect.x;
272 }
274 AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController)
275 : Axis(aAsyncPanZoomController)
276 {
278 }
280 float AxisY::GetPointOffset(const CSSPoint& aPoint) const
281 {
282 return aPoint.y;
283 }
285 float AxisY::GetRectLength(const CSSRect& aRect) const
286 {
287 return aRect.height;
288 }
290 float AxisY::GetRectOffset(const CSSRect& aRect) const
291 {
292 return aRect.y;
293 }
295 }
296 }