1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** Contact: Qt Software Information ([email protected])
|
---|
5 | **
|
---|
6 | ** This file is part of the QtCore module of the Qt Toolkit.
|
---|
7 | **
|
---|
8 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
9 | ** Commercial Usage
|
---|
10 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
11 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
12 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
13 | ** a written agreement between you and Nokia.
|
---|
14 | **
|
---|
15 | ** GNU Lesser General Public License Usage
|
---|
16 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
17 | ** General Public License version 2.1 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
19 | ** packaging of this file. Please review the following information to
|
---|
20 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
22 | **
|
---|
23 | ** In addition, as a special exception, Nokia gives you certain
|
---|
24 | ** additional rights. These rights are described in the Nokia Qt LGPL
|
---|
25 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
---|
26 | ** package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you are unsure which license is appropriate for your use, please
|
---|
37 | ** contact the sales department at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "qtimeline.h"
|
---|
43 |
|
---|
44 | #include <private/qobject_p.h>
|
---|
45 | #include <QtCore/qdatetime.h>
|
---|
46 | #include <QtCore/qcoreevent.h>
|
---|
47 | #include <QtCore/qmath.h>
|
---|
48 |
|
---|
49 | QT_BEGIN_NAMESPACE
|
---|
50 |
|
---|
51 | static const qreal pi = qreal(3.14159265359);
|
---|
52 | static const qreal halfPi = pi / qreal(2.0);
|
---|
53 |
|
---|
54 |
|
---|
55 | static inline qreal qt_sinProgress(qreal value)
|
---|
56 | {
|
---|
57 | return qSin((value * pi) - halfPi) / 2 + qreal(0.5);
|
---|
58 | }
|
---|
59 |
|
---|
60 | static inline qreal qt_smoothBeginEndMixFactor(qreal value)
|
---|
61 | {
|
---|
62 | return qMin(qMax(1 - value * 2 + qreal(0.3), qreal(0.0)), qreal(1.0));
|
---|
63 | }
|
---|
64 |
|
---|
65 | class QTimeLinePrivate : public QObjectPrivate
|
---|
66 | {
|
---|
67 | Q_DECLARE_PUBLIC(QTimeLine)
|
---|
68 | public:
|
---|
69 | inline QTimeLinePrivate()
|
---|
70 | : startTime(0), duration(1000), startFrame(0), endFrame(0),
|
---|
71 | updateInterval(1000 / 25),
|
---|
72 | totalLoopCount(1), currentLoopCount(0), currentTime(0), timerId(0),
|
---|
73 | direction(QTimeLine::Forward), curveShape(QTimeLine::EaseInOutCurve),
|
---|
74 | state(QTimeLine::NotRunning)
|
---|
75 | { }
|
---|
76 |
|
---|
77 | int startTime;
|
---|
78 | int duration;
|
---|
79 | int startFrame;
|
---|
80 | int endFrame;
|
---|
81 | int updateInterval;
|
---|
82 | int totalLoopCount;
|
---|
83 | int currentLoopCount;
|
---|
84 |
|
---|
85 | int currentTime;
|
---|
86 | int elapsedTime;
|
---|
87 | int timerId;
|
---|
88 | QTime timer;
|
---|
89 |
|
---|
90 | QTimeLine::Direction direction;
|
---|
91 | QTimeLine::CurveShape curveShape;
|
---|
92 | QTimeLine::State state;
|
---|
93 | inline void setState(QTimeLine::State newState)
|
---|
94 | {
|
---|
95 | Q_Q(QTimeLine);
|
---|
96 | if (newState != state)
|
---|
97 | emit q->stateChanged(state = newState);
|
---|
98 | }
|
---|
99 |
|
---|
100 | void setCurrentTime(int msecs);
|
---|
101 | };
|
---|
102 |
|
---|
103 | /*!
|
---|
104 | \internal
|
---|
105 | */
|
---|
106 | void QTimeLinePrivate::setCurrentTime(int msecs)
|
---|
107 | {
|
---|
108 | Q_Q(QTimeLine);
|
---|
109 |
|
---|
110 | qreal lastValue = q->currentValue();
|
---|
111 | int lastFrame = q->currentFrame();
|
---|
112 |
|
---|
113 | // Determine if we are looping.
|
---|
114 | int elapsed = (direction == QTimeLine::Backward) ? (-msecs + duration) : msecs;
|
---|
115 | int loopCount = elapsed / duration;
|
---|
116 |
|
---|
117 | bool looping = (loopCount != currentLoopCount);
|
---|
118 | #ifdef QTIMELINE_DEBUG
|
---|
119 | qDebug() << "QTimeLinePrivate::setCurrentTime:" << msecs << duration << "with loopCount" << loopCount
|
---|
120 | << "currentLoopCount" << currentLoopCount
|
---|
121 | << "looping" << looping;
|
---|
122 | #endif
|
---|
123 | if (looping)
|
---|
124 | currentLoopCount = loopCount;
|
---|
125 |
|
---|
126 | // Normalize msecs to be between 0 and duration, inclusive.
|
---|
127 | currentTime = elapsed % duration;
|
---|
128 | if (direction == QTimeLine::Backward)
|
---|
129 | currentTime = duration - currentTime;
|
---|
130 |
|
---|
131 | // Check if we have reached the end of loopcount.
|
---|
132 | bool finished = false;
|
---|
133 | if (totalLoopCount && currentLoopCount >= totalLoopCount) {
|
---|
134 | finished = true;
|
---|
135 | currentTime = (direction == QTimeLine::Backward) ? 0 : duration;
|
---|
136 | currentLoopCount = totalLoopCount - 1;
|
---|
137 | }
|
---|
138 |
|
---|
139 | int currentFrame = q->frameForTime(currentTime);
|
---|
140 | #ifdef QTIMELINE_DEBUG
|
---|
141 | qDebug() << "QTimeLinePrivate::setCurrentTime: frameForTime" << currentTime << currentFrame;
|
---|
142 | #endif
|
---|
143 | if (lastValue != q->currentValue())
|
---|
144 | emit q->valueChanged(q->currentValue());
|
---|
145 | if (lastFrame != currentFrame) {
|
---|
146 | const int transitionframe = (direction == QTimeLine::Forward ? endFrame : startFrame);
|
---|
147 | if (looping && !finished && transitionframe != currentFrame) {
|
---|
148 | #ifdef QTIMELINE_DEBUG
|
---|
149 | qDebug() << "QTimeLinePrivate::setCurrentTime: transitionframe";
|
---|
150 | #endif
|
---|
151 | emit q->frameChanged(transitionframe);
|
---|
152 | }
|
---|
153 | #ifdef QTIMELINE_DEBUG
|
---|
154 | else {
|
---|
155 | QByteArray reason;
|
---|
156 | if (!looping)
|
---|
157 | reason += " not looping";
|
---|
158 | if (finished) {
|
---|
159 | if (!reason.isEmpty())
|
---|
160 | reason += " and";
|
---|
161 | reason += " finished";
|
---|
162 | }
|
---|
163 | if (transitionframe == currentFrame) {
|
---|
164 | if (!reason.isEmpty())
|
---|
165 | reason += " and";
|
---|
166 | reason += " transitionframe is equal to currentFrame: " + QByteArray::number(currentFrame);
|
---|
167 | }
|
---|
168 | qDebug("QTimeLinePrivate::setCurrentTime: not transitionframe because %s", reason.constData());
|
---|
169 | }
|
---|
170 | #endif
|
---|
171 | emit q->frameChanged(currentFrame);
|
---|
172 | }
|
---|
173 | if (finished && state == QTimeLine::Running) {
|
---|
174 | q->stop();
|
---|
175 | emit q->finished();
|
---|
176 | }
|
---|
177 | }
|
---|
178 |
|
---|
179 | /*!
|
---|
180 | \class QTimeLine
|
---|
181 | \brief The QTimeLine class provides a timeline for controlling animations.
|
---|
182 | \since 4.2
|
---|
183 | \ingroup multimedia
|
---|
184 |
|
---|
185 | It's most commonly used to animate a GUI control by calling a slot
|
---|
186 | periodically. You can construct a timeline by passing its duration in
|
---|
187 | milliseconds to QTimeLine's constructor. The timeline's duration describes
|
---|
188 | for how long the animation will run. Then you set a suitable frame range
|
---|
189 | by calling setFrameRange(). Finally connect the frameChanged() signal to a
|
---|
190 | suitable slot in the widget you wish to animate (e.g., setValue() in
|
---|
191 | QProgressBar). When you proceed to calling start(), QTimeLine will enter
|
---|
192 | Running state, and start emitting frameChanged() at regular intervals,
|
---|
193 | causing your widget's connected property's value to grow from the lower
|
---|
194 | end to the upper and of your frame range, at a steady rate. You can
|
---|
195 | specify the update interval by calling setUpdateInterval(). When done,
|
---|
196 | QTimeLine enters NotRunning state, and emits finished().
|
---|
197 |
|
---|
198 | Example:
|
---|
199 |
|
---|
200 | \snippet doc/src/snippets/code/src_corelib_tools_qtimeline.cpp 0
|
---|
201 |
|
---|
202 | You can also use QTimeLine with the
|
---|
203 | \l{Graphics View}{Graphics View framework} for
|
---|
204 | animations. The QGraphicsItemAnimation class implements animation
|
---|
205 | of \l{QGraphicsItem}{QGraphicsItems} with a timeline.
|
---|
206 |
|
---|
207 | By default the timeline runs once, from the beginning and towards the end,
|
---|
208 | upon which you must call start() again to restart from the beginning. To
|
---|
209 | make the timeline loop, you can call setLoopCount(), passing the number of
|
---|
210 | times the timeline should run before finishing. The direction can also be
|
---|
211 | changed, causing the timeline to run backward, by calling
|
---|
212 | setDirection(). You can also pause and unpause the timeline while it's
|
---|
213 | running by calling setPaused(). For interactive control, the
|
---|
214 | setCurrentTime() function is provided, which sets the time position of the
|
---|
215 | time line directly. Although most useful in NotRunning state, (e.g.,
|
---|
216 | connected to a valueChanged() signal in a QSlider,) this function can be
|
---|
217 | called at any time.
|
---|
218 |
|
---|
219 | The frame interface is useful for standard widgets, but QTimeLine can be
|
---|
220 | used to control any type of animation. The heart of QTimeLine lies in the
|
---|
221 | valueForTime() function, which generates a \e value between 0 and 1 for a
|
---|
222 | given time. This value is typically used to describe the steps of an
|
---|
223 | animation, where 0 is the first step of an animation, and 1 is the last
|
---|
224 | step. When running, QTimeLine generates values between 0 and 1 by calling
|
---|
225 | valueForTime() and emitting valueChanged(). By default, valueForTime()
|
---|
226 | applies an interpolation algorithm to generate these value. You can choose
|
---|
227 | from a set of predefined timeline algorithms by calling
|
---|
228 | setCurveShape(). By default, QTimeLine uses the EaseInOut curve shape,
|
---|
229 | which provides a value that grows slowly, then grows steadily, and
|
---|
230 | finally grows slowly. For a custom timeline, you can reimplement
|
---|
231 | valueForTime(), in which case QTimeLine's curveShape property is ignored.
|
---|
232 |
|
---|
233 | \sa QProgressBar, QProgressDialog, QGraphicsItemAnimation
|
---|
234 | */
|
---|
235 |
|
---|
236 | /*!
|
---|
237 | \enum QTimeLine::State
|
---|
238 |
|
---|
239 | This enum describes the state of the timeline.
|
---|
240 |
|
---|
241 | \value NotRunning The timeline is not running. This is the initial state
|
---|
242 | of QTimeLine, and the state QTimeLine reenters when finished. The current
|
---|
243 | time, frame and value remain unchanged until either setCurrentTime() is
|
---|
244 | called, or the timeline is started by calling start().
|
---|
245 |
|
---|
246 | \value Paused The timeline is paused (i.e., temporarily
|
---|
247 | suspended). Calling setPaused(false) will resume timeline activity.
|
---|
248 |
|
---|
249 | \value Running The timeline is running. While control is in the event
|
---|
250 | loop, QTimeLine will update its current time at regular intervals,
|
---|
251 | emitting valueChanged() and frameChanged() when appropriate.
|
---|
252 |
|
---|
253 | \sa state(), stateChanged()
|
---|
254 | */
|
---|
255 |
|
---|
256 | /*!
|
---|
257 | \enum QTimeLine::Direction
|
---|
258 |
|
---|
259 | This enum describes the direction of the timeline when in \l Running state.
|
---|
260 |
|
---|
261 | \value Forward The current time of the timeline increases with time (i.e.,
|
---|
262 | moves from 0 and towards the end / duration).
|
---|
263 |
|
---|
264 | \value Backward The current time of the timeline decreases with time (i.e.,
|
---|
265 | moves from the end / duration and towards 0).
|
---|
266 |
|
---|
267 | \sa setDirection()
|
---|
268 | */
|
---|
269 |
|
---|
270 | /*!
|
---|
271 | \enum QTimeLine::CurveShape
|
---|
272 |
|
---|
273 | This enum describes the default shape of QTimeLine's value curve. The
|
---|
274 | default, shape is EaseInOutCurve. The curve defines the relation
|
---|
275 | between the value and the timeline.
|
---|
276 |
|
---|
277 | \value EaseInCurve The value starts growing slowly, then increases in speed.
|
---|
278 | \value EaseOutCurve The value starts growing steadily, then ends slowly.
|
---|
279 | \value EaseInOutCurve The value starts growing slowly, then runs steadily, then grows slowly again.
|
---|
280 | \value LinearCurve The value grows linearly (e.g., if the duration is 1000 ms,
|
---|
281 | the value at time 500 ms is 0.5).
|
---|
282 | \value SineCurve The value grows sinusoidally.
|
---|
283 | \value CosineCurve The value grows cosinusoidally.
|
---|
284 |
|
---|
285 | \sa setCurveShape()
|
---|
286 | */
|
---|
287 |
|
---|
288 | /*!
|
---|
289 | \fn QTimeLine::valueChanged(qreal value)
|
---|
290 |
|
---|
291 | QTimeLine emits this signal at regular intervals when in \l Running state,
|
---|
292 | but only if the current value changes. \a value is the current value. \a value is
|
---|
293 | a number between 0.0 and 1.0
|
---|
294 |
|
---|
295 | \sa QTimeLine::setDuration(), QTimeLine::valueForTime(), QTimeLine::updateInterval
|
---|
296 | */
|
---|
297 |
|
---|
298 | /*!
|
---|
299 | \fn QTimeLine::frameChanged(int frame)
|
---|
300 |
|
---|
301 | QTimeLine emits this signal at regular intervals when in \l Running state,
|
---|
302 | but only if the current frame changes. \a frame is the current frame number.
|
---|
303 |
|
---|
304 | \sa QTimeLine::setFrameRange(), QTimeLine::updateInterval
|
---|
305 | */
|
---|
306 |
|
---|
307 | /*!
|
---|
308 | \fn QTimeLine::stateChanged(QTimeLine::State newState)
|
---|
309 |
|
---|
310 | This signal is emitted whenever QTimeLine's state changes. The new state
|
---|
311 | is \a newState.
|
---|
312 | */
|
---|
313 |
|
---|
314 | /*!
|
---|
315 | \fn QTimeLine::finished()
|
---|
316 |
|
---|
317 | This signal is emitted when QTimeLine finishes (i.e., reaches the end of
|
---|
318 | its time line), and does not loop.
|
---|
319 | */
|
---|
320 |
|
---|
321 | /*!
|
---|
322 | Constructs a timeline with a duration of \a duration milliseconds. \a
|
---|
323 | parent is passed to QObject's constructor. The default duration is 1000
|
---|
324 | milliseconds.
|
---|
325 | */
|
---|
326 | QTimeLine::QTimeLine(int duration, QObject *parent)
|
---|
327 | : QObject(*new QTimeLinePrivate, parent)
|
---|
328 | {
|
---|
329 | setDuration(duration);
|
---|
330 | }
|
---|
331 |
|
---|
332 | /*!
|
---|
333 | Destroys the timeline.
|
---|
334 | */
|
---|
335 | QTimeLine::~QTimeLine()
|
---|
336 | {
|
---|
337 | Q_D(QTimeLine);
|
---|
338 |
|
---|
339 | if (d->state == Running)
|
---|
340 | stop();
|
---|
341 | }
|
---|
342 |
|
---|
343 | /*!
|
---|
344 | Returns the state of the timeline.
|
---|
345 |
|
---|
346 | \sa start(), setPaused(), stop()
|
---|
347 | */
|
---|
348 | QTimeLine::State QTimeLine::state() const
|
---|
349 | {
|
---|
350 | Q_D(const QTimeLine);
|
---|
351 | return d->state;
|
---|
352 | }
|
---|
353 |
|
---|
354 | /*!
|
---|
355 | \property QTimeLine::loopCount
|
---|
356 | \brief the number of times the timeline should loop before it's finished.
|
---|
357 |
|
---|
358 | A loop count of of 0 means that the timeline will loop forever.
|
---|
359 |
|
---|
360 | By default, this property contains a value of 1.
|
---|
361 | */
|
---|
362 | int QTimeLine::loopCount() const
|
---|
363 | {
|
---|
364 | Q_D(const QTimeLine);
|
---|
365 | return d->totalLoopCount;
|
---|
366 | }
|
---|
367 | void QTimeLine::setLoopCount(int count)
|
---|
368 | {
|
---|
369 | Q_D(QTimeLine);
|
---|
370 | d->totalLoopCount = count;
|
---|
371 | }
|
---|
372 |
|
---|
373 | /*!
|
---|
374 | \property QTimeLine::direction
|
---|
375 | \brief the direction of the timeline when QTimeLine is in \l Running
|
---|
376 | state.
|
---|
377 |
|
---|
378 | This direction indicates whether the time moves from 0 towards the
|
---|
379 | timeline duration, or from the value of the duration and towards 0 after
|
---|
380 | start() has been called.
|
---|
381 |
|
---|
382 | By default, this property is set to \l Forward.
|
---|
383 | */
|
---|
384 | QTimeLine::Direction QTimeLine::direction() const
|
---|
385 | {
|
---|
386 | Q_D(const QTimeLine);
|
---|
387 | return d->direction;
|
---|
388 | }
|
---|
389 | void QTimeLine::setDirection(Direction direction)
|
---|
390 | {
|
---|
391 | Q_D(QTimeLine);
|
---|
392 | d->direction = direction;
|
---|
393 | d->startTime = d->currentTime;
|
---|
394 | d->timer.start();
|
---|
395 | }
|
---|
396 |
|
---|
397 | /*!
|
---|
398 | \property QTimeLine::duration
|
---|
399 | \brief the total duration of the timeline in milliseconds.
|
---|
400 |
|
---|
401 | By default, this value is 1000 (i.e., 1 second), but you can change this
|
---|
402 | by either passing a duration to QTimeLine's constructor, or by calling
|
---|
403 | setDuration(). The duration must be larger than 0.
|
---|
404 | */
|
---|
405 | int QTimeLine::duration() const
|
---|
406 | {
|
---|
407 | Q_D(const QTimeLine);
|
---|
408 | return d->duration;
|
---|
409 | }
|
---|
410 | void QTimeLine::setDuration(int duration)
|
---|
411 | {
|
---|
412 | Q_D(QTimeLine);
|
---|
413 | if (duration <= 0) {
|
---|
414 | qWarning("QTimeLine::setDuration: cannot set duration <= 0");
|
---|
415 | return;
|
---|
416 | }
|
---|
417 | d->duration = duration;
|
---|
418 | }
|
---|
419 |
|
---|
420 | /*!
|
---|
421 | Returns the start frame, which is the frame corresponding to the start of
|
---|
422 | the timeline (i.e., the frame for which the current value is 0).
|
---|
423 |
|
---|
424 | \sa setStartFrame(), setFrameRange()
|
---|
425 | */
|
---|
426 | int QTimeLine::startFrame() const
|
---|
427 | {
|
---|
|
---|