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 <QApplication>
10 #include <QPropertyAnimation>
13 #include <QWheelEvent>
15 KItemListSmoothScroller::KItemListSmoothScroller(QScrollBar
* scrollBar
,
18 m_scrollBarPressed(false),
19 m_smoothScrolling(true),
20 m_scrollBar(scrollBar
),
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);
28 const int animationDuration
= 100;
29 const bool animationEnabled
= m_scrollBar
->style()->styleHint(QStyle::SH_Widget_Animate
, nullptr, m_scrollBar
);
31 m_animation
->setDuration(animationEnabled
? animationDuration
: 1);
32 connect(m_animation
, &QPropertyAnimation::stateChanged
,
33 this, &KItemListSmoothScroller::slotAnimationStateChanged
);
35 m_scrollBar
->installEventFilter(this);
38 KItemListSmoothScroller::~KItemListSmoothScroller()
42 void KItemListSmoothScroller::setScrollBar(QScrollBar
*scrollBar
)
44 m_scrollBar
= scrollBar
;
47 QScrollBar
* KItemListSmoothScroller::scrollBar() const
52 void KItemListSmoothScroller::setTargetObject(QObject
* target
)
54 m_animation
->setTargetObject(target
);
57 QObject
* KItemListSmoothScroller::targetObject() const
59 return m_animation
->targetObject();
62 void KItemListSmoothScroller::setPropertyName(const QByteArray
& propertyName
)
64 m_animation
->setPropertyName(propertyName
);
67 QByteArray
KItemListSmoothScroller::propertyName() const
69 return m_animation
->propertyName();
72 void KItemListSmoothScroller::scrollContentsBy(qreal distance
)
74 QObject
* target
= targetObject();
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
86 const bool animRunning
= (m_animation
->state() == QAbstractAnimation::Running
);
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
);
95 const qreal endOffset
= currentOffset
- distance
;
96 if (m_smoothScrolling
|| animRunning
) {
97 qreal startOffset
= currentOffset
;
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
);
107 startOffset
= qMax(startOffset
, endOffset
);
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
);
118 target
->setProperty(name
, endOffset
);
122 void KItemListSmoothScroller::scrollTo(qreal position
)
124 int newValue
= position
;
125 newValue
= qBound(0, newValue
, m_scrollBar
->maximum());
127 if (newValue
!= m_scrollBar
->value()) {
128 m_smoothScrolling
= true;
129 m_scrollBar
->setValue(newValue
);
133 bool KItemListSmoothScroller::requestScrollBarUpdate(int newMaximum
)
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.
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.
151 bool KItemListSmoothScroller::eventFilter(QObject
* obj
, QEvent
* event
)
153 Q_ASSERT(obj
== m_scrollBar
);
155 switch (event
->type()) {
156 case QEvent::MouseButtonPress
:
157 m_scrollBarPressed
= true;
158 m_smoothScrolling
= true;
161 case QEvent::MouseButtonRelease
:
162 m_scrollBarPressed
= false;
163 m_smoothScrolling
= false;
167 return false; // we're the ones sending them
173 return QObject::eventFilter(obj
, event
);
176 void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::State newState
,
177 QAbstractAnimation::State oldState
)
180 if (newState
== QAbstractAnimation::Stopped
&& m_smoothScrolling
&& !m_scrollBarPressed
) {
181 m_smoothScrolling
= false;
185 void KItemListSmoothScroller::handleWheelEvent(QWheelEvent
* event
)
187 const bool previous
= m_smoothScrolling
;
189 m_smoothScrolling
= true;
191 QWheelEvent copy
= *event
;
192 QApplication::sendEvent(m_scrollBar
, ©
);
193 event
->setAccepted(copy
.isAccepted());
195 m_smoothScrolling
= previous
;