]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kitemlistsmoothscroller.cpp
Clazy fix
[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 <KConfigGroup>
10 #include <KSharedConfig>
11
12 #include <QApplication>
13 #include <QDBusConnection>
14 #include <QPropertyAnimation>
15 #include <QScrollBar>
16 #include <QStyle>
17 #include <QWheelEvent>
18
19 KItemListSmoothScroller::KItemListSmoothScroller(QScrollBar *scrollBar, QObject *parent)
20 : QObject(parent)
21 , m_scrollBarPressed(false)
22 , m_smoothScrolling(true)
23 , m_scrollBar(scrollBar)
24 , m_animation(nullptr)
25 {
26 m_animation = new QPropertyAnimation(this);
27
28 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals);
29 KConfigGroup configGroup(globalConfig, QStringLiteral("KDE"));
30 updateAnimationDuration(configGroup.readEntry("SmoothScroll", true));
31
32 QDBusConnection::sessionBus().connect(QString(),
33 QStringLiteral("/SmoothScroll"),
34 QStringLiteral("org.kde.SmoothScroll"),
35 QStringLiteral("notifyChange"),
36 this,
37 SLOT(updateAnimationDuration(bool)));
38 connect(m_animation, &QPropertyAnimation::stateChanged, this, &KItemListSmoothScroller::slotAnimationStateChanged);
39
40 m_scrollBar->installEventFilter(this);
41 }
42
43 KItemListSmoothScroller::~KItemListSmoothScroller()
44 {
45 }
46
47 void KItemListSmoothScroller::setScrollBar(QScrollBar *scrollBar)
48 {
49 m_scrollBar = scrollBar;
50 }
51
52 QScrollBar *KItemListSmoothScroller::scrollBar() const
53 {
54 return m_scrollBar;
55 }
56
57 void KItemListSmoothScroller::setTargetObject(QObject *target)
58 {
59 if (m_animation->state() == QAbstractAnimation::Running) {
60 m_animation->stop();
61 }
62 m_animation->setTargetObject(target);
63 }
64
65 QObject *KItemListSmoothScroller::targetObject() const
66 {
67 return m_animation->targetObject();
68 }
69
70 void KItemListSmoothScroller::setPropertyName(const QByteArray &propertyName)
71 {
72 if (m_animation->state() == QAbstractAnimation::Running) {
73 m_animation->stop();
74 }
75 m_animation->setPropertyName(propertyName);
76 }
77
78 QByteArray KItemListSmoothScroller::propertyName() const
79 {
80 return m_animation->propertyName();
81 }
82
83 void KItemListSmoothScroller::scrollContentsBy(qreal distance)
84 {
85 QObject *target = targetObject();
86 if (!target) {
87 return;
88 }
89
90 const QByteArray name = propertyName();
91 const qreal currentOffset = target->property(name).toReal();
92 if (static_cast<int>(currentOffset) == m_scrollBar->value()) {
93 // The current offset is already synchronous to the scrollbar
94 return;
95 }
96
97 const bool animRunning = (m_animation->state() == QAbstractAnimation::Running);
98 if (animRunning) {
99 // Stopping a running animation means skipping the range from the current offset
100 // until the target offset. To prevent skipping of the range the difference
101 // is added to the new target offset.
102 const qreal oldEndOffset = m_animation->endValue().toReal();
103 distance += (currentOffset - oldEndOffset);
104 }
105
106 const qreal endOffset = currentOffset - distance;
107 if (m_smoothScrolling || animRunning) {
108 qreal startOffset = currentOffset;
109 if (animRunning) {
110 // If the animation was running and has been interrupted by assigning a new end-offset
111 // one frame must be added to the start-offset to keep the animation smooth. This also
112 // assures that animation proceeds even in cases where new end-offset are triggered
113 // within a very short timeslots.
114 startOffset += (endOffset - currentOffset) * 1000 / (m_animation->duration() * 60);
115 if (currentOffset < endOffset) {
116 startOffset = qMin(startOffset, endOffset);
117 } else {
118 startOffset = qMax(startOffset, endOffset);
119 }
120 }
121
122 m_animation->stop();
123 m_animation->setStartValue(startOffset);
124 m_animation->setEndValue(endOffset);
125 m_animation->setEasingCurve(animRunning ? QEasingCurve::OutQuad : QEasingCurve::InOutQuad);
126 m_animation->start();
127 target->setProperty(name, startOffset);
128 } else {
129 target->setProperty(name, endOffset);
130 }
131 }
132
133 void KItemListSmoothScroller::scrollTo(qreal position)
134 {
135 int newValue = position;
136 newValue = qBound(0, newValue, m_scrollBar->maximum());
137
138 if (newValue != m_scrollBar->value()) {
139 m_smoothScrolling = true;
140 m_scrollBar->setValue(newValue);
141 }
142 }
143
144 bool KItemListSmoothScroller::requestScrollBarUpdate(int newMaximum)
145 {
146 if (m_animation->state() == QAbstractAnimation::Running) {
147 if (newMaximum == m_scrollBar->maximum()) {
148 // The value has been changed by the animation, no update
149 // of the scrollbars is required as their target state will be
150 // reached with the end of the animation.
151 return false;
152 }
153
154 // The maximum has been changed which indicates that the content
155 // of the view has been changed. Stop the animation in any case and
156 // update the scrollbars immediately.
157 m_animation->stop();
158 }
159 return true;
160 }
161
162 bool KItemListSmoothScroller::eventFilter(QObject *obj, QEvent *event)
163 {
164 Q_ASSERT(obj == m_scrollBar);
165
166 switch (event->type()) {
167 case QEvent::MouseButtonPress:
168 m_scrollBarPressed = true;
169 m_smoothScrolling = true;
170 break;
171
172 case QEvent::MouseButtonRelease:
173 m_scrollBarPressed = false;
174 m_smoothScrolling = false;
175 break;
176
177 case QEvent::Wheel:
178 return false; // we're the ones sending them
179
180 default:
181 break;
182 }
183
184 return QObject::eventFilter(obj, event);
185 }
186
187 void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
188 {
189 Q_UNUSED(oldState)
190 if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) {
191 m_smoothScrolling = false;
192 }
193 if (newState == QAbstractAnimation::Stopped) {
194 Q_EMIT scrollingStopped();
195 }
196 }
197
198 void KItemListSmoothScroller::updateAnimationDuration(bool isSmoothScrollingEnabled)
199 {
200 if (isSmoothScrollingEnabled) {
201 // Breeze sets SH_Widget_Animation_Duration from the KDE global animation speed setting
202 const int animationDuration = m_scrollBar->style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, m_scrollBar);
203 const bool animationEnabled = (animationDuration > 0);
204 m_animation->setDuration(animationEnabled ? animationDuration : 1);
205 } else {
206 m_animation->setDuration(1);
207 }
208 }
209
210 void KItemListSmoothScroller::handleWheelEvent(QWheelEvent *event)
211 {
212 const bool previous = m_smoothScrolling;
213
214 m_smoothScrolling = true;
215
216 QWheelEvent *copy = event->clone();
217 QApplication::sendEvent(m_scrollBar, copy);
218 event->setAccepted(copy->isAccepted());
219
220 m_smoothScrolling = previous;
221 }
222
223 bool KItemListSmoothScroller::isAnimating()
224 {
225 if (m_animation) {
226 return (m_animation->state() == QAbstractAnimation::Running);
227 }
228 return false;
229 }
230
231 #include "moc_kitemlistsmoothscroller.cpp"