1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qstyleanimation_p.h"
41
42#include <qcoreapplication.h>
43#include <qwidget.h>
44#include <qevent.h>
45
46QT_BEGIN_NAMESPACE
47
48static const qreal ScrollBarFadeOutDuration = 200.0;
49static const qreal ScrollBarFadeOutDelay = 450.0;
50
51QStyleAnimation::QStyleAnimation(QObject *target) : QAbstractAnimation(target),
52 _delay(0), _duration(-1), _startTime(QTime::currentTime()), _fps(ThirtyFps), _skip(0)
53{
54}
55
56QStyleAnimation::~QStyleAnimation()
57{
58}
59
60QObject *QStyleAnimation::target() const
61{
62 return parent();
63}
64
65int QStyleAnimation::duration() const
66{
67 return _duration;
68}
69
70void QStyleAnimation::setDuration(int duration)
71{
72 _duration = duration;
73}
74
75int QStyleAnimation::delay() const
76{
77 return _delay;
78}
79
80void QStyleAnimation::setDelay(int delay)
81{
82 _delay = delay;
83}
84
85QTime QStyleAnimation::startTime() const
86{
87 return _startTime;
88}
89
90void QStyleAnimation::setStartTime(QTime time)
91{
92 _startTime = time;
93}
94
95QStyleAnimation::FrameRate QStyleAnimation::frameRate() const
96{
97 return _fps;
98}
99
100void QStyleAnimation::setFrameRate(FrameRate fps)
101{
102 _fps = fps;
103}
104
105void QStyleAnimation::updateTarget()
106{
107 QEvent event(QEvent::StyleAnimationUpdate);
108 event.setAccepted(false);
109 QCoreApplication::sendEvent(target(), &event);
110 if (!event.isAccepted())
111 stop();
112}
113
114void QStyleAnimation::start()
115{
116 _skip = 0;
117 QAbstractAnimation::start(DeleteWhenStopped);
118}
119
120bool QStyleAnimation::isUpdateNeeded() const
121{
122 return currentTime() > _delay;
123}
124
125void QStyleAnimation::updateCurrentTime(int)
126{
127 if (++_skip >= _fps) {
128 _skip = 0;
129 if (target() && isUpdateNeeded())
130 updateTarget();
131 }
132}
133
134QProgressStyleAnimation::QProgressStyleAnimation(int speed, QObject *target) :
135 QStyleAnimation(target), _speed(speed), _step(-1)
136{
137}
138
139int QProgressStyleAnimation::animationStep() const
140{
141 return currentTime() / (1000.0 / _speed);
142}
143
144int QProgressStyleAnimation::progressStep(int width) const
145{
146 int step = animationStep();
147 int progress = (step * width / _speed) % width;
148 if (((step * width / _speed) % (2 * width)) >= width)
149 progress = width - progress;
150 return progress;
151}
152
153int QProgressStyleAnimation::speed() const
154{
155 return _speed;
156}
157
158void QProgressStyleAnimation::setSpeed(int speed)
159{
160 _speed = speed;
161}
162
163bool QProgressStyleAnimation::isUpdateNeeded() const
164{
165 if (QStyleAnimation::isUpdateNeeded()) {
166 int current = animationStep();
167 if (_step == -1 || _step != current)
168 {
169 _step = current;
170 return true;
171 }
172 }
173 return false;
174}
175
176QNumberStyleAnimation::QNumberStyleAnimation(QObject *target) :
177 QStyleAnimation(target), _start(0.0), _end(1.0), _prev(0.0)
178{
179 setDuration(250);
180}
181
182qreal QNumberStyleAnimation::startValue() const
183{
184 return _start;
185}
186
187void QNumberStyleAnimation::setStartValue(qreal value)
188{
189 _start = value;
190}
191
192qreal QNumberStyleAnimation::endValue() const
193{
194 return _end;
195}
196
197void QNumberStyleAnimation::setEndValue(qreal value)
198{
199 _end = value;
200}
201
202qreal QNumberStyleAnimation::currentValue() const
203{
204 qreal step = qreal(currentTime() - delay()) / (duration() - delay());
205 return _start + qMax(qreal(0), step) * (_end - _start);
206}
207
208bool QNumberStyleAnimation::isUpdateNeeded() const
209{
210 if (QStyleAnimation::isUpdateNeeded()) {
211 qreal current = currentValue();
212 if (!qFuzzyCompare(_prev, current))
213 {
214 _prev = current;
215 return true;
216 }
217 }
218 return false;
219}
220
221QBlendStyleAnimation::QBlendStyleAnimation(Type type, QObject *target) :
222 QStyleAnimation(target), _type(type)
223{
224 setDuration(250);
225}
226
227QImage QBlendStyleAnimation::startImage() const
228{
229 return _start;
230}
231
232void QBlendStyleAnimation::setStartImage(const QImage& image)
233{
234 _start = image;
235}
236
237QImage QBlendStyleAnimation::endImage() const
238{
239 return _end;
240}
241
242void QBlendStyleAnimation::setEndImage(const QImage& image)
243{
244 _end = image;
245}
246
247QImage QBlendStyleAnimation::currentImage() const
248{
249 return _current;
250}
251
252/*! \internal
253
254 A helper function to blend two images.
255
256 The result consists of ((alpha)*startImage) + ((1-alpha)*endImage)
257
258*/
259static QImage blendedImage(const QImage &start, const QImage &end, float alpha)
260{
261 if (start.isNull() || end.isNull())
262 return QImage();
263
264 QImage blended;
265 const int a = qRound(alpha*256);
266 const int ia = 256 - a;
267 const int sw = start.width();
268 const int sh = start.height();
269 const qsizetype bpl = start.bytesPerLine();
270 switch (start.depth()) {
271 case 32:
272 {
273 blended = QImage(sw, sh, start.format());
274 blended.setDevicePixelRatio(start.devicePixelRatio());
275 uchar *mixed_data = blended.bits();
276 const uchar *back_data = start.bits();
277 const uchar *front_data = end.bits();
278 for (int sy = 0; sy < sh; sy++) {
279 quint32* mixed = (quint32*)mixed_data;
280 const quint32* back = (const quint32*)back_data;
281 const quint32* front = (const quint32*)front_data;
282 for (int sx = 0; sx < sw; sx++) {
283 quint32 bp = back[sx];
284 quint32 fp = front[sx];
285 mixed[sx] = qRgba ((qRed(bp)*ia + qRed(fp)*a)>>8,
286 (qGreen(bp)*ia + qGreen(fp)*a)>>8,
287 (qBlue(bp)*ia + qBlue(fp)*a)>>8,
288 (qAlpha(bp)*ia + qAlpha(fp)*a)>>8);
289 }
290 mixed_data += bpl;
291 back_data += bpl;
292 front_data += bpl;
293 }
294 }
295 default:
296 break;
297 }
298 return blended;
299}
300
301void QBlendStyleAnimation::updateCurrentTime(int time)
302{
303 QStyleAnimation::updateCurrentTime(time);
304
305 float alpha = 1.0;
306 if (duration() > 0) {
307 if (_type == Pulse) {
308 time = time % duration() * 2;
309 if (time > duration())
310 time = duration() * 2 - time;
311 }
312
313 alpha = time / static_cast<float>(duration());
314
315 if (_type == Transition && time > duration()) {
316 alpha = 1.0;
317 stop();
318 }
319 } else if (time > 0) {
320 stop();
321 }
322
323 _current = blendedImage(_start, _end, alpha);
324}
325
326QScrollbarStyleAnimation::QScrollbarStyleAnimation(Mode mode, QObject *target) : QNumberStyleAnimation(target), _mode(mode), _active(false)
327{
328 switch (mode) {
329 case Activating:
330 setDuration(ScrollBarFadeOutDuration);
331 setStartValue(0.0);
332 setEndValue(1.0);
333 break;
334 case Deactivating:
335 setDuration(ScrollBarFadeOutDelay + ScrollBarFadeOutDuration);
336 setDelay(ScrollBarFadeOutDelay);
337 setStartValue(1.0);
338 setEndValue(0.0);
339 break;
340 }
341}
342
343QScrollbarStyleAnimation::Mode QScrollbarStyleAnimation::mode() const
344{
345 return _mode;
346}
347
348bool QScrollbarStyleAnimation::wasActive() const
349{
350 return _active;
351}
352
353void QScrollbarStyleAnimation::setActive(bool active)
354{
355 _active = active;
356}
357
358void QScrollbarStyleAnimation::updateCurrentTime(int time)
359{
360 QNumberStyleAnimation::updateCurrentTime(time);
361 if (_mode == Deactivating && qFuzzyIsNull(currentValue()))
362 target()->setProperty("visible", false);
363}
364
365QT_END_NAMESPACE
366
367#include "moc_qstyleanimation_p.cpp"
368