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