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