1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtCore module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qtimeline.h"
44 #include <private/qobject_p.h>
45 #include <QtCore/qcoreevent.h>
46 #include <QtCore/qmath.h>
47 #include <QtCore/qelapsedtimer.h>
51 class QTimeLinePrivate : public QObjectPrivate
53 Q_DECLARE_PUBLIC(QTimeLine)
55 inline QTimeLinePrivate()
56 : startTime(0), duration(1000), startFrame(0), endFrame(0),
57 updateInterval(1000 / 25),
58 totalLoopCount(1), currentLoopCount(0), currentTime(0), timerId(0),
59 direction(QTimeLine::Forward), easingCurve(QEasingCurve::InOutSine),
60 state(QTimeLine::NotRunning)
75 QTimeLine::Direction direction;
76 QEasingCurve easingCurve;
77 QTimeLine::State state;
78 inline void setState(QTimeLine::State newState)
81 if (newState != state)
82 emit q->stateChanged(state = newState);
85 void setCurrentTime(int msecs);
91 void QTimeLinePrivate::setCurrentTime(int msecs)
95 qreal lastValue = q->currentValue();
96 int lastFrame = q->currentFrame();
98 // Determine if we are looping.
99 int elapsed = (direction == QTimeLine::Backward) ? (-msecs + duration) : msecs;
100 int loopCount = elapsed / duration;
102 bool looping = (loopCount != currentLoopCount);
103 #ifdef QTIMELINE_DEBUG
104 qDebug() << "QTimeLinePrivate::setCurrentTime:" << msecs << duration << "with loopCount" << loopCount
105 << "currentLoopCount" << currentLoopCount
106 << "looping" << looping;
109 currentLoopCount = loopCount;
111 // Normalize msecs to be between 0 and duration, inclusive.
112 currentTime = elapsed % duration;
113 if (direction == QTimeLine::Backward)
114 currentTime = duration - currentTime;
116 // Check if we have reached the end of loopcount.
117 bool finished = false;
118 if (totalLoopCount && currentLoopCount >= totalLoopCount) {
120 currentTime = (direction == QTimeLine::Backward) ? 0 : duration;
121 currentLoopCount = totalLoopCount - 1;
124 int currentFrame = q->frameForTime(currentTime);
125 #ifdef QTIMELINE_DEBUG
126 qDebug() << "QTimeLinePrivate::setCurrentTime: frameForTime" << currentTime << currentFrame;
128 if (!qFuzzyCompare(lastValue, q->currentValue()))
129 emit q->valueChanged(q->currentValue());
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";
136 emit q->frameChanged(transitionframe);
138 #ifdef QTIMELINE_DEBUG
142 reason += " not looping";
144 if (!reason.isEmpty())
146 reason += " finished";
148 if (transitionframe == currentFrame) {
149 if (!reason.isEmpty())
151 reason += " transitionframe is equal to currentFrame: " + QByteArray::number(currentFrame);
153 qDebug("QTimeLinePrivate::setCurrentTime: not transitionframe because %s", reason.constData());
156 emit q->frameChanged(currentFrame);
158 if (finished && state == QTimeLine::Running) {
166 \brief The QTimeLine class provides a timeline for controlling animations.
170 It's most commonly used to animate a GUI control by calling a slot
171 periodically. You can construct a timeline by passing its duration in
172 milliseconds to QTimeLine's constructor. The timeline's duration describes
173 for how long the animation will run. Then you set a suitable frame range
174 by calling setFrameRange(). Finally connect the frameChanged() signal to a
175 suitable slot in the widget you wish to animate (e.g., setValue() in
176 QProgressBar). When you proceed to calling start(), QTimeLine will enter
177 Running state, and start emitting frameChanged() at regular intervals,
178 causing your widget's connected property's value to grow from the lower
179 end to the upper and of your frame range, at a steady rate. You can
180 specify the update interval by calling setUpdateInterval(). When done,
181 QTimeLine enters NotRunning state, and emits finished().
185 \snippet doc/src/snippets/code/src_corelib_tools_qtimeline.cpp 0
187 You can also use QTimeLine with the
188 \l{Graphics View}{Graphics View framework} for
189 animations. The QGraphicsItemAnimation class implements animation
190 of \l{QGraphicsItem}{QGraphicsItems} with a timeline.
192 By default the timeline runs once, from the beginning and towards the end,
193 upon which you must call start() again to restart from the beginning. To
194 make the timeline loop, you can call setLoopCount(), passing the number of
195 times the timeline should run before finishing. The direction can also be
196 changed, causing the timeline to run backward, by calling
197 setDirection(). You can also pause and unpause the timeline while it's
198 running by calling setPaused(). For interactive control, the
199 setCurrentTime() function is provided, which sets the time position of the
200 time line directly. Although most useful in NotRunning state, (e.g.,
201 connected to a valueChanged() signal in a QSlider,) this function can be
204 The frame interface is useful for standard widgets, but QTimeLine can be
205 used to control any type of animation. The heart of QTimeLine lies in the
206 valueForTime() function, which generates a \e value between 0 and 1 for a
207 given time. This value is typically used to describe the steps of an
208 animation, where 0 is the first step of an animation, and 1 is the last
209 step. When running, QTimeLine generates values between 0 and 1 by calling
210 valueForTime() and emitting valueChanged(). By default, valueForTime()
211 applies an interpolation algorithm to generate these value. You can choose
212 from a set of predefined timeline algorithms by calling
215 Note that by default, QTimeLine uses the EaseInOut curve shape,
216 which provides a value that grows slowly, then grows steadily, and
217 finally grows slowly. For a custom timeline, you can reimplement
218 valueForTime(), in which case QTimeLine's curveShape property is ignored.
220 \sa QProgressBar, QProgressDialog, QGraphicsItemAnimation
224 \enum QTimeLine::State
226 This enum describes the state of the timeline.
228 \value NotRunning The timeline is not running. This is the initial state
229 of QTimeLine, and the state QTimeLine reenters when finished. The current
230 time, frame and value remain unchanged until either setCurrentTime() is
231 called, or the timeline is started by calling start().
233 \value Paused The timeline is paused (i.e., temporarily
234 suspended). Calling setPaused(false) will resume timeline activity.
236 \value Running The timeline is running. While control is in the event
237 loop, QTimeLine will update its current time at regular intervals,
238 emitting valueChanged() and frameChanged() when appropriate.
240 \sa state(), stateChanged()
244 \enum QTimeLine::Direction
246 This enum describes the direction of the timeline when in \l Running state.
248 \value Forward The current time of the timeline increases with time (i.e.,
249 moves from 0 and towards the end / duration).
251 \value Backward The current time of the timeline decreases with time (i.e.,
252 moves from the end / duration and towards 0).
258 \enum QTimeLine::CurveShape
260 This enum describes the default shape of QTimeLine's value curve. The
261 default, shape is EaseInOutCurve. The curve defines the relation
262 between the value and the timeline.
264 \value EaseInCurve The value starts growing slowly, then increases in speed.
265 \value EaseOutCurve The value starts growing steadily, then ends slowly.
266 \value EaseInOutCurve The value starts growing slowly, then runs steadily, then grows slowly again.
267 \value LinearCurve The value grows linearly (e.g., if the duration is 1000 ms,
268 the value at time 500 ms is 0.5).
269 \value SineCurve The value grows sinusoidally.
270 \value CosineCurve The value grows cosinusoidally.
276 \fn QTimeLine::valueChanged(qreal value)
278 QTimeLine emits this signal at regular intervals when in \l Running state,
279 but only if the current value changes. \a value is the current value. \a value is
280 a number between 0.0 and 1.0
282 \sa QTimeLine::setDuration(), QTimeLine::valueForTime(), QTimeLine::updateInterval
286 \fn QTimeLine::frameChanged(int frame)
288 QTimeLine emits this signal at regular intervals when in \l Running state,
289 but only if the current frame changes. \a frame is the current frame number.
291 \sa QTimeLine::setFrameRange(), QTimeLine::updateInterval
295 \fn QTimeLine::stateChanged(QTimeLine::State newState)
297 This signal is emitted whenever QTimeLine's state changes. The new state
302 \fn QTimeLine::finished()
304 This signal is emitted when QTimeLine finishes (i.e., reaches the end of
305 its time line), and does not loop.
309 Constructs a timeline with a duration of \a duration milliseconds. \a
310 parent is passed to QObject's constructor. The default duration is 1000
313 QTimeLine::QTimeLine(int duration, QObject *parent)
314 : QObject(*new QTimeLinePrivate, parent)
316 setDuration(duration);
320 Destroys the timeline.
322 QTimeLine::~QTimeLine()
326 if (d->state == Running)
331 Returns the state of the timeline.
333 \sa start(), setPaused(), stop()
335 QTimeLine::State QTimeLine::state() const
337 Q_D(const QTimeLine);
342 \property QTimeLine::loopCount
343 \brief the number of times the timeline should loop before it's finished.
345 A loop count of of 0 means that the timeline will loop forever.
347 By default, this property contains a value of 1.
349 int QTimeLine::loopCount() const
351 Q_D(const QTimeLine);
352 return d->totalLoopCount;
354 void QTimeLine::setLoopCount(int count)
357 d->totalLoopCount = count;
361 \property QTimeLine::direction
362 \brief the direction of the timeline when QTimeLine is in \l Running
365 This direction indicates whether the time moves from 0 towards the
366 timeline duration, or from the value of the duration and towards 0 after
367 start() has been called.
369 By default, this property is set to \l Forward.
371 QTimeLine::Direction QTimeLine::direction() const
373 Q_D(const QTimeLine);
376 void QTimeLine::setDirection(Direction direction)
379 d->direction = direction;
380 d->startTime = d->currentTime;
385 \property QTimeLine::duration
386 \brief the total duration of the timeline in milliseconds.
388 By default, this value is 1000 (i.e., 1 second), but you can change this
389 by either passing a duration to QTimeLine's constructor, or by calling
390 setDuration(). The duration must be larger than 0.
392 \note Changing the duration does not cause the current time to be reset
393 to zero or the new duration. You also need to call setCurrentTime() with
396 int QTimeLine::duration() const
398 Q_D(const QTimeLine);
401 void QTimeLine::setDuration(int duration)
405 qWarning("QTimeLine::setDuration: cannot set duration <= 0");
408 d->duration = duration;
412 Returns the start frame, which is the frame corresponding to the start of
413 the timeline (i.e., the frame for which the current value is 0).
415 \sa setStartFrame(), setFrameRange()
417 int QTimeLine::startFrame() const
419 Q_D(const QTimeLine);
420 return d->startFrame;
424 Sets the start frame, which is the frame corresponding to the start of the
425 timeline (i.e., the frame for which the current value is 0), to \a frame.
427 \sa startFrame(), endFrame(), setFrameRange()
429 void QTimeLine::setStartFrame(int frame)
432 d->startFrame = frame;
436 Returns the end frame, which is the frame corresponding to the end of the
437 timeline (i.e., the frame for which the current value is 1).
439 \sa setEndFrame(), setFrameRange()
441 int QTimeLine::endFrame() const
443 Q_D(const QTimeLine);
448 Sets the end frame, which is the frame corresponding to the end of the
449 timeline (i.e., the frame for which the current value is 1), to \a frame.
451 \sa endFrame(), startFrame(), setFrameRange()
453 void QTimeLine::setEndFrame(int frame)
460 Sets the timeline's frame counter to start at \a startFrame, and end and
461 \a endFrame. For each time value, QTimeLine will find the corresponding
462 frame when you call currentFrame() or frameForTime() by interpolating,
463 using the return value of valueForTime().
465 When in Running state, QTimeLine also emits the frameChanged() signal when
468 \sa startFrame(), endFrame(), start(), currentFrame()
470 void QTimeLine::setFrameRange(int startFrame, int endFrame)
473 d->startFrame = startFrame;
474 d->endFrame = endFrame;
478 \property QTimeLine::updateInterval
479 \brief the time in milliseconds between each time QTimeLine updates its
482 When updating the current time, QTimeLine will emit valueChanged() if the
483 current value changed, and frameChanged() if the frame changed.
485 By default, the interval is 40 ms, which corresponds to a rate of 25
488 int QTimeLine::updateInterval() const
490 Q_D(const QTimeLine);
491 return d->updateInterval;
493 void QTimeLine::setUpdateInterval(int interval)
496 d->updateInterval = interval;
500 \property QTimeLine::curveShape
501 \brief the shape of the timeline curve.
503 The curve shape describes the relation between the time and value for the
504 base implementation of valueForTime().
506 If you have reimplemented valueForTime(), this value is ignored.
508 By default, this property is set to \l EaseInOutCurve.
512 QTimeLine::CurveShape QTimeLine::curveShape() const
514 Q_D(const QTimeLine);
515 switch (d->easingCurve.type()) {
517 case QEasingCurve::InOutSine:
518 return EaseInOutCurve;
519 case QEasingCurve::InCurve:
521 case QEasingCurve::OutCurve:
523 case QEasingCurve::Linear:
525 case QEasingCurve::SineCurve:
527 case QEasingCurve::CosineCurve:
530 return EaseInOutCurve;
533 void QTimeLine::setCurveShape(CurveShape shape)
538 setEasingCurve(QEasingCurve(QEasingCurve::InOutSine));
541 setEasingCurve(QEasingCurve(QEasingCurve::InCurve));
544 setEasingCurve(QEasingCurve(QEasingCurve::OutCurve));
547 setEasingCurve(QEasingCurve(QEasingCurve::Linear));
550 setEasingCurve(QEasingCurve(QEasingCurve::SineCurve));
553 setEasingCurve(QEasingCurve(QEasingCurve::CosineCurve));
559 \property QTimeLine::easingCurve
563 Specifies the easing curve that the timeline will use.
564 If both easing curve and curveShape are set, the last set property will
565 override the previous one. (If valueForTime() is reimplemented it will
569 QEasingCurve QTimeLine::easingCurve() const
571 Q_D(const QTimeLine);
572 return d->easingCurve;
575 void QTimeLine::setEasingCurve(const QEasingCurve& curve)
578 d->easingCurve = curve;
582 \property QTimeLine::currentTime
583 \brief the current time of the time line.
585 When QTimeLine is in Running state, this value is updated continuously as
586 a function of the duration and direction of the timeline. Otherwise, it is
587 value that was current when stop() was called last, or the value set by
590 By default, this property contains a value of 0.
592 int QTimeLine::currentTime() const
594 Q_D(const QTimeLine);
595 return d->currentTime;
597 void QTimeLine::setCurrentTime(int msec)
601 d->currentLoopCount = 0;
603 d->setCurrentTime(msec);
607 Returns the frame corresponding to the current time.
609 \sa currentTime(), frameForTime(), setFrameRange()
611 int QTimeLine::currentFrame() const
613 Q_D(const QTimeLine);
614 return frameForTime(d->currentTime);
618 Returns the value corresponding to the current time.
620 \sa valueForTime(), currentFrame()
622 qreal QTimeLine::currentValue() const
624 Q_D(const QTimeLine);
625 return valueForTime(d->currentTime);
629 Returns the frame corresponding to the time \a msec. This value is
630 calculated using a linear interpolation of the start and end frame, based
631 on the value returned by valueForTime().
633 \sa valueForTime(), setFrameRange()
635 int QTimeLine::frameForTime(int msec) const
637 Q_D(const QTimeLine);
638 if (d->direction == Forward)
639 return d->startFrame + int((d->endFrame - d->startFrame) * valueForTime(msec));
640 return d->startFrame + qCeil((d->endFrame - d->startFrame) * valueForTime(msec));
644 Returns the timeline value for the time \a msec. The returned value, which
645 varies depending on the curve shape, is always between 0 and 1. If \a msec
646 is 0, the default implementation always returns 0.
648 Reimplement this function to provide a custom curve shape for your
651 \sa CurveShape, frameForTime()
653 qreal QTimeLine::valueForTime(int msec) const
655 Q_D(const QTimeLine);
656 msec = qMin(qMax(msec, 0), d->duration);
658 qreal value = msec / qreal(d->duration);
659 return d->easingCurve.valueForProgress(value);
663 Starts the timeline. QTimeLine will enter Running state, and once it
664 enters the event loop, it will update its current time, frame and value at
665 regular intervals. The default interval is 40 ms (i.e., 25 times per
666 second). You can change the update interval by calling
669 The timeline will start from position 0, or the end if going backward.
670 If you want to resume a stopped timeline without restarting, you can call
673 \sa resume(), updateInterval(), frameChanged(), valueChanged()
675 void QTimeLine::start()
679 qWarning("QTimeLine::start: already running");
683 if (d->direction == Backward)
684 curTime = d->duration;
685 d->timerId = startTimer(d->updateInterval);
686 d->startTime = curTime;
687 d->currentLoopCount = 0;
689 d->setState(Running);
690 d->setCurrentTime(curTime);
694 Resumes the timeline from the current time. QTimeLine will reenter Running
695 state, and once it enters the event loop, it will update its current time,
696 frame and value at regular intervals.
698 In contrast to start(), this function does not restart the timeline before
701 \sa start(), updateInterval(), frameChanged(), valueChanged()
703 void QTimeLine::resume()
707 qWarning("QTimeLine::resume: already running");
710 d->timerId = startTimer(d->updateInterval);
711 d->startTime = d->currentTime;
713 d->setState(Running);
717 Stops the timeline, causing QTimeLine to enter NotRunning state.
721 void QTimeLine::stop()
725 killTimer(d->timerId);
726 d->setState(NotRunning);
731 If \a paused is true, the timeline is paused, causing QTimeLine to enter
732 Paused state. No updates will be signaled until either start() or
733 setPaused(false) is called. If \a paused is false, the timeline is resumed
734 and continues where it left.
738 void QTimeLine::setPaused(bool paused)
741 if (d->state == NotRunning) {
742 qWarning("QTimeLine::setPaused: Not running");
745 if (paused && d->state != Paused) {
746 d->startTime = d->currentTime;
747 killTimer(d->timerId);
750 } else if (!paused && d->state == Paused) {
751 d->timerId = startTimer(d->updateInterval);
752 d->setState(Running);
757 Toggles the direction of the timeline. If the direction was Forward, it
758 becomes Backward, and vice verca.
762 void QTimeLine::toggleDirection()
765 setDirection(d->direction == Forward ? Backward : Forward);
771 void QTimeLine::timerEvent(QTimerEvent *event)
774 if (event->timerId() != d->timerId) {
780 if (d->direction == Forward) {
781 d->setCurrentTime(d->startTime + d->timer.elapsed());
783 d->setCurrentTime(d->startTime - d->timer.elapsed());