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