Use "= default" to denote default constructor or destructor
[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 #include <wtf/CurrentTime.h>
39
40 namespace WebCore {
41
42 static const Seconds overflowScrollbarsAnimationDuration { 1_s };
43 static const Seconds overflowScrollbarsAnimationHideDelay { 2_s };
44 static const Seconds scrollCaptureThreshold { 150_ms };
45
46 std::unique_ptr<ScrollAnimator> ScrollAnimator::create(ScrollableArea& scrollableArea)
47 {
48     return std::make_unique<ScrollAnimatorGtk>(scrollableArea);
49 }
50
51 ScrollAnimatorGtk::ScrollAnimatorGtk(ScrollableArea& scrollableArea)
52     : ScrollAnimator(scrollableArea)
53     , m_overlayScrollbarAnimationTimer(*this, &ScrollAnimatorGtk::overlayScrollbarAnimationTimerFired)
54 {
55     m_kineticAnimation = std::make_unique<ScrollAnimationKinetic>(m_scrollableArea, [this](FloatPoint&& position) {
56 #if ENABLE(SMOOTH_SCROLLING)
57         if (m_smoothAnimation)
58             m_smoothAnimation->setCurrentPosition(position);
59 #endif
60         updatePosition(WTFMove(position));
61     });
62
63 #if ENABLE(SMOOTH_SCROLLING)
64     if (scrollableArea.scrollAnimatorEnabled())
65         ensureSmoothScrollingAnimation();
66 #endif
67 }
68
69 ScrollAnimatorGtk::~ScrollAnimatorGtk() = default;
70
71 #if ENABLE(SMOOTH_SCROLLING)
72 void ScrollAnimatorGtk::ensureSmoothScrollingAnimation()
73 {
74     if (m_smoothAnimation)
75         return;
76
77     m_smoothAnimation = std::make_unique<ScrollAnimationSmooth>(m_scrollableArea, m_currentPosition, [this](FloatPoint&& position) {
78         updatePosition(WTFMove(position));
79     });
80 }
81 #endif
82
83 #if ENABLE(SMOOTH_SCROLLING)
84 bool ScrollAnimatorGtk::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
85 {
86     if (!m_scrollableArea.scrollAnimatorEnabled() || granularity == ScrollByPrecisePixel)
87         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
88
89     ensureSmoothScrollingAnimation();
90     return m_smoothAnimation->scroll(orientation, granularity, step, multiplier);
91 }
92 #endif
93
94 void ScrollAnimatorGtk::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
95 {
96     FloatPoint position = ScrollableArea::scrollPositionFromOffset(offset, toFloatSize(m_scrollableArea.scrollOrigin()));
97     m_kineticAnimation->stop();
98     m_scrollHistory.clear();
99
100 #if ENABLE(SMOOTH_SCROLLING)
101     if (m_smoothAnimation)
102         m_smoothAnimation->setCurrentPosition(position);
103 #endif
104
105     updatePosition(WTFMove(position));
106 }
107
108 FloatPoint ScrollAnimatorGtk::computeVelocity()
109 {
110     if (m_scrollHistory.isEmpty())
111         return { };
112
113     auto first = m_scrollHistory[0].timestamp();
114     auto last = m_scrollHistory.rbegin()->timestamp();
115
116     if (last == first)
117         return { };
118
119     FloatPoint accumDelta;
120     for (const auto& scrollEvent : m_scrollHistory)
121         accumDelta += FloatPoint(scrollEvent.deltaX(), scrollEvent.deltaY());
122
123     m_scrollHistory.clear();
124
125     return FloatPoint(accumDelta.x() * -1000 / (last - first).value(), accumDelta.y() * -1000 / (last - first).value());
126 }
127
128 bool ScrollAnimatorGtk::handleWheelEvent(const PlatformWheelEvent& event)
129 {
130     m_kineticAnimation->stop();
131
132     m_scrollHistory.removeAllMatching([&event] (PlatformWheelEvent& otherEvent) -> bool {
133         return (event.timestamp() - otherEvent.timestamp()) > scrollCaptureThreshold;
134     });
135
136     if (event.isEndOfNonMomentumScroll()) {
137         // We don't need to add the event to the history as its delta will be (0, 0).
138         static_cast<ScrollAnimationKinetic*>(m_kineticAnimation.get())->start(m_currentPosition, computeVelocity(), m_scrollableArea.horizontalScrollbar(), m_scrollableArea.verticalScrollbar());
139         return true;
140     }
141     if (event.isTransitioningToMomentumScroll()) {
142         m_scrollHistory.clear();
143         static_cast<ScrollAnimationKinetic*>(m_kineticAnimation.get())->start(m_currentPosition, event.swipeVelocity(), m_scrollableArea.horizontalScrollbar(), m_scrollableArea.verticalScrollbar());
144         return true;
145     }
146
147     m_scrollHistory.append(event);
148
149     return ScrollAnimator::handleWheelEvent(event);
150 }
151
152 void ScrollAnimatorGtk::willEndLiveResize()
153 {
154     m_kineticAnimation->updateVisibleLengths();
155
156 #if ENABLE(SMOOTH_SCROLLING)
157     if (m_smoothAnimation)
158         m_smoothAnimation->updateVisibleLengths();
159 #endif
160 }
161
162 void ScrollAnimatorGtk::updatePosition(FloatPoint&& position)
163 {
164     FloatSize delta = position - m_currentPosition;
165     m_currentPosition = WTFMove(position);
166     notifyPositionChanged(delta);
167 }
168
169 void ScrollAnimatorGtk::didAddVerticalScrollbar(Scrollbar* scrollbar)
170 {
171     m_kineticAnimation->updateVisibleLengths();
172
173 #if ENABLE(SMOOTH_SCROLLING)
174     if (m_smoothAnimation)
175         m_smoothAnimation->updateVisibleLengths();
176 #endif
177     if (!scrollbar->isOverlayScrollbar())
178         return;
179     m_verticalOverlayScrollbar = scrollbar;
180     if (!m_horizontalOverlayScrollbar)
181         m_overlayScrollbarAnimationCurrent = 1;
182     m_verticalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
183     hideOverlayScrollbars();
184 }
185
186 void ScrollAnimatorGtk::didAddHorizontalScrollbar(Scrollbar* scrollbar)
187 {
188     m_kineticAnimation->updateVisibleLengths();
189
190 #if ENABLE(SMOOTH_SCROLLING)
191     if (m_smoothAnimation)
192         m_smoothAnimation->updateVisibleLengths();
193 #endif
194     if (!scrollbar->isOverlayScrollbar())
195         return;
196     m_horizontalOverlayScrollbar = scrollbar;
197     if (!m_verticalOverlayScrollbar)
198         m_overlayScrollbarAnimationCurrent = 1;
199     m_horizontalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
200     hideOverlayScrollbars();
201 }
202
203 void ScrollAnimatorGtk::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
204 {
205     if (m_verticalOverlayScrollbar != scrollbar)
206         return;
207     m_verticalOverlayScrollbar = nullptr;
208     if (!m_horizontalOverlayScrollbar)
209         m_overlayScrollbarAnimationCurrent = 0;
210 }
211
212 void ScrollAnimatorGtk::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
213 {
214     if (m_horizontalOverlayScrollbar != scrollbar)
215         return;
216     m_horizontalOverlayScrollbar = nullptr;
217     if (!m_verticalOverlayScrollbar)
218         m_overlayScrollbarAnimationCurrent = 0;
219 }
220
221 void ScrollAnimatorGtk::updateOverlayScrollbarsOpacity()
222 {
223     if (m_verticalOverlayScrollbar && m_overlayScrollbarAnimationCurrent != m_verticalOverlayScrollbar->opacity()) {
224         m_verticalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
225         if (m_verticalOverlayScrollbar->hoveredPart() == NoPart)
226             m_verticalOverlayScrollbar->invalidate();
227     }
228
229     if (m_horizontalOverlayScrollbar && m_overlayScrollbarAnimationCurrent != m_horizontalOverlayScrollbar->opacity()) {
230         m_horizontalOverlayScrollbar->setOpacity(m_overlayScrollbarAnimationCurrent);
231         if (m_horizontalOverlayScrollbar->hoveredPart() == NoPart)
232             m_horizontalOverlayScrollbar->invalidate();
233     }
234 }
235
236 static inline double easeOutCubic(double t)
237 {
238     double p = t - 1;
239     return p * p * p + 1;
240 }
241
242 void ScrollAnimatorGtk::overlayScrollbarAnimationTimerFired()
243 {
244     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
245         return;
246     if (m_overlayScrollbarsLocked)
247         return;
248
249     MonotonicTime currentTime = MonotonicTime::now();
250     double progress = 1;
251     if (currentTime < m_overlayScrollbarAnimationEndTime)
252         progress = (currentTime - m_overlayScrollbarAnimationStartTime).value() / (m_overlayScrollbarAnimationEndTime - m_overlayScrollbarAnimationStartTime).value();
253     progress = m_overlayScrollbarAnimationSource + (easeOutCubic(progress) * (m_overlayScrollbarAnimationTarget - m_overlayScrollbarAnimationSource));
254     if (progress != m_overlayScrollbarAnimationCurrent) {
255         m_overlayScrollbarAnimationCurrent = progress;
256         updateOverlayScrollbarsOpacity();
257     }
258
259     if (m_overlayScrollbarAnimationCurrent != m_overlayScrollbarAnimationTarget) {
260         static const double frameRate = 60;
261         static const Seconds tickTime = 1_s / frameRate;
262         static const Seconds minimumTimerInterval = 1_ms;
263         Seconds deltaToNextFrame = std::max(tickTime - (MonotonicTime::now() - currentTime), minimumTimerInterval);
264         m_overlayScrollbarAnimationTimer.startOneShot(deltaToNextFrame);
265     } else
266         hideOverlayScrollbars();
267 }
268
269 void ScrollAnimatorGtk::showOverlayScrollbars()
270 {
271     if (m_overlayScrollbarsLocked)
272         return;
273
274     if (m_overlayScrollbarAnimationTimer.isActive() && m_overlayScrollbarAnimationTarget == 1)
275         return;
276     m_overlayScrollbarAnimationTimer.stop();
277
278     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
279         return;
280
281     m_overlayScrollbarAnimationSource = m_overlayScrollbarAnimationCurrent;
282     m_overlayScrollbarAnimationTarget = 1;
283     if (m_overlayScrollbarAnimationTarget != m_overlayScrollbarAnimationCurrent) {
284         m_overlayScrollbarAnimationStartTime = MonotonicTime::now();
285         m_overlayScrollbarAnimationEndTime = m_overlayScrollbarAnimationStartTime + overflowScrollbarsAnimationDuration;
286         m_overlayScrollbarAnimationTimer.startOneShot(0_s);
287     } else
288         hideOverlayScrollbars();
289 }
290
291 void ScrollAnimatorGtk::hideOverlayScrollbars()
292 {
293     if (m_overlayScrollbarAnimationTimer.isActive() && !m_overlayScrollbarAnimationTarget)
294         return;
295     m_overlayScrollbarAnimationTimer.stop();
296
297     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
298         return;
299
300     m_overlayScrollbarAnimationSource = m_overlayScrollbarAnimationCurrent;
301     m_overlayScrollbarAnimationTarget = 0;
302     if (m_overlayScrollbarAnimationTarget == m_overlayScrollbarAnimationCurrent)
303         return;
304     m_overlayScrollbarAnimationStartTime = MonotonicTime::now() + overflowScrollbarsAnimationHideDelay;
305     m_overlayScrollbarAnimationEndTime = m_overlayScrollbarAnimationStartTime + overflowScrollbarsAnimationDuration + overflowScrollbarsAnimationHideDelay;
306     m_overlayScrollbarAnimationTimer.startOneShot(overflowScrollbarsAnimationHideDelay);
307 }
308
309 void ScrollAnimatorGtk::mouseEnteredContentArea()
310 {
311     showOverlayScrollbars();
312 }
313
314 void ScrollAnimatorGtk::mouseExitedContentArea()
315 {
316     hideOverlayScrollbars();
317 }
318
319 void ScrollAnimatorGtk::mouseMovedInContentArea()
320 {
321     showOverlayScrollbars();
322 }
323
324 void ScrollAnimatorGtk::contentAreaDidShow()
325 {
326     showOverlayScrollbars();
327 }
328
329 void ScrollAnimatorGtk::contentAreaDidHide()
330 {
331     if (m_overlayScrollbarsLocked)
332         return;
333     m_overlayScrollbarAnimationTimer.stop();
334     if (m_overlayScrollbarAnimationCurrent) {
335         m_overlayScrollbarAnimationCurrent = 0;
336         updateOverlayScrollbarsOpacity();
337     }
338 }
339
340 void ScrollAnimatorGtk::notifyContentAreaScrolled(const FloatSize&)
341 {
342     showOverlayScrollbars();
343 }
344
345 void ScrollAnimatorGtk::lockOverlayScrollbarStateToHidden(bool shouldLockState)
346 {
347     if (m_overlayScrollbarsLocked == shouldLockState)
348         return;
349     m_overlayScrollbarsLocked = shouldLockState;
350
351     if (!m_horizontalOverlayScrollbar && !m_verticalOverlayScrollbar)
352         return;
353
354     if (m_overlayScrollbarsLocked) {
355         m_overlayScrollbarAnimationTimer.stop();
356         if (m_horizontalOverlayScrollbar)
357             m_horizontalOverlayScrollbar->setOpacity(0);
358         if (m_verticalOverlayScrollbar)
359             m_verticalOverlayScrollbar->setOpacity(0);
360     } else {
361         if (m_overlayScrollbarAnimationCurrent == 1)
362             updateOverlayScrollbarsOpacity();
363         else
364             showOverlayScrollbars();
365     }
366 }
367
368 } // namespace WebCore
369