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