2 * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
4 * SPDX-License-Identifier: GPL-2.0-or-later
7 #include "kitemlistsmoothscroller.h"
9 #include <KConfigGroup>
10 #include <KSharedConfig>
12 #include <QApplication>
13 #include <QDBusConnection>
14 #include <QPropertyAnimation>
17 #include <QWheelEvent>
19 KItemListSmoothScroller::KItemListSmoothScroller(QScrollBar
*scrollBar
, QObject
*parent
)
21 , m_scrollBarPressed(false)
22 , m_smoothScrolling(true)
23 , m_scrollBar(scrollBar
)
24 , m_animation(nullptr)
26 m_animation
= new QPropertyAnimation(this);
28 KSharedConfig::Ptr globalConfig
= KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals
);
29 KConfigGroup
configGroup(globalConfig
, QStringLiteral("KDE"));
30 updateAnimationDuration(configGroup
.readEntry("SmoothScroll", true));
32 QDBusConnection::sessionBus().connect(QString(),
33 QStringLiteral("/SmoothScroll"),
34 QStringLiteral("org.kde.SmoothScroll"),
35 QStringLiteral("notifyChange"),
37 SLOT(updateAnimationDuration(bool)));
38 connect(m_animation
, &QPropertyAnimation::stateChanged
, this, &KItemListSmoothScroller::slotAnimationStateChanged
);
40 m_scrollBar
->installEventFilter(this);
43 KItemListSmoothScroller::~KItemListSmoothScroller()
47 void KItemListSmoothScroller::setScrollBar(QScrollBar
*scrollBar
)
49 m_scrollBar
= scrollBar
;
52 QScrollBar
*KItemListSmoothScroller::scrollBar() const
57 void KItemListSmoothScroller::setTargetObject(QObject
*target
)
59 if (m_animation
->state() == QAbstractAnimation::Running
) {
62 m_animation
->setTargetObject(target
);
65 QObject
*KItemListSmoothScroller::targetObject() const
67 return m_animation
->targetObject();
70 void KItemListSmoothScroller::setPropertyName(const QByteArray
&propertyName
)
72 if (m_animation
->state() == QAbstractAnimation::Running
) {
75 m_animation
->setPropertyName(propertyName
);
78 QByteArray
KItemListSmoothScroller::propertyName() const
80 return m_animation
->propertyName();
83 void KItemListSmoothScroller::scrollContentsBy(qreal distance
)
85 QObject
*target
= targetObject();
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
97 const bool animRunning
= (m_animation
->state() == QAbstractAnimation::Running
);
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
);
106 const qreal endOffset
= currentOffset
- distance
;
107 if (m_smoothScrolling
|| animRunning
) {
108 qreal startOffset
= currentOffset
;
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
);
118 startOffset
= qMax(startOffset
, endOffset
);
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
);
129 target
->setProperty(name
, endOffset
);
133 void KItemListSmoothScroller::scrollTo(qreal position
)
135 int newValue
= position
;
136 newValue
= qBound(0, newValue
, m_scrollBar
->maximum());
138 if (newValue
!= m_scrollBar
->value()) {
139 m_smoothScrolling
= true;
140 m_scrollBar
->setValue(newValue
);
144 bool KItemListSmoothScroller::requestScrollBarUpdate(int newMaximum
)
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.
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.
162 bool KItemListSmoothScroller::eventFilter(QObject
*obj
, QEvent
*event
)
164 Q_ASSERT(obj
== m_scrollBar
);
166 switch (event
->type()) {
167 case QEvent::MouseButtonPress
:
168 m_scrollBarPressed
= true;
169 m_smoothScrolling
= true;
172 case QEvent::MouseButtonRelease
:
173 m_scrollBarPressed
= false;
174 m_smoothScrolling
= false;
178 return false; // we're the ones sending them
184 return QObject::eventFilter(obj
, event
);
187 void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::State newState
, QAbstractAnimation::State oldState
)
190 if (newState
== QAbstractAnimation::Stopped
&& m_smoothScrolling
&& !m_scrollBarPressed
) {
191 m_smoothScrolling
= false;
193 if (newState
== QAbstractAnimation::Stopped
) {
194 Q_EMIT
scrollingStopped();
198 void KItemListSmoothScroller::updateAnimationDuration(bool isSmoothScrollingEnabled
)
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);
206 m_animation
->setDuration(1);
210 void KItemListSmoothScroller::handleWheelEvent(QWheelEvent
*event
)
212 const bool previous
= m_smoothScrolling
;
214 m_smoothScrolling
= true;
216 QWheelEvent
*copy
= event
->clone();
217 QApplication::sendEvent(m_scrollBar
, copy
);
218 event
->setAccepted(copy
->isAccepted());
220 m_smoothScrolling
= previous
;
223 bool KItemListSmoothScroller::isAnimating()
226 return (m_animation
->state() == QAbstractAnimation::Running
);
231 #include "moc_kitemlistsmoothscroller.cpp"