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 | |
46 | QT_BEGIN_NAMESPACE |
47 | |
48 | static const qreal ScrollBarFadeOutDuration = 200.0; |
49 | static const qreal ScrollBarFadeOutDelay = 450.0; |
50 | |
51 | QStyleAnimation::QStyleAnimation(QObject *target) : QAbstractAnimation(target), |
52 | _delay(0), _duration(-1), _startTime(QTime::currentTime()), _fps(ThirtyFps), _skip(0) |
53 | { |
54 | } |
55 | |
56 | QStyleAnimation::~QStyleAnimation() |
57 | { |
58 | } |
59 | |
60 | QObject *QStyleAnimation::target() const |
61 | { |
62 | return parent(); |
63 | } |
64 | |
65 | int QStyleAnimation::duration() const |
66 | { |
67 | return _duration; |
68 | } |
69 | |
70 | void QStyleAnimation::setDuration(int duration) |
71 | { |
72 | _duration = duration; |
73 | } |
74 | |
75 | int QStyleAnimation::delay() const |
76 | { |
77 | return _delay; |
78 | } |
79 | |
80 | void QStyleAnimation::setDelay(int delay) |
81 | { |
82 | _delay = delay; |
83 | } |
84 | |
85 | QTime QStyleAnimation::startTime() const |
86 | { |
87 | return _startTime; |
88 | } |
89 | |
90 | void QStyleAnimation::setStartTime(QTime time) |
91 | { |
92 | _startTime = time; |
93 | } |
94 | |
95 | QStyleAnimation::FrameRate QStyleAnimation::frameRate() const |
96 | { |
97 | return _fps; |
98 | } |
99 | |
100 | void QStyleAnimation::setFrameRate(FrameRate fps) |
101 | { |
102 | _fps = fps; |
103 | } |
104 | |
105 | void 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 | |
114 | void QStyleAnimation::start() |
115 | { |
116 | _skip = 0; |
117 | QAbstractAnimation::start(DeleteWhenStopped); |
118 | } |
119 | |
120 | bool QStyleAnimation::isUpdateNeeded() const |
121 | { |
122 | return currentTime() > _delay; |
123 | } |
124 | |
125 | void QStyleAnimation::updateCurrentTime(int) |
126 | { |
127 | if (++_skip >= _fps) { |
128 | _skip = 0; |
129 | if (target() && isUpdateNeeded()) |
130 | updateTarget(); |
131 | } |
132 | } |
133 | |
134 | QProgressStyleAnimation::QProgressStyleAnimation(int speed, QObject *target) : |
135 | QStyleAnimation(target), _speed(speed), _step(-1) |
136 | { |
137 | } |
138 | |
139 | int QProgressStyleAnimation::animationStep() const |
140 | { |
141 | return currentTime() / (1000.0 / _speed); |
142 | } |
143 | |
144 | int 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 | |
153 | int QProgressStyleAnimation::speed() const |
154 | { |
155 | return _speed; |
156 | } |
157 | |
158 | void QProgressStyleAnimation::setSpeed(int speed) |
159 | { |
160 | _speed = speed; |
161 | } |
162 | |
163 | bool 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 | |
176 | QNumberStyleAnimation::QNumberStyleAnimation(QObject *target) : |
177 | QStyleAnimation(target), _start(0.0), _end(1.0), _prev(0.0) |
178 | { |
179 | setDuration(250); |
180 | } |
181 | |
182 | qreal QNumberStyleAnimation::startValue() const |
183 | { |
184 | return _start; |
185 | } |
186 | |
187 | void QNumberStyleAnimation::setStartValue(qreal value) |
188 | { |
189 | _start = value; |
190 | } |
191 | |
192 | qreal QNumberStyleAnimation::endValue() const |
193 | { |
194 | return _end; |
195 | } |
196 | |
197 | void QNumberStyleAnimation::setEndValue(qreal value) |
198 | { |
199 | _end = value; |
200 | } |
201 | |
202 | qreal QNumberStyleAnimation::currentValue() const |
203 | { |
204 | qreal step = qreal(currentTime() - delay()) / (duration() - delay()); |
205 | return _start + qMax(qreal(0), step) * (_end - _start); |
206 | } |
207 | |
208 | bool 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 | |
221 | QBlendStyleAnimation::QBlendStyleAnimation(Type type, QObject *target) : |
222 | QStyleAnimation(target), _type(type) |
223 | { |
224 | setDuration(250); |
225 | } |
226 | |
227 | QImage QBlendStyleAnimation::startImage() const |
228 | { |
229 | return _start; |
230 | } |
231 | |
232 | void QBlendStyleAnimation::setStartImage(const QImage& image) |
233 | { |
234 | _start = image; |
235 | } |
236 | |
237 | QImage QBlendStyleAnimation::endImage() const |
238 | { |
239 | return _end; |
240 | } |
241 | |
242 | void QBlendStyleAnimation::setEndImage(const QImage& image) |
243 | { |
244 | _end = image; |
245 | } |
246 | |
247 | QImage 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 | */ |
259 | static 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 | |
301 | void 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 | |
326 | QScrollbarStyleAnimation::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 | |
343 | QScrollbarStyleAnimation::Mode QScrollbarStyleAnimation::mode() const |
344 | { |
345 | return _mode; |
346 | } |
347 | |
348 | bool QScrollbarStyleAnimation::wasActive() const |
349 | { |
350 | return _active; |
351 | } |
352 | |
353 | void QScrollbarStyleAnimation::setActive(bool active) |
354 | { |
355 | _active = active; |
356 | } |
357 | |
358 | void QScrollbarStyleAnimation::updateCurrentTime(int time) |
359 | { |
360 | QNumberStyleAnimation::updateCurrentTime(time); |
361 | if (_mode == Deactivating && qFuzzyIsNull(currentValue())) |
362 | target()->setProperty("visible" , false); |
363 | } |
364 | |
365 | QT_END_NAMESPACE |
366 | |
367 | #include "moc_qstyleanimation_p.cpp" |
368 | |