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 QtCore 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 "qtimeline.h" |
41 | |
42 | #include <private/qobject_p.h> |
43 | #include <QtCore/qcoreevent.h> |
44 | #include <QtCore/qmath.h> |
45 | #include <QtCore/qelapsedtimer.h> |
46 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | class QTimeLinePrivate : public QObjectPrivate |
50 | { |
51 | Q_DECLARE_PUBLIC(QTimeLine) |
52 | public: |
53 | inline QTimeLinePrivate() |
54 | : easingCurve(QEasingCurve::InOutSine), |
55 | startTime(0), duration(1000), startFrame(0), endFrame(0), |
56 | updateInterval(1000 / 25), |
57 | totalLoopCount(1), currentLoopCount(0), currentTime(0), timerId(0), |
58 | direction(QTimeLine::Forward), |
59 | state(QTimeLine::NotRunning) |
60 | { } |
61 | |
62 | QElapsedTimer timer; |
63 | QEasingCurve easingCurve; |
64 | |
65 | int startTime; |
66 | int duration; |
67 | int startFrame; |
68 | int endFrame; |
69 | int updateInterval; |
70 | int totalLoopCount; |
71 | int currentLoopCount; |
72 | |
73 | int currentTime; |
74 | int timerId; |
75 | |
76 | QTimeLine::Direction direction; |
77 | QTimeLine::State state; |
78 | inline void setState(QTimeLine::State newState) |
79 | { |
80 | Q_Q(QTimeLine); |
81 | if (newState != state) |
82 | emit q->stateChanged(newState: state = newState, QTimeLine::QPrivateSignal()); |
83 | } |
84 | |
85 | void setCurrentTime(int msecs); |
86 | }; |
87 | |
88 | /*! |
89 | \internal |
90 | */ |
91 | void QTimeLinePrivate::setCurrentTime(int msecs) |
92 | { |
93 | Q_Q(QTimeLine); |
94 | |
95 | qreal lastValue = q->currentValue(); |
96 | int lastFrame = q->currentFrame(); |
97 | |
98 | // Determine if we are looping. |
99 | int elapsed = (direction == QTimeLine::Backward) ? (-msecs + duration) : msecs; |
100 | int loopCount = elapsed / duration; |
101 | |
102 | bool looping = (loopCount != currentLoopCount); |
103 | #ifdef QTIMELINE_DEBUG |
104 | qDebug() << "QTimeLinePrivate::setCurrentTime:" << msecs << duration << "with loopCount" << loopCount |
105 | << "currentLoopCount" << currentLoopCount |
106 | << "looping" << looping; |
107 | #endif |
108 | if (looping) |
109 | currentLoopCount = loopCount; |
110 | |
111 | // Normalize msecs to be between 0 and duration, inclusive. |
112 | currentTime = elapsed % duration; |
113 | if (direction == QTimeLine::Backward) |
114 | currentTime = duration - currentTime; |
115 | |
116 | // Check if we have reached the end of loopcount. |
117 | bool finished = false; |
118 | if (totalLoopCount && currentLoopCount >= totalLoopCount) { |
119 | finished = true; |
120 | currentTime = (direction == QTimeLine::Backward) ? 0 : duration; |
121 | currentLoopCount = totalLoopCount - 1; |
122 | } |
123 | |
124 | int currentFrame = q->frameForTime(msec: currentTime); |
125 | #ifdef QTIMELINE_DEBUG |
126 | qDebug() << "QTimeLinePrivate::setCurrentTime: frameForTime" << currentTime << currentFrame; |
127 | #endif |
128 | if (!qFuzzyCompare(p1: lastValue, p2: q->currentValue())) |
129 | emit q->valueChanged(x: q->currentValue(), QTimeLine::QPrivateSignal()); |
130 | if (lastFrame != currentFrame) { |
131 | const int transitionframe = (direction == QTimeLine::Forward ? endFrame : startFrame); |
132 | if (looping && !finished && transitionframe != currentFrame) { |
133 | #ifdef QTIMELINE_DEBUG |
134 | qDebug("QTimeLinePrivate::setCurrentTime: transitionframe" ); |
135 | #endif |
136 | emit q->frameChanged(transitionframe, QTimeLine::QPrivateSignal()); |
137 | } |
138 | #ifdef QTIMELINE_DEBUG |
139 | else { |
140 | QByteArray reason; |
141 | if (!looping) |
142 | reason += " not looping" ; |
143 | if (finished) { |
144 | if (!reason.isEmpty()) |
145 | reason += " and" ; |
146 | reason += " finished" ; |
147 | } |
148 | if (transitionframe == currentFrame) { |
149 | if (!reason.isEmpty()) |
150 | reason += " and" ; |
151 | reason += " transitionframe is equal to currentFrame: " + QByteArray::number(currentFrame); |
152 | } |
153 | qDebug("QTimeLinePrivate::setCurrentTime: not transitionframe because %s" , reason.constData()); |
154 | } |
155 | #endif |
156 | emit q->frameChanged(currentFrame, QTimeLine::QPrivateSignal()); |
157 | } |
158 | if (finished && state == QTimeLine::Running) { |
159 | q->stop(); |
160 | emit q->finished(QTimeLine::QPrivateSignal()); |
161 | } |
162 | } |
163 | |
164 | /*! |
165 | \class QTimeLine |
166 | \inmodule QtCore |
167 | \brief The QTimeLine class provides a timeline for controlling animations. |
168 | \since 4.2 |
169 | \ingroup animation |
170 | |
171 | It's most commonly used to animate a GUI control by calling a slot |
172 | periodically. You can construct a timeline by passing its duration in |
173 | milliseconds to QTimeLine's constructor. The timeline's duration describes |
174 | for how long the animation will run. Then you set a suitable frame range |
175 | by calling setFrameRange(). Finally connect the frameChanged() signal to a |
176 | suitable slot in the widget you wish to animate (for example, \l {QProgressBar::}{setValue()} |
177 | in QProgressBar). When you proceed to calling start(), QTimeLine will enter |
178 | Running state, and start emitting frameChanged() at regular intervals, |
179 | causing your widget's connected property's value to grow from the lower |
180 | end to the upper and of your frame range, at a steady rate. You can |
181 | specify the update interval by calling setUpdateInterval(). When done, |
182 | QTimeLine enters NotRunning state, and emits finished(). |
183 | |
184 | Example: |
185 | |
186 | \snippet code/src_corelib_tools_qtimeline.cpp 0 |
187 | |
188 | By default the timeline runs once, from its beginning to its end, |
189 | upon which you must call start() again to restart from the beginning. To |
190 | make the timeline loop, you can call setLoopCount(), passing the number of |
191 | times the timeline should run before finishing. The direction can also be |
192 | changed, causing the timeline to run backward, by calling |
193 | setDirection(). You can also pause and unpause the timeline while it's |
194 | running by calling setPaused(). For interactive control, the |
195 | setCurrentTime() function is provided, which sets the time position of the |
196 | time line directly. Although most useful in NotRunning state (e.g., |
197 | connected to a valueChanged() signal in a QSlider), this function can be |
198 | called at any time. |
199 | |
200 | The frame interface is useful for standard widgets, but QTimeLine can be |
201 | used to control any type of animation. The heart of QTimeLine lies in the |
202 | valueForTime() function, which generates a \e value between 0 and 1 for a |
203 | given time. This value is typically used to describe the steps of an |
204 | animation, where 0 is the first step of an animation, and 1 is the last |
205 | step. When running, QTimeLine generates values between 0 and 1 by calling |
206 | valueForTime() and emitting valueChanged(). By default, valueForTime() |
207 | applies an interpolation algorithm to generate these value. You can choose |
208 | from a set of predefined timeline algorithms by calling setEasingCurve(). |
209 | |
210 | Note that, by default, QTimeLine uses QEasingCurve::InOutSine, which |
211 | provides a value that grows slowly, then grows steadily, and finally grows |
212 | slowly. For a custom timeline, you can reimplement valueForTime(), in which |
213 | case QTimeLine's easingCurve property is ignored. |
214 | |
215 | \sa QProgressBar, QProgressDialog |
216 | */ |
217 | |
218 | /*! |
219 | \enum QTimeLine::State |
220 | |
221 | This enum describes the state of the timeline. |
222 | |
223 | \value NotRunning The timeline is not running. This is the initial state |
224 | of QTimeLine, and the state QTimeLine reenters when finished. The current |
225 | time, frame and value remain unchanged until either setCurrentTime() is |
226 | called, or the timeline is started by calling start(). |
227 | |
228 | \value Paused The timeline is paused (i.e., temporarily |
229 | suspended). Calling setPaused(false) will resume timeline activity. |
230 | |
231 | \value Running The timeline is running. While control is in the event |
232 | loop, QTimeLine will update its current time at regular intervals, |
233 | emitting valueChanged() and frameChanged() when appropriate. |
234 | |
235 | \sa state(), stateChanged() |
236 | */ |
237 | |
238 | /*! |
239 | \enum QTimeLine::Direction |
240 | |
241 | This enum describes the direction of the timeline when in \l Running state. |
242 | |
243 | \value Forward The current time of the timeline increases with time (i.e., |
244 | moves from 0 and towards the end / duration). |
245 | |
246 | \value Backward The current time of the timeline decreases with time (i.e., |
247 | moves from the end / duration and towards 0). |
248 | |
249 | \sa setDirection() |
250 | */ |
251 | |
252 | /*! |
253 | \enum QTimeLine::CurveShape |
254 | \obsolete use QEasingCurve instead |
255 | |
256 | This enum describes the shape of QTimeLine's value curve. The default shape |
257 | is EaseInOutCurve. The curve defines the relation between the value and the |
258 | timeline. |
259 | |
260 | \value EaseInCurve Obsolete equivalent of QEasingCurve::InCurve |
261 | \value EaseOutCurve Obsolete equivalent of QEasingCurve::OutCurve |
262 | \value EaseInOutCurve Obsolete equivalent of QEasingCurve::InOutSine |
263 | \value LinearCurve Obsolete equivalent of QEasingCurve::Linear |
264 | \value SineCurve Obsolete equivalent of QEasingCurve::SineCurve |
265 | \value CosineCurve Obsolete equivalent of QEasingCurve::CosineCurve |
266 | |
267 | \sa curveShape, setCurveShape(), easingCurve, QEasingCurve |
268 | */ |
269 | |
270 | /*! |
271 | \fn void QTimeLine::valueChanged(qreal value) |
272 | |
273 | QTimeLine emits this signal at regular intervals when in \l Running state, |
274 | but only if the current value changes. \a value is the current value. \a value is |
275 | a number between 0.0 and 1.0 |
276 | |
277 | \sa QTimeLine::setDuration(), QTimeLine::valueForTime(), QTimeLine::updateInterval |
278 | */ |
279 | |
280 | /*! |
281 | \fn void QTimeLine::frameChanged(int frame) |
282 | |
283 | QTimeLine emits this signal at regular intervals when in \l Running state, |
284 | but only if the current frame changes. \a frame is the current frame number. |
285 | |
286 | \sa QTimeLine::setFrameRange(), QTimeLine::updateInterval |
287 | */ |
288 | |
289 | /*! |
290 | \fn void QTimeLine::stateChanged(QTimeLine::State newState) |
291 | |
292 | This signal is emitted whenever QTimeLine's state changes. The new state |
293 | is \a newState. |
294 | */ |
295 | |
296 | /*! |
297 | \fn void QTimeLine::finished() |
298 | |
299 | This signal is emitted when QTimeLine finishes (i.e., reaches the end of |
300 | its time line), and does not loop. |
301 | */ |
302 | |
303 | /*! |
304 | Constructs a timeline with a duration of \a duration milliseconds. \a |
305 | parent is passed to QObject's constructor. The default duration is 1000 |
306 | milliseconds. |
307 | */ |
308 | QTimeLine::QTimeLine(int duration, QObject *parent) |
309 | : QObject(*new QTimeLinePrivate, parent) |
310 | { |
311 | setDuration(duration); |
312 | } |
313 | |
314 | /*! |
315 | Destroys the timeline. |
316 | */ |
317 | QTimeLine::~QTimeLine() |
318 | { |
319 | Q_D(QTimeLine); |
320 | |
321 | if (d->state == Running) |
322 | stop(); |
323 | } |
324 | |
325 | /*! |
326 | Returns the state of the timeline. |
327 | |
328 | \sa start(), setPaused(), stop() |
329 | */ |
330 | QTimeLine::State QTimeLine::state() const |
331 | { |
332 | Q_D(const QTimeLine); |
333 | return d->state; |
334 | } |
335 | |
336 | /*! |
337 | \property QTimeLine::loopCount |
338 | \brief the number of times the timeline should loop before it's finished. |
339 | |
340 | A loop count of of 0 means that the timeline will loop forever. |
341 | |
342 | By default, this property contains a value of 1. |
343 | */ |
344 | int QTimeLine::loopCount() const |
345 | { |
346 | Q_D(const QTimeLine); |
347 | return d->totalLoopCount; |
348 | } |
349 | void QTimeLine::setLoopCount(int count) |
350 | { |
351 | Q_D(QTimeLine); |
352 | d->totalLoopCount = count; |
353 | } |
354 | |
355 | /*! |
356 | \property QTimeLine::direction |
357 | \brief the direction of the timeline when QTimeLine is in \l Running |
358 | state. |
359 | |
360 | This direction indicates whether the time moves from 0 towards the |
361 | timeline duration, or from the value of the duration and towards 0 after |
362 | start() has been called. |
363 | |
364 | By default, this property is set to \l Forward. |
365 | */ |
366 | QTimeLine::Direction QTimeLine::direction() const |
367 | { |
368 | Q_D(const QTimeLine); |
369 | return d->direction; |
370 | } |
371 | void QTimeLine::setDirection(Direction direction) |
372 | { |
373 | Q_D(QTimeLine); |
374 | d->direction = direction; |
375 | d->startTime = d->currentTime; |
376 | d->timer.start(); |
377 | } |
378 | |
379 | /*! |
380 | \property QTimeLine::duration |
381 | \brief the total duration of the timeline in milliseconds. |
382 | |
383 | By default, this value is 1000 (i.e., 1 second), but you can change this |
384 | by either passing a duration to QTimeLine's constructor, or by calling |
385 | setDuration(). The duration must be larger than 0. |
386 | |
387 | \note Changing the duration does not cause the current time to be reset |
388 | to zero or the new duration. You also need to call setCurrentTime() with |
389 | the desired value. |
390 | */ |
391 | int QTimeLine::duration() const |
392 | { |
393 | Q_D(const QTimeLine); |
394 | return d->duration; |
395 | } |
396 | void QTimeLine::setDuration(int duration) |
397 | { |
398 | Q_D(QTimeLine); |
399 | if (duration <= 0) { |
400 | qWarning(msg: "QTimeLine::setDuration: cannot set duration <= 0" ); |
401 | return; |
402 | } |
403 | d->duration = duration; |
404 | } |
405 | |
406 | /*! |
407 | Returns the start frame, which is the frame corresponding to the start of |
408 | the timeline (i.e., the frame for which the current value is 0). |
409 | |
410 | \sa setStartFrame(), setFrameRange() |
411 | */ |
412 | int QTimeLine::startFrame() const |
413 | { |
414 | Q_D(const QTimeLine); |
415 | return d->startFrame; |
416 | } |
417 | |
418 | /*! |
419 | Sets the start frame, which is the frame corresponding to the start of the |
420 | timeline (i.e., the frame for which the current value is 0), to \a frame. |
421 | |
422 | \sa startFrame(), endFrame(), setFrameRange() |
423 | */ |
424 | void QTimeLine::setStartFrame(int frame) |
425 | { |
426 | Q_D(QTimeLine); |
427 | d->startFrame = frame; |
428 | } |
429 | |
430 | /*! |
431 | Returns the end frame, which is the frame corresponding to the end of the |
432 | timeline (i.e., the frame for which the current value is 1). |
433 | |
434 | \sa setEndFrame(), setFrameRange() |
435 | */ |
436 | int QTimeLine::endFrame() const |
437 | { |
438 | Q_D(const QTimeLine); |
439 | return d->endFrame; |
440 | } |
441 | |
442 | /*! |
443 | Sets the end frame, which is the frame corresponding to the end of the |
444 | timeline (i.e., the frame for which the current value is 1), to \a frame. |
445 | |
446 | \sa endFrame(), startFrame(), setFrameRange() |
447 | */ |
448 | void QTimeLine::setEndFrame(int frame) |
449 | { |
450 | Q_D(QTimeLine); |
451 | d->endFrame = frame; |
452 | } |
453 | |
454 | /*! |
455 | Sets the timeline's frame counter to start at \a startFrame, and end and |
456 | \a endFrame. For each time value, QTimeLine will find the corresponding |
457 | frame when you call currentFrame() or frameForTime() by interpolating, |
458 | using the return value of valueForTime(). |
459 | |
460 | When in Running state, QTimeLine also emits the frameChanged() signal when |
461 | the frame changes. |
462 | |
463 | \sa startFrame(), endFrame(), start(), currentFrame() |
464 | */ |
465 | void QTimeLine::setFrameRange(int startFrame, int endFrame) |
466 | { |
467 | Q_D(QTimeLine); |
468 | d->startFrame = startFrame; |
469 | d->endFrame = endFrame; |
470 | } |
471 | |
472 | /*! |
473 | \property QTimeLine::updateInterval |
474 | \brief the time in milliseconds between each time QTimeLine updates its |
475 | current time. |
476 | |
477 | When updating the current time, QTimeLine will emit valueChanged() if the |
478 | current value changed, and frameChanged() if the frame changed. |
479 | |
480 | By default, the interval is 40 ms, which corresponds to a rate of 25 |
481 | updates per second. |
482 | */ |
483 | int QTimeLine::updateInterval() const |
484 | { |
485 | Q_D(const QTimeLine); |
486 | return d->updateInterval; |
487 | } |
488 | void QTimeLine::setUpdateInterval(int interval) |
489 | { |
490 | Q_D(QTimeLine); |
491 | d->updateInterval = interval; |
492 | } |
493 | |
494 | #if QT_DEPRECATED_SINCE(5, 15) |
495 | /*! |
496 | \property QTimeLine::curveShape |
497 | \brief the shape of the timeline curve. |
498 | |
499 | The curve shape describes the relation between the time and value for the |
500 | base implementation of valueForTime(). |
501 | |
502 | This property is an indirect way to update the easingCurve property; if you |
503 | set both, the one set more recently overrides the other. (If valueForTime() |
504 | is reimplemented it will override both.) |
505 | |
506 | By default, this property is set to \l EaseInOutCurve. |
507 | |
508 | \obsolete Access \c easingCurve instead. |
509 | |
510 | \sa valueForTime(), easingCurve |
511 | */ |
512 | QTimeLine::CurveShape QTimeLine::curveShape() const |
513 | { |
514 | Q_D(const QTimeLine); |
515 | switch (d->easingCurve.type()) { |
516 | default: |
517 | case QEasingCurve::InOutSine: |
518 | return EaseInOutCurve; |
519 | case QEasingCurve::InCurve: |
520 | return EaseInCurve; |
521 | case QEasingCurve::OutCurve: |
522 | return EaseOutCurve; |
523 | case QEasingCurve::Linear: |
524 | return LinearCurve; |
525 | case QEasingCurve::SineCurve: |
526 | return SineCurve; |
527 | case QEasingCurve::CosineCurve: |
528 | return CosineCurve; |
529 | } |
530 | return EaseInOutCurve; |
531 | } |
532 | |
533 | static QEasingCurve::Type convert(QTimeLine::CurveShape shape) |
534 | { |
535 | switch (shape) { |
536 | #define CASE(x, y) case QTimeLine::x: return QEasingCurve::y |
537 | CASE(EaseInOutCurve, InOutSine); |
538 | CASE(EaseInCurve, InCurve); |
539 | CASE(EaseOutCurve, OutCurve); |
540 | CASE(LinearCurve, Linear); |
541 | CASE(SineCurve, SineCurve); |
542 | CASE(CosineCurve, CosineCurve); |
543 | #undef CASE |
544 | } |
545 | Q_UNREACHABLE(); |
546 | } |
547 | |
548 | void QTimeLine::setCurveShape(CurveShape shape) |
549 | { |
550 | setEasingCurve(convert(shape)); |
551 | } |
552 | #endif // 5.15 deprecation |
553 | |
554 | /*! |
555 | \property QTimeLine::easingCurve |
556 | |
557 | \since 4.6 |
558 | |
559 | Specifies the easing curve that the timeline will use. |
560 | If valueForTime() is reimplemented, this value is ignored. |
561 | If both easingCurve and curveShape are set, the last property set will |
562 | override the previous one. |
563 | |
564 | \sa valueForTime() |
565 | */ |
566 | |
567 | QEasingCurve QTimeLine::easingCurve() const |
568 | { |
569 | Q_D(const QTimeLine); |
570 | return d->easingCurve; |
571 | } |
572 | |
573 | void QTimeLine::setEasingCurve(const QEasingCurve& curve) |
574 | { |
575 | Q_D(QTimeLine); |
576 | d->easingCurve = curve; |
577 | } |
578 | |
579 | /*! |
580 | \property QTimeLine::currentTime |
581 | \brief the current time of the time line. |
582 | |
583 | When QTimeLine is in Running state, this value is updated continuously as |
584 | a function of the duration and direction of the timeline. Otherwise, it is |
585 | value that was current when stop() was called last, or the value set by |
586 | setCurrentTime(). |
587 | |
588 | By default, this property contains a value of 0. |
589 | */ |
590 | int QTimeLine::currentTime() const |
591 | { |
592 | Q_D(const QTimeLine); |
593 | return d->currentTime; |
594 | } |
595 | void QTimeLine::setCurrentTime(int msec) |
596 | { |
597 | Q_D(QTimeLine); |
598 | d->startTime = 0; |
599 | d->currentLoopCount = 0; |
600 | d->timer.restart(); |
601 | d->setCurrentTime(msec); |
602 | } |
603 | |
604 | /*! |
605 | Returns the frame corresponding to the current time. |
606 | |
607 | \sa currentTime(), frameForTime(), setFrameRange() |
608 | */ |
609 | int QTimeLine::currentFrame() const |
610 | { |
611 | Q_D(const QTimeLine); |
612 | return frameForTime(msec: d->currentTime); |
613 | } |
614 | |
615 | /*! |
616 | Returns the value corresponding to the current time. |
617 | |
618 | \sa valueForTime(), currentFrame() |
619 | */ |
620 | qreal QTimeLine::currentValue() const |
621 | { |
622 | Q_D(const QTimeLine); |
623 | return valueForTime(msec: d->currentTime); |
624 | } |
625 | |
626 | /*! |
627 | Returns the frame corresponding to the time \a msec. This value is |
628 | calculated using a linear interpolation of the start and end frame, based |
629 | on the value returned by valueForTime(). |
630 | |
631 | \sa valueForTime(), setFrameRange() |
632 | */ |
633 | int QTimeLine::frameForTime(int msec) const |
634 | { |
635 | Q_D(const QTimeLine); |
636 | if (d->direction == Forward) |
637 | return d->startFrame + int((d->endFrame - d->startFrame) * valueForTime(msec)); |
638 | return d->startFrame + qCeil(v: (d->endFrame - d->startFrame) * valueForTime(msec)); |
639 | } |
640 | |
641 | /*! |
642 | Returns the timeline value for the time \a msec. The returned value, which |
643 | varies depending on the curve shape, is always between 0 and 1. If \a msec |
644 | is 0, the default implementation always returns 0. |
645 | |
646 | Reimplement this function to provide a custom curve shape for your |
647 | timeline. |
648 | |
649 | \sa CurveShape, frameForTime() |
650 | */ |
651 | qreal QTimeLine::valueForTime(int msec) const |
652 | { |
653 | Q_D(const QTimeLine); |
654 | msec = qMin(a: qMax(a: msec, b: 0), b: d->duration); |
655 | |
656 | qreal value = msec / qreal(d->duration); |
657 | return d->easingCurve.valueForProgress(progress: value); |
658 | } |
659 | |
660 | /*! |
661 | Starts the timeline. QTimeLine will enter Running state, and once it |
662 | enters the event loop, it will update its current time, frame and value at |
663 | regular intervals. The default interval is 40 ms (i.e., 25 times per |
664 | second). You can change the update interval by calling |
665 | setUpdateInterval(). |
666 | |
667 | The timeline will start from position 0, or the end if going backward. |
668 | If you want to resume a stopped timeline without restarting, you can call |
669 | resume() instead. |
670 | |
671 | \sa resume(), updateInterval(), frameChanged(), valueChanged() |
672 | */ |
673 | void QTimeLine::start() |
674 | { |
675 | Q_D(QTimeLine); |
676 | if (d->timerId) { |
677 | qWarning(msg: "QTimeLine::start: already running" ); |
678 | return; |
679 | } |
680 | int curTime = 0; |
681 | if (d->direction == Backward) |
682 | curTime = d->duration; |
683 | d->timerId = startTimer(interval: d->updateInterval); |
684 | d->startTime = curTime; |
685 | d->currentLoopCount = 0; |
686 | d->timer.start(); |
687 | d->setState(Running); |
688 | d->setCurrentTime(curTime); |
689 | } |
690 | |
691 | /*! |
692 | Resumes the timeline from the current time. QTimeLine will reenter Running |
693 | state, and once it enters the event loop, it will update its current time, |
694 | frame and value at regular intervals. |
695 | |
696 | In contrast to start(), this function does not restart the timeline before |
697 | it resumes. |
698 | |
699 | \sa start(), updateInterval(), frameChanged(), valueChanged() |
700 | */ |
701 | void QTimeLine::resume() |
702 | { |
703 | Q_D(QTimeLine); |
704 | if (d->timerId) { |
705 | qWarning(msg: "QTimeLine::resume: already running" ); |
706 | return; |
707 | } |
708 | d->timerId = startTimer(interval: d->updateInterval); |
709 | d->startTime = d->currentTime; |
710 | d->timer.start(); |
711 | d->setState(Running); |
712 | } |
713 | |
714 | /*! |
715 | Stops the timeline, causing QTimeLine to enter NotRunning state. |
716 | |
717 | \sa start() |
718 | */ |
719 | void QTimeLine::stop() |
720 | { |
721 | Q_D(QTimeLine); |
722 | if (d->timerId) |
723 | killTimer(id: d->timerId); |
724 | d->setState(NotRunning); |
725 | d->timerId = 0; |
726 | } |
727 | |
728 | /*! |
729 | If \a paused is true, the timeline is paused, causing QTimeLine to enter |
730 | Paused state. No updates will be signaled until either start() or |
731 | setPaused(false) is called. If \a paused is false, the timeline is resumed |
732 | and continues where it left. |
733 | |
734 | \sa state(), start() |
735 | */ |
736 | void QTimeLine::setPaused(bool paused) |
737 | { |
738 | Q_D(QTimeLine); |
739 | if (d->state == NotRunning) { |
740 | qWarning(msg: "QTimeLine::setPaused: Not running" ); |
741 | return; |
742 | } |
743 | if (paused && d->state != Paused) { |
744 | d->startTime = d->currentTime; |
745 | killTimer(id: d->timerId); |
746 | d->timerId = 0; |
747 | d->setState(Paused); |
748 | } else if (!paused && d->state == Paused) { |
749 | // Same as resume() |
750 | d->timerId = startTimer(interval: d->updateInterval); |
751 | d->startTime = d->currentTime; |
752 | d->timer.start(); |
753 | d->setState(Running); |
754 | } |
755 | } |
756 | |
757 | /*! |
758 | Toggles the direction of the timeline. If the direction was Forward, it |
759 | becomes Backward, and vice verca. |
760 | |
761 | \sa setDirection() |
762 | */ |
763 | void QTimeLine::toggleDirection() |
764 | { |
765 | Q_D(QTimeLine); |
766 | setDirection(d->direction == Forward ? Backward : Forward); |
767 | } |
768 | |
769 | /*! |
770 | \reimp |
771 | */ |
772 | void QTimeLine::timerEvent(QTimerEvent *event) |
773 | { |
774 | Q_D(QTimeLine); |
775 | if (event->timerId() != d->timerId) { |
776 | event->ignore(); |
777 | return; |
778 | } |
779 | event->accept(); |
780 | |
781 | if (d->direction == Forward) { |
782 | d->setCurrentTime(d->startTime + d->timer.elapsed()); |
783 | } else { |
784 | d->setCurrentTime(d->startTime - d->timer.elapsed()); |
785 | } |
786 | } |
787 | |
788 | QT_END_NAMESPACE |
789 | |
790 | #include "moc_qtimeline.cpp" |
791 | |