source: trunk/src/corelib/tools/qtimeline.cpp@ 45

Last change on this file since 45 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 23.3 KB
Line 
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
49QT_BEGIN_NAMESPACE
50
51static const qreal pi = qreal(3.14159265359);
52static const qreal halfPi = pi / qreal(2.0);
53
54
55static inline qreal qt_sinProgress(qreal value)
56{
57 return qSin((value * pi) - halfPi) / 2 + qreal(0.5);
58}
59
60static 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
65class QTimeLinePrivate : public QObjectPrivate
66{
67 Q_DECLARE_PUBLIC(QTimeLine)
68public:
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*/
106void 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 */
326QTimeLine::QTimeLine(int duration, QObject *parent)
327 : QObject(*new QTimeLinePrivate, parent)
328{
329 setDuration(duration);
330}
331
332/*!
333 Destroys the timeline.
334 */
335QTimeLine::~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*/
348QTimeLine::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*/
362int QTimeLine::loopCount() const
363{
364 Q_D(const QTimeLine);
365 return d->totalLoopCount;
366}
367void 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*/
384QTimeLine::Direction QTimeLine::direction() const
385{
386 Q_D(const QTimeLine);
387 return d->direction;
388}
389void 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*/
405int QTimeLine::duration() const
406{
407 Q_D(const QTimeLine);
408 return d->duration;
409}
410void 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*/
426int QTimeLine::startFrame() const
427{