[WTF] Move currentCPUTime and sleep(Seconds) to CPUTime.h and Seconds.h respectively
[WebKit-https.git] / Source / WebCore / platform / gtk / ScrollAnimatorGtk.cpp
1 /*
2  * Copyright (c) 2016 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "ScrollAnimatorGtk.h"
33
34 #include "ScrollAnimationKinetic.h"
35 #include "ScrollAnimationSmooth.h"
36 #include "ScrollableArea.h"
37 #include "ScrollbarTheme.h"
38
39 namespace WebCore {
40
41 static const Seconds overflowScrollbarsAnimationDuration { 1_s };
42 static const Seconds overflowScrollbarsAnimationHideDelay { 2_s };
43 static const Seconds scrollCaptureThreshold { 150_ms };
44
45 std::unique_ptr<ScrollAnimator> ScrollAnimator::create(ScrollableArea& scrollableArea)
46 {
47     return std::make_unique<ScrollAnimatorGtk>(scrollableArea);
48 }
49
50 ScrollAnimatorGtk::ScrollAnimatorGtk(ScrollableArea& scrollableArea)
51     : ScrollAnimator(scrollableArea)
52     , m_overlayScrollbarAnimationTimer(*this, &ScrollAnimatorGtk::overlayScrollbarAnimationTimerFired)
53 {
54     m_kineticAnimation = std::make_unique<ScrollAnimationKinetic>(m_scrollableArea, [this](FloatPoint&& position) {
55 #if ENABLE(SMOOTH_SCROLLING)
56         if (m_smoothAnimation)
57             m_smoothAnimation->setCurrentPosition(position);
58 #endif
59         updatePosition(WTFMove(position));
60     });
61
62 #if ENABLE(SMOOTH_SCROLLING)
63     if (scrollableArea.scrollAnimatorEnabled())
64         ensureSmoothScrollingAnimation();
65 #endif
66 }
67
68 ScrollAnimatorGtk::~ScrollAnimatorGtk() = default;
69
70 #if ENABLE(SMOOTH_SCROLLING)
71 void ScrollAnimatorGtk::ensureSmoothScrollingAnimation()
72 {
73     if (m_smoothAnimation)
74         return;
75
76     m_smoothAnimation = std::make_unique<ScrollAnimationSmooth>(m_scrollableArea, m_currentPosition, [this](FloatPoint&& position) {
77         updatePosition(WTFMove(position));
78     });
79 }
80 #endif
81
82 #if ENABLE(SMOOTH_SCROLLING)
83 bool ScrollAnimatorGtk::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
84 {
85     if (!m_scrollableArea.scrollAnimatorEnabled() || granularity == ScrollByPrecisePixel)
86         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
87
88     ensureSmoothScrollingAnimation();
89     return m_smoothAnimation->scroll(orientation, granularity, step, multiplier);
90 }
91 #endif
92
93 void ScrollAnimatorGtk::scrollToOffsetWithoutAnimation(const FloatPoint& offset, ScrollClamping)
94 {
95     FloatPoint position = ScrollableArea::scrollPositionFromOffset(offset, toFloatSize(m_scrollableArea.scrollOrigin()));
96     m_kineticAnimation->stop();
97     m_scrollHistory.clear();
98
99 #if ENABLE(SMOOTH_SCROLLING)
100     if (m_smoothAnimation)
101         m_smoothAnimation->setCurrentPosition(position);
102 #endif
103
104     updatePosition(WTFMove(position));
105 }
106
107 FloatPoint ScrollAnimatorGtk::computeVelocity()
108 {
109     if (m_scrollHistory.isEmpty())
110         return { };
111
112     auto first = m_scrollHistory[0].timestamp();
113     auto last = m_scrollHistory.rbegin()->timestamp();
114
115     if (last == first)
116         return { };
117
118     FloatPoint accumDelta;
119     for (const auto& scrollEvent : m_scrollHistory)
120         accumDelta += FloatPoint(scrollEvent.deltaX(), scrollEvent.deltaY());
121
122     m_scrollHistory.clear();
123
124     return FloatPoint(accumDelta.x() * -1 / (last - first).value(), accumDelta.y() * -1 / (last - first).value());
125 }
126
127 bool ScrollAnimatorGtk::handleWheelEvent(const PlatformWheelEvent& event)
128 {
129     m_kineticAnimation->stop();
130
131     m_scrollHistory.removeAllMatching([&event] (PlatformWheelEvent& otherEvent) -> bool {
132         return (event.timestamp() - otherEvent.timestamp()) > scrollCaptureThreshold;
133     });
134
135     if (event.isEndOfNonMomentumScroll()) {
136         // We don't need to add the event to the history as its delta will be (0, 0).
137         static_cast<ScrollAnimationKinetic*>(m_kineticAnimation.get())->start(m_currentPosition, computeVelocity(), m_scrollableArea.horizontalScrollbar(), m_scrollableArea.verticalScrollbar());
138         return true;
139     }
140     if (event.isTransitioningToMomentumScroll()) {
141         m_scrollHistory.clear();
142         static_cast<ScrollAnimationKinetic*>(m_kineticAnimation.get())->start(m_currentPosition, event.swipeVelocity(), m_scrollableArea.horizontalScrollbar(), m_scrollableArea.verticalScrollbar());
143         return true;
144     }
145
146     m_scrollHistory.append(event);
147
148     return ScrollAnimator::handleWheelEvent(event);
149 }
150
151 void ScrollAnimatorGtk::willEndLiveResize()
152 {
153     m_kineticAnimation->updateVisibleLengths();
154
155 #if ENABLE(SMOOTH_SCROLLING)
156     if (m_smoothAnimation)
157         m_smoothAnimation->updateVisibleLengths();
158 #endif
159 }
160
161 void ScrollAnimatorGtk::updatePosition(FloatPoint&& position)
162 {
163     FloatSize delta = position - m_currentPosition;
164     m_currentPosition = WTFMove(position);
165     notifyPositionChanged(delta);
166 }
167
168 void ScrollAnimatorGtk::didAddVerticalScrollbar(Scrollbar* scrollbar)
169 {
170     m_kineticAnimation->updateVisibleLengths();
171
172 #if ENABLE(SMOOTH_SCROLLING)
173     if (m_smoothAnimation)
174         m_smoothAnimation->updateVisibleLengths();
175 #endif
176     if (!scrollbar->isOverlayScrollbar())
177         return;
178     m_verticalOverlayScrollbar = scrollbar;
179     if (!m_horizontalOverlayScrollbar)
180         m_overlayScrollbarAnimationCurrent = 1;
181     m_verticalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
182     hideOverlayScrollbars();
183 }
184
185 void ScrollAnimatorGtk::didAddHorizontalScrollbar(Scrollbar* scrollbar)
186 {
187     m_kineticAnimation->updateVisibleLengths();
188
189 #if ENABLE(SMOOTH_SCROLLING)
190     if (m_smoothAnimation)
191         m_smoothAnimation->updateVisibleLengths();
192 #endif
193     if (!scrollbar->isOverlayScrollbar())
194         return;
195     m_horizontalOverlayScrollbar = scrollbar;
196     if (!m_verticalOverlayScrollbar)
197         m_overlayScrollbarAnimationCurrent = 1;
198     m_horizontalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
199     hideOverlayScrollbars();
200 }
201
202 void ScrollAnimatorGtk::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
203 {
204     if (m_verticalOverlayScrollbar != scrollbar)
205         return;
206     m_verticalOverlayScrollbar = nullptr;
207     if (!m_horizontalOverlayScrollbar)
208         m_overlayScrollbarAnimationCurrent = 0;
209 }
210
211 void ScrollAnimatorGtk::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
212 {
213     if (m_horizontalOverlayScrollbar != scrollbar)
214         return;
215     m_horizontalOverlayScrollbar = nullptr;
216     if (!m_verticalOverlayScrollbar)
217         m_overlayScrollbarAnimationCurrent = 0;
218 }
219
220 void ScrollAnimatorGtk::updateOverlayScrollbarsOpacity()
221 {
222     if (m_verticalOverlayScrollbar && m_overlayScrollbarAnimationCurrent != m_verticalOverlayScrollbar->opacity()) {
223         m_verticalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
224         if (m_verticalOverlayScrollbar->hoveredPart() == NoPart)
225             m_verticalOverlayScrollbar->invalidate();
226     }
227
228     if (m_horizontalOverlayScrollbar && m_overlayScrollbarAnimationCurrent != m_horizontalOverlayScrollbar->opacity()) {
229         m_horizontalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
230         if (m_horizontalOverlayScrollbar->hoveredPart() == NoPart)
231             m_horizontalOverlayScrollbar->invalidate();
232     }
233 }
234
235 static inline double easeOutCubic(double t)
236 {
237     double p = t - 1;
238     return p * p * p + 1;
239 }
240
241 void ScrollAnimatorGtk::overlayScrollbarAnimationTimerFired()
242 {
243     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
244         return;
245     if (m_overlayScrollbarsLocked)
246         return;
247
248     MonotonicTime currentTime = MonotonicTime::now();
249     double progress = 1;
250     if (currentTime < m_overlayScrollbarAnimationEndTime)
251         progress = (currentTime - m_overlayScrollbarAnimationStartTime).value() / (m_overlayScrollbarAnimationEndTime - m_overlayScrollbarAnimationStartTime).value();
252     progress = m_overlayScrollbarAnimationSource + (easeOutCubic(progress) * (m_overlayScrollbarAnimationTarget - m_overlayScrollbarAnimationSource));
253     if (progress != m_overlayScrollbarAnimationCurrent) {
254         m_overlayScrollbarAnimationCurrent = progress;
255         updateOverlayScrollbarsOpacity();
256     }
257
258     if (m_overlayScrollbarAnimationCurrent != m_overlayScrollbarAnimationTarget) {
259         static const double frameRate = 60;
260         static const Seconds tickTime = 1_s / frameRate;
261         static const Seconds minimumTimerInterval = 1_ms;
262         Seconds deltaToNextFrame = std::max(tickTime - (MonotonicTime::now() - currentTime), minimumTimerInterval);
263         m_overlayScrollbarAnimationTimer.startOneShot(deltaToNextFrame);
264     } else
265         hideOverlayScrollbars();
266 }
267
268 void ScrollAnimatorGtk::showOverlayScrollbars()
269 {
270     if (m_overlayScrollbarsLocked)
271         return;
272
273     if (m_overlayScrollbarAnimationTimer.isActive() && m_overlayScrollbarAnimationTarget == 1)
274         return;
275     m_overlayScrollbarAnimationTimer.stop();
276
277     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
278         return;
279
280     m_overlayScrollbarAnimationSource = m_overlayScrollbarAnimationCurrent;
281     m_overlayScrollbarAnimationTarget = 1;
282     if (m_overlayScrollbarAnimationTarget != m_overlayScrollbarAnimationCurrent) {
283         m_overlayScrollbarAnimationStartTime = MonotonicTime::now();
284         m_overlayScrollbarAnimationEndTime = m_overlayScrollbarAnimationStartTime + overflowScrollbarsAnimationDuration;
285         m_overlayScrollbarAnimationTimer.startOneShot(0_s);
286     } else
287         hideOverlayScrollbars();
288 }
289
290 void ScrollAnimatorGtk::hideOverlayScrollbars()
291 {
292     if (m_overlayScrollbarAnimationTimer.isActive() && !m_overlayScrollbarAnimationTarget)
293         return;
294     m_overlayScrollbarAnimationTimer.stop();
295
296     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
297         return;
298
299     m_overlayScrollbarAnimationSource = m_overlayScrollbarAnimationCurrent;
300     m_overlayScrollbarAnimationTarget = 0;
301     if (m_overlayScrollbarAnimationTarget == m_overlayScrollbarAnimationCurrent)
302         return;
303     m_overlayScrollbarAnimationStartTime = MonotonicTime::now() + overflowScrollbarsAnimationHideDelay;
304     m_overlayScrollbarAnimationEndTime = m_overlayScrollbarAnimationStartTime + overflowScrollbarsAnimationDuration + overflowScrollbarsAnimationHideDelay;
305     m_overlayScrollbarAnimationTimer.startOneShot(overflowScrollbarsAnimationHideDelay);
306 }
307
308 void ScrollAnimatorGtk::mouseEnteredContentArea()
309 {
310     showOverlayScrollbars();
311 }
312
313 void ScrollAnimatorGtk::mouseExitedContentArea()
314 {
315     hideOverlayScrollbars();
316 }
317
318 void ScrollAnimatorGtk::mouseMovedInContentArea()
319 {
320     showOverlayScrollbars();
321 }
322
323 void ScrollAnimatorGtk::contentAreaDidShow()
324 {
325     showOverlayScrollbars();
326 }
327
328 void ScrollAnimatorGtk::contentAreaDidHide()
329 {
330     if (m_overlayScrollbarsLocked)
331         return;
332     m_overlayScrollbarAnimationTimer.stop();
333     if (m_overlayScrollbarAnimationCurrent) {
334         m_overlayScrollbarAnimationCurrent = 0;
335         updateOverlayScrollbarsOpacity();
336     }
337 }
338
339 void ScrollAnimatorGtk::notifyContentAreaScrolled(const FloatSize&)
340 {
341     showOverlayScrollbars();
342 }
343
344 void ScrollAnimatorGtk::lockOverlayScrollbarStateToHidden(bool shouldLockState)
345 {
346     if (m_overlayScrollbarsLocked == shouldLockState)
347         return;
348     m_overlayScrollbarsLocked = shouldLockState;
349
350     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
351         return;
352
353     if (m_overlayScrollbarsLocked) {
354         m_overlayScrollbarAnimationTimer.stop();
355         if (m_horizontalOverlayScrollbar)
356             m_horizontalOverlayScrollbar->setOpacity(0);
357         if (m_verticalOverlayScrollbar)
358             m_verticalOverlayScrollbar->setOpacity(0);
359     } else {
360         if (m_overlayScrollbarAnimationCurrent == 1)
361             updateOverlayScrollbarsOpacity();
362         else
363             showOverlayScrollbars();
364     }
365 }
366
367 } // namespace WebCore
368