]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kitemlistsmoothscroller.cpp
Merge branch 'release/20.08' into master
[dolphin.git] / src / kitemviews / private / kitemlistsmoothscroller.cpp
1 /*
2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "kitemlistsmoothscroller.h"
8
9 #include <QApplication>
10 #include <QPropertyAnimation>
11 #include <QScrollBar>
12 #include <QStyle>
13 #include <QWheelEvent>
14
15 KItemListSmoothScroller::KItemListSmoothScroller(QScrollBar* scrollBar,
16 QObject* parent) :
17 QObject(parent),
18 m_scrollBarPressed(false),
19 m_smoothScrolling(true),
20 m_scrollBar(scrollBar),
21 m_animation(nullptr)
22 {
23 m_animation = new QPropertyAnimation(this);
24 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
25 const int animationDuration = m_scrollBar->style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, m_scrollBar);
26 const bool animationEnabled = (animationDuration > 0);
27 #else
28 const int animationDuration = 100;
29 const bool animationEnabled = m_scrollBar->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, m_scrollBar);
30 #endif
31 m_animation->setDuration(animationEnabled ? animationDuration : 1);
32 connect(m_animation, &QPropertyAnimation::stateChanged,
33 this, &KItemListSmoothScroller::slotAnimationStateChanged);
34
35 m_scrollBar->installEventFilter(this);
36 }
37
38 KItemListSmoothScroller::~KItemListSmoothScroller()
39 {
40 }
41
42 void KItemListSmoothScroller::setScrollBar(QScrollBar *scrollBar)
43 {
44 m_scrollBar = scrollBar;
45 }
46
47 QScrollBar* KItemListSmoothScroller::scrollBar() const
48 {
49 return m_scrollBar;
50 }
51
52 void KItemListSmoothScroller::setTargetObject(QObject* target)
53 {
54 m_animation->setTargetObject(target);
55 }
56
57 QObject* KItemListSmoothScroller::targetObject() const
58 {
59 return m_animation->targetObject();
60 }
61
62 void KItemListSmoothScroller::setPropertyName(const QByteArray& propertyName)
63 {
64 m_animation->setPropertyName(propertyName);
65 }
66
67 QByteArray KItemListSmoothScroller::propertyName() const
68 {
69 return m_animation->propertyName();
70 }
71
72 void KItemListSmoothScroller::scrollContentsBy(qreal distance)
73 {
74 QObject* target = targetObject();
75 if (!target) {
76 return;
77 }
78
79 const QByteArray name = propertyName();
80 const qreal currentOffset = target->property(name).toReal();
81 if (static_cast<int>(currentOffset) == m_scrollBar->value()) {
82 // The current offset is already synchronous to the scrollbar
83 return;
84 }
85
86 const bool animRunning = (m_animation->state() == QAbstractAnimation::Running);
87 if (animRunning) {
88 // Stopping a running animation means skipping the range from the current offset
89 // until the target offset. To prevent skipping of the range the difference
90 // is added to the new target offset.
91 const qreal oldEndOffset = m_animation->endValue().toReal();
92 distance += (currentOffset - oldEndOffset);
93 }
94
95 const qreal endOffset = currentOffset - distance;
96 if (m_smoothScrolling || animRunning) {
97 qreal startOffset = currentOffset;
98 if (animRunning) {
99 // If the animation was running and has been interrupted by assigning a new end-offset
100 // one frame must be added to the start-offset to keep the animation smooth. This also
101 // assures that animation proceeds even in cases where new end-offset are triggered
102 // within a very short timeslots.
103 startOffset += (endOffset - currentOffset) * 1000 / (m_animation->duration() * 60);
104 if (currentOffset < endOffset) {
105 startOffset = qMin(startOffset, endOffset);
106 } else {
107 startOffset = qMax(startOffset, endOffset);
108 }
109 }
110
111 m_animation->stop();
112 m_animation->setStartValue(startOffset);
113 m_animation->setEndValue(endOffset);
114 m_animation->setEasingCurve(animRunning ? QEasingCurve::OutQuad : QEasingCurve::InOutQuad);
115 m_animation->start();
116 target->setProperty(name, startOffset);
117 } else {
118 target->setProperty(name, endOffset);
119 }
120 }
121
122 void KItemListSmoothScroller::scrollTo(qreal position)
123 {
124 int newValue = position;
125 newValue = qBound(0, newValue, m_scrollBar->maximum());
126
127 if (newValue != m_scrollBar->value()) {
128 m_smoothScrolling = true;
129 m_scrollBar->setValue(newValue);
130 }
131 }
132
133 bool KItemListSmoothScroller::requestScrollBarUpdate(int newMaximum)
134 {
135 if (m_animation->state() == QAbstractAnimation::Running) {
136 if (newMaximum == m_scrollBar->maximum()) {
137 // The value has been changed by the animation, no update
138 // of the scrollbars is required as their target state will be
139 // reached with the end of the animation.
140 return false;
141 }
142
143 // The maximum has been changed which indicates that the content
144 // of the view has been changed. Stop the animation in any case and
145 // update the scrollbars immediately.
146 m_animation->stop();
147 }
148 return true;
149 }
150
151 bool KItemListSmoothScroller::eventFilter(QObject* obj, QEvent* event)
152 {
153 Q_ASSERT(obj == m_scrollBar);
154
155 switch (event->type()) {
156 case QEvent::MouseButtonPress:
157 m_scrollBarPressed = true;
158 m_smoothScrolling = true;
159 break;
160
161 case QEvent::MouseButtonRelease:
162 m_scrollBarPressed = false;
163 m_smoothScrolling = false;
164 break;
165
166 case QEvent::Wheel:
167 return false; // we're the ones sending them
168
169 default:
170 break;
171 }
172
173 return QObject::eventFilter(obj, event);
174 }
175
176 void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::State newState,
177 QAbstractAnimation::State oldState)
178 {
179 Q_UNUSED(oldState)
180 if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) {
181 m_smoothScrolling = false;
182 }
183 }
184
185 void KItemListSmoothScroller::handleWheelEvent(QWheelEvent* event)
186 {
187 const bool previous = m_smoothScrolling;
188
189 m_smoothScrolling = true;
190
191 QWheelEvent copy = *event;
192 QApplication::sendEvent(m_scrollBar, &copy);
193 event->setAccepted(copy.isAccepted());
194
195 m_smoothScrolling = previous;
196 }
197