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