1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qstyleanimation_p.h"
5
6#include <qcoreapplication.h>
7#include <qwidget.h>
8#include <qevent.h>
9
10QT_BEGIN_NAMESPACE
11
12static const qreal ScrollBarFadeOutDuration = 200.0;
13static const qreal ScrollBarFadeOutDelay = 450.0;
14
15QStyleAnimation::QStyleAnimation(QObject *target) : QAbstractAnimation(target),
16 _delay(0), _duration(-1), _startTime(QTime::currentTime()), _fps(ThirtyFps), _skip(0)
17{
18}
19
20QStyleAnimation::~QStyleAnimation()
21{
22}
23
24QObject *QStyleAnimation::target() const
25{
26 return parent();
27}
28
29int QStyleAnimation::duration() const
30{
31 return _duration;
32}
33
34void QStyleAnimation::setDuration(int duration)
35{
36 _duration = duration;
37}
38
39int QStyleAnimation::delay() const
40{
41 return _delay;
42}
43
44void QStyleAnimation::setDelay(int delay)
45{
46 _delay = delay;
47}
48
49QTime QStyleAnimation::startTime() const
50{
51 return _startTime;
52}
53
54void QStyleAnimation::setStartTime(QTime time)
55{
56 _startTime = time;
57}
58
59QStyleAnimation::FrameRate QStyleAnimation::frameRate() const
60{
61 return _fps;
62}
63
64void QStyleAnimation::setFrameRate(FrameRate fps)
65{
66 _fps = fps;
67}
68
69void QStyleAnimation::updateTarget()
70{
71 QEvent event(QEvent::StyleAnimationUpdate);
72 event.setAccepted(false);
73 QCoreApplication::sendEvent(receiver: target(), event: &event);
74 if (!event.isAccepted())
75 stop();
76}
77
78void QStyleAnimation::start()
79{
80 _skip = 0;
81 QAbstractAnimation::start(policy: DeleteWhenStopped);
82}
83
84bool QStyleAnimation::isUpdateNeeded() const
85{
86 return currentTime() > _delay;
87}
88
89void QStyleAnimation::updateCurrentTime(int time)
90{
91 if (++_skip >= _fps || time >= duration()) {
92 _skip = 0;
93 if (target() && isUpdateNeeded())
94 updateTarget();
95 }
96}
97
98QProgressStyleAnimation::QProgressStyleAnimation(int speed, QObject *target) :
99 QStyleAnimation(target), _speed(speed), _step(-1)
100{
101}
102
103int QProgressStyleAnimation::animationStep() const
104{
105 return currentTime() / (1000.0 / _speed);
106}
107
108int QProgressStyleAnimation::progressStep(int width) const
109{
110 int step = animationStep();
111 int progress = (step * width / _speed) % width;
112 if (((step * width / _speed) % (2 * width)) >= width)
113 progress = width - progress;
114 return progress;
115}
116
117int QProgressStyleAnimation::speed() const
118{
119 return _speed;
120}
121
122void QProgressStyleAnimation::setSpeed(int speed)
123{
124 _speed = speed;
125}
126
127bool QProgressStyleAnimation::isUpdateNeeded() const
128{
129 if (QStyleAnimation::isUpdateNeeded()) {
130 int current = animationStep();
131 if (_step == -1 || _step != current)
132 {
133 _step = current;
134 return true;
135 }
136 }
137 return false;
138}
139
140QNumberStyleAnimation::QNumberStyleAnimation(QObject *target) :
141 QStyleAnimation(target), _start(0.0), _end(1.0), _prev(0.0)
142{
143 setDuration(250);
144}
145
146qreal QNumberStyleAnimation::startValue() const
147{
148 return _start;
149}
150
151void QNumberStyleAnimation::setStartValue(qreal value)
152{
153 _start = value;
154}
155
156qreal QNumberStyleAnimation::endValue() const
157{
158 return _end;
159}
160
161void QNumberStyleAnimation::setEndValue(qreal value)
162{
163 _end = value;
164}
165
166qreal QNumberStyleAnimation::currentValue() const
167{
168 qreal step = qreal(currentTime() - delay()) / (duration() - delay());
169 return _start + qMax(a: qreal(0), b: step) * (_end - _start);
170}
171
172bool QNumberStyleAnimation::isUpdateNeeded() const
173{
174 if (QStyleAnimation::isUpdateNeeded()) {
175 qreal current = currentValue();
176 if (!qFuzzyCompare(p1: _prev, p2: current))
177 {
178 _prev = current;
179 return true;
180 }
181 }
182 return false;
183}
184
185QBlendStyleAnimation::QBlendStyleAnimation(Type type, QObject *target) :
186 QStyleAnimation(target), _type(type)
187{
188 setDuration(250);
189}
190
191QImage QBlendStyleAnimation::startImage() const
192{
193 return _start;
194}
195
196void QBlendStyleAnimation::setStartImage(const QImage& image)
197{
198 _start = image;
199}
200
201QImage QBlendStyleAnimation::endImage() const
202{
203 return _end;
204}
205
206void QBlendStyleAnimation::setEndImage(const QImage& image)
207{
208 _end = image;
209}
210
211QImage QBlendStyleAnimation::currentImage() const
212{
213 return _current;
214}
215
216/*! \internal
217
218 A helper function to blend two images.
219
220 The result consists of ((alpha)*startImage) + ((1-alpha)*endImage)
221
222*/
223static QImage blendedImage(const QImage &start, const QImage &end, float alpha)
224{
225 if (start.isNull() || end.isNull())
226 return QImage();
227
228 QImage blended;
229 const int a = qRound(f: alpha*256);
230 const int ia = 256 - a;
231 const int sw = start.width();
232 const int sh = start.height();
233 const qsizetype bpl = start.bytesPerLine();
234 switch (start.depth()) {
235 case 32:
236 {
237 blended = QImage(sw, sh, start.format());
238 blended.setDevicePixelRatio(start.devicePixelRatio());
239 uchar *mixed_data = blended.bits();
240 const uchar *back_data = start.bits();
241 const uchar *front_data = end.bits();
242 for (int sy = 0; sy < sh; sy++) {
243 quint32* mixed = (quint32*)mixed_data;
244 const quint32* back = (const quint32*)back_data;
245 const quint32* front = (const quint32*)front_data;
246 for (int sx = 0; sx < sw; sx++) {
247 quint32 bp = back[sx];
248 quint32 fp = front[sx];
249 mixed[sx] = qRgba (r: (qRed(rgb: bp)*ia + qRed(rgb: fp)*a)>>8,
250 g: (qGreen(rgb: bp)*ia + qGreen(rgb: fp)*a)>>8,
251 b: (qBlue(rgb: bp)*ia + qBlue(rgb: fp)*a)>>8,
252 a: (qAlpha(rgb: bp)*ia + qAlpha(rgb: fp)*a)>>8);
253 }
254 mixed_data += bpl;
255 back_data += bpl;
256 front_data += bpl;
257 }
258 }
259 default:
260 break;
261 }
262 return blended;
263}
264
265void QBlendStyleAnimation::updateCurrentTime(int time)
266{
267 QStyleAnimation::updateCurrentTime(time);
268
269 float alpha = 1.0;
270 if (duration() > 0) {
271 if (_type == Pulse) {
272 time = time % duration() * 2;
273 if (time > duration())
274 time = duration() * 2 - time;
275 }
276
277 alpha = time / static_cast<float>(duration());
278
279 if (_type == Transition && time > duration()) {
280 alpha = 1.0;
281 stop();
282 }
283 } else if (time > 0) {
284 stop();
285 }
286
287 _current = blendedImage(start: _start, end: _end, alpha);
288}
289
290QScrollbarStyleAnimation::QScrollbarStyleAnimation(Mode mode, QObject *target) : QNumberStyleAnimation(target), _mode(mode), _active(false)
291{
292 switch (mode) {
293 case Activating:
294 setDuration(ScrollBarFadeOutDuration);
295 setStartValue(0.0);
296 setEndValue(1.0);
297 break;
298 case Deactivating:
299 setDuration(ScrollBarFadeOutDelay + ScrollBarFadeOutDuration);
300 setDelay(ScrollBarFadeOutDelay);
301 setStartValue(1.0);
302 setEndValue(0.0);
303 break;
304 }
305}
306
307QScrollbarStyleAnimation::Mode QScrollbarStyleAnimation::mode() const
308{
309 return _mode;
310}
311
312bool QScrollbarStyleAnimation::wasActive() const
313{
314 return _active;
315}
316
317void QScrollbarStyleAnimation::setActive(bool active)
318{
319 _active = active;
320}
321
322void QScrollbarStyleAnimation::updateCurrentTime(int time)
323{
324 QNumberStyleAnimation::updateCurrentTime(time);
325 if (_mode == Deactivating && qFuzzyIsNull(d: currentValue()))
326 target()->setProperty(name: "visible", value: false);
327}
328
329QT_END_NAMESPACE
330
331#include "moc_qstyleanimation_p.cpp"
332

source code of qtbase/src/widgets/styles/qstyleanimation.cpp