]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kitemlistsmoothscroller.cpp
cdb7f255b978763ead545ee9901871d6cc191270
[dolphin.git] / src / kitemviews / private / kitemlistsmoothscroller.cpp
1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include "kitemlistsmoothscroller.h"
21
22 #include <QApplication>
23 #include <QPropertyAnimation>
24 #include <QScrollBar>
25 #include <QStyle>
26 #include <QWheelEvent>
27
28 KItemListSmoothScroller::KItemListSmoothScroller(QScrollBar* scrollBar,
29 QObject* parent) :
30 QObject(parent),
31 m_scrollBarPressed(false),
32 m_smoothScrolling(true),
33 m_scrollBar(scrollBar),
34 m_animation(nullptr)
35 {
36 m_animation = new QPropertyAnimation(this);
37 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
38 const int animationDuration = m_scrollBar->style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, m_scrollBar);
39 const bool animationEnabled = (animationDuration > 0);
40 #else
41 const int animationDuration = 100;
42 const bool animationEnabled = m_scrollBar->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, m_scrollBar);
43 #endif
44 m_animation->setDuration(animationEnabled ? animationDuration : 1);
45 connect(m_animation, &QPropertyAnimation::stateChanged,
46 this, &KItemListSmoothScroller::slotAnimationStateChanged);
47
48 m_scrollBar->installEventFilter(this);
49 }
50
51 KItemListSmoothScroller::~KItemListSmoothScroller()
52 {
53 }
54
55 void KItemListSmoothScroller::setScrollBar(QScrollBar *scrollBar)
56 {
57 m_scrollBar = scrollBar;
58 }
59
60 QScrollBar* KItemListSmoothScroller::scrollBar() const
61 {
62 return m_scrollBar;
63 }
64
65 void KItemListSmoothScroller::setTargetObject(QObject* target)
66 {
67 m_animation->setTargetObject(target);
68 }
69
70 QObject* KItemListSmoothScroller::targetObject() const
71 {
72 return m_animation->targetObject();
73 }
74
75 void KItemListSmoothScroller::setPropertyName(const QByteArray& propertyName)
76 {
77 m_animation->setPropertyName(propertyName);
78 }
79
80 QByteArray KItemListSmoothScroller::propertyName() const
81 {
82 return m_animation->propertyName();
83 }
84
85 void KItemListSmoothScroller::scrollContentsBy(qreal distance)
86 {
87 QObject* target = targetObject();
88 if (!target) {
89 return;
90 }
91
92 const QByteArray name = propertyName();
93 const qreal currentOffset = target->property(name).toReal();
94 if (static_cast<int>(currentOffset) == m_scrollBar->value()) {
95 // The current offset is already synchronous to the scrollbar
96 return;
97 }
98
99 const bool animRunning = (m_animation->state() == QAbstractAnimation::Running);
100 if (animRunning) {
101 // Stopping a running animation means skipping the range from the current offset
102 // until the target offset. To prevent skipping of the range the difference
103 // is added to the new target offset.
104 const qreal oldEndOffset = m_animation->endValue().toReal();
105 distance += (currentOffset - oldEndOffset);
106 }
107
108 const qreal endOffset = currentOffset - distance;
109 if (m_smoothScrolling || animRunning) {
110 qreal startOffset = currentOffset;
111 if (animRunning) {
112 // If the animation was running and has been interrupted by assigning a new end-offset
113 // one frame must be added to the start-offset to keep the animation smooth. This also
114 // assures that animation proceeds even in cases where new end-offset are triggered
115 // within a very short timeslots.
116 startOffset += (endOffset - currentOffset) * 1000 / (m_animation->duration() * 60);
117 if (currentOffset < endOffset) {
118 startOffset = qMin(startOffset, endOffset);
119 } else {
120 startOffset = qMax(startOffset, endOffset);
121 }
122 }
123
124 m_animation->stop();
125 m_animation->setStartValue(startOffset);
126 m_animation->setEndValue(endOffset);
127 m_animation->setEasingCurve(animRunning ? QEasingCurve::OutQuad : QEasingCurve::InOutQuad);
128 m_animation->start();
129 target->setProperty(name, startOffset);
130 } else {
131 target->setProperty(name, endOffset);
132 }
133 }
134
135 void KItemListSmoothScroller::scrollTo(qreal position)
136 {
137 int newValue = position;
138 newValue = qBound(0, newValue, m_scrollBar->maximum());
139
140 if (newValue != m_scrollBar->value()) {
141 m_smoothScrolling = true;
142 m_scrollBar->setValue(newValue);
143 }
144 }
145
146 bool KItemListSmoothScroller::requestScrollBarUpdate(int newMaximum)
147 {
148 if (m_animation->state() == QAbstractAnimation::Running) {
149 if (newMaximum == m_scrollBar->maximum()) {
150 // The value has been changed by the animation, no update
151 // of the scrollbars is required as their target state will be
152 // reached with the end of the animation.
153 return false;
154 }
155
156 // The maximum has been changed which indicates that the content
157 // of the view has been changed. Stop the animation in any case and
158 // update the scrollbars immediately.
159 m_animation->stop();
160 }
161 return true;
162 }
163
164 bool KItemListSmoothScroller::eventFilter(QObject* obj, QEvent* event)
165 {
166 Q_ASSERT(obj == m_scrollBar);
167
168 switch (event->type()) {
169 case QEvent::MouseButtonPress:
170 m_scrollBarPressed = true;
171 m_smoothScrolling = true;
172 break;
173
174 case QEvent::MouseButtonRelease:
175 m_scrollBarPressed = false;
176 m_smoothScrolling = false;
177 break;
178
179 case QEvent::Wheel:
180 return false; // we're the ones sending them
181
182 default:
183 break;
184 }
185
186 return QObject::eventFilter(obj, event);
187 }
188
189 void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::State newState,
190 QAbstractAnimation::State oldState)
191 {
192 Q_UNUSED(oldState)
193 if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) {
194 m_smoothScrolling = false;
195 }
196 }
197
198 void KItemListSmoothScroller::handleWheelEvent(QWheelEvent* event)
199 {
200 const bool previous = m_smoothScrolling;
201
202 m_smoothScrolling = true;
203
204 QWheelEvent copy = *event;
205 QApplication::sendEvent(m_scrollBar, &copy);
206 event->setAccepted(copy.isAccepted());
207
208 m_smoothScrolling = previous;
209 }
210