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