|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtDeclarative module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "private/qdeclarativetimeline_p_p.h" |
|
43 |
|
44 #include <QDebug> |
|
45 #include <QMutex> |
|
46 #include <QThread> |
|
47 #include <QWaitCondition> |
|
48 #include <QEvent> |
|
49 #include <QCoreApplication> |
|
50 #include <QEasingCurve> |
|
51 #include <QTime> |
|
52 |
|
53 QT_BEGIN_NAMESPACE |
|
54 |
|
55 struct Update { |
|
56 Update(QDeclarativeTimeLineValue *_g, qreal _v) |
|
57 : g(_g), v(_v) {} |
|
58 Update(const QDeclarativeTimeLineCallback &_e) |
|
59 : g(0), v(0), e(_e) {} |
|
60 |
|
61 QDeclarativeTimeLineValue *g; |
|
62 qreal v; |
|
63 QDeclarativeTimeLineCallback e; |
|
64 }; |
|
65 |
|
66 struct QDeclarativeTimeLinePrivate |
|
67 { |
|
68 QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *); |
|
69 |
|
70 struct Op { |
|
71 enum Type { |
|
72 Pause, |
|
73 Set, |
|
74 Move, |
|
75 MoveBy, |
|
76 Accel, |
|
77 AccelDistance, |
|
78 Execute |
|
79 }; |
|
80 Op() {} |
|
81 Op(Type t, int l, qreal v, qreal v2, int o, |
|
82 const QDeclarativeTimeLineCallback &ev = QDeclarativeTimeLineCallback(), const QEasingCurve &es = QEasingCurve()) |
|
83 : type(t), length(l), value(v), value2(v2), order(o), event(ev), |
|
84 easing(es) {} |
|
85 Op(const Op &o) |
|
86 : type(o.type), length(o.length), value(o.value), value2(o.value2), |
|
87 order(o.order), event(o.event), easing(o.easing) {} |
|
88 Op &operator=(const Op &o) { |
|
89 type = o.type; length = o.length; value = o.value; |
|
90 value2 = o.value2; order = o.order; event = o.event; |
|
91 easing = o.easing; |
|
92 return *this; |
|
93 } |
|
94 |
|
95 Type type; |
|
96 int length; |
|
97 qreal value; |
|
98 qreal value2; |
|
99 |
|
100 int order; |
|
101 QDeclarativeTimeLineCallback event; |
|
102 QEasingCurve easing; |
|
103 }; |
|
104 struct TimeLine |
|
105 { |
|
106 TimeLine() : length(0), consumedOpLength(0), base(0.) {} |
|
107 QList<Op> ops; |
|
108 int length; |
|
109 int consumedOpLength; |
|
110 qreal base; |
|
111 }; |
|
112 |
|
113 int length; |
|
114 int syncPoint; |
|
115 typedef QHash<QDeclarativeTimeLineObject *, TimeLine> Ops; |
|
116 Ops ops; |
|
117 QDeclarativeTimeLine *q; |
|
118 |
|
119 void add(QDeclarativeTimeLineObject &, const Op &); |
|
120 qreal value(const Op &op, int time, qreal base, bool *) const; |
|
121 |
|
122 int advance(int); |
|
123 |
|
124 bool clockRunning; |
|
125 int prevTime; |
|
126 |
|
127 int order; |
|
128 |
|
129 QDeclarativeTimeLine::SyncMode syncMode; |
|
130 int syncAdj; |
|
131 QList<QPair<int, Update> > *updateQueue; |
|
132 }; |
|
133 |
|
134 QDeclarativeTimeLinePrivate::QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *parent) |
|
135 : length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QDeclarativeTimeLine::LocalSync), syncAdj(0), updateQueue(0) |
|
136 { |
|
137 } |
|
138 |
|
139 void QDeclarativeTimeLinePrivate::add(QDeclarativeTimeLineObject &g, const Op &o) |
|
140 { |
|
141 if (g._t && g._t != q) { |
|
142 qWarning() << "QDeclarativeTimeLine: Cannot modify a QDeclarativeTimeLineValue owned by" |
|
143 << "another timeline."; |
|
144 return; |
|
145 } |
|
146 g._t = q; |
|
147 |
|
148 Ops::Iterator iter = ops.find(&g); |
|
149 if (iter == ops.end()) { |
|
150 iter = ops.insert(&g, TimeLine()); |
|
151 if (syncPoint > 0) |
|
152 q->pause(g, syncPoint); |
|
153 } |
|
154 if (!iter->ops.isEmpty() && |
|
155 o.type == Op::Pause && |
|
156 iter->ops.last().type == Op::Pause) { |
|
157 iter->ops.last().length += o.length; |
|
158 iter->length += o.length; |
|
159 } else { |
|
160 iter->ops.append(o); |
|
161 iter->length += o.length; |
|
162 } |
|
163 |
|
164 if (iter->length > length) |
|
165 length = iter->length; |
|
166 |
|
167 if (!clockRunning) { |
|
168 q->stop(); |
|
169 prevTime = 0; |
|
170 clockRunning = true; |
|
171 |
|
172 if (syncMode == QDeclarativeTimeLine::LocalSync) { |
|
173 syncAdj = -1; |
|
174 } else { |
|
175 syncAdj = 0; |
|
176 } |
|
177 q->start(); |
|
178 /* q->tick(0); |
|
179 if (syncMode == QDeclarativeTimeLine::LocalSync) { |
|
180 syncAdj = -1; |
|
181 } else { |
|
182 syncAdj = 0; |
|
183 } |
|
184 */ |
|
185 } |
|
186 } |
|
187 |
|
188 qreal QDeclarativeTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const |
|
189 { |
|
190 Q_ASSERT(time >= 0); |
|
191 Q_ASSERT(time <= op.length); |
|
192 *changed = true; |
|
193 |
|
194 switch(op.type) { |
|
195 case Op::Pause: |
|
196 *changed = false; |
|
197 return base; |
|
198 case Op::Set: |
|
199 return op.value; |
|
200 case Op::Move: |
|
201 if (time == 0) { |
|
202 return base; |
|
203 } else if (time == (op.length)) { |
|
204 return op.value; |
|
205 } else { |
|
206 qreal delta = op.value - base; |
|
207 qreal pTime = (qreal)(time) / (qreal)op.length; |
|
208 if (op.easing.type() == QEasingCurve::Linear) |
|
209 return base + delta * pTime; |
|
210 else |
|
211 return base + delta * op.easing.valueForProgress(pTime); |
|
212 } |
|
213 case Op::MoveBy: |
|
214 if (time == 0) { |
|
215 return base; |
|
216 } else if (time == (op.length)) { |
|
217 return base + op.value; |
|
218 } else { |
|
219 qreal delta = op.value; |
|
220 qreal pTime = (qreal)(time) / (qreal)op.length; |
|
221 if (op.easing.type() == QEasingCurve::Linear) |
|
222 return base + delta * pTime; |
|
223 else |
|
224 return base + delta * op.easing.valueForProgress(pTime); |
|
225 } |
|
226 case Op::Accel: |
|
227 if (time == 0) { |
|
228 return base; |
|
229 } else { |
|
230 qreal t = (qreal)(time) / 1000.0f; |
|
231 qreal delta = op.value * t + 0.5f * op.value2 * t * t; |
|
232 return base + delta; |
|
233 } |
|
234 case Op::AccelDistance: |
|
235 if (time == 0) { |
|
236 return base; |
|
237 } else if (time == (op.length)) { |
|
238 return base + op.value2; |
|
239 } else { |
|
240 qreal t = (qreal)(time) / 1000.0f; |
|
241 qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length; |
|
242 qreal delta = op.value * t + 0.5f * accel * t * t; |
|
243 return base + delta; |
|
244 |
|
245 } |
|
246 case Op::Execute: |
|
247 op.event.d0(op.event.d1); |
|
248 *changed = false; |
|
249 return -1; |
|
250 } |
|
251 |
|
252 return base; |
|
253 } |
|
254 |
|
255 /*! |
|
256 \internal |
|
257 \class QDeclarativeTimeLine |
|
258 \brief The QDeclarativeTimeLine class provides a timeline for controlling animations. |
|
259 |
|
260 QDeclarativeTimeLine is similar to QTimeLine except: |
|
261 \list |
|
262 \i It updates QDeclarativeTimeLineValue instances directly, rather than maintaining a single |
|
263 current value. |
|
264 |
|
265 For example, the following animates a simple value over 200 milliseconds: |
|
266 \code |
|
267 QDeclarativeTimeLineValue v(<starting value>); |
|
268 QDeclarativeTimeLine tl; |
|
269 tl.move(v, 100., 200); |
|
270 tl.start() |
|
271 \endcode |
|
272 |
|
273 If your program needs to know when values are changed, it can either |
|
274 connect to the QDeclarativeTimeLine's updated() signal, or inherit from QDeclarativeTimeLineValue |
|
275 and reimplement the QDeclarativeTimeLineValue::setValue() method. |
|
276 |
|
277 \i Supports multiple QDeclarativeTimeLineValue, arbitrary start and end values and allows |
|
278 animations to be strung together for more complex effects. |
|
279 |
|
280 For example, the following animation moves the x and y coordinates of |
|
281 an object from wherever they are to the position (100, 100) in 50 |
|
282 milliseconds and then further animates them to (100, 200) in 50 |
|
283 milliseconds: |
|
284 |
|
285 \code |
|
286 QDeclarativeTimeLineValue x(<starting value>); |
|
287 QDeclarativeTimeLineValue y(<starting value>); |
|
288 |
|
289 QDeclarativeTimeLine tl; |
|
290 tl.start(); |
|
291 |
|
292 tl.move(x, 100., 50); |
|
293 tl.move(y, 100., 50); |
|
294 tl.move(y, 200., 50); |
|
295 \endcode |
|
296 |
|
297 \i All QDeclarativeTimeLine instances share a single, synchronized clock. |
|
298 |
|
299 Actions scheduled within the same event loop tick are scheduled |
|
300 synchronously against each other, regardless of the wall time between the |
|
301 scheduling. Synchronized scheduling applies both to within the same |
|
302 QDeclarativeTimeLine and across separate QDeclarativeTimeLine's within the same process. |
|
303 |
|
304 \endlist |
|
305 |
|
306 Currently easing functions are not supported. |
|
307 */ |
|
308 |
|
309 |
|
310 /*! |
|
311 Construct a new QDeclarativeTimeLine with the specified \a parent. |
|
312 */ |
|
313 QDeclarativeTimeLine::QDeclarativeTimeLine(QObject *parent) |
|
314 : QAbstractAnimation(parent) |
|
315 { |
|
316 d = new QDeclarativeTimeLinePrivate(this); |
|
317 } |
|
318 |
|
319 /*! |
|
320 Destroys the time line. Any inprogress animations are canceled, but not |
|
321 completed. |
|
322 */ |
|
323 QDeclarativeTimeLine::~QDeclarativeTimeLine() |
|
324 { |
|
325 for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); |
|
326 iter != d->ops.end(); |
|
327 ++iter) |
|
328 iter.key()->_t = 0; |
|
329 |
|
330 delete d; d = 0; |
|
331 } |
|
332 |
|
333 /*! |
|
334 \enum QDeclarativeTimeLine::SyncMode |
|
335 */ |
|
336 |
|
337 /*! |
|
338 Return the timeline's synchronization mode. |
|
339 */ |
|
340 QDeclarativeTimeLine::SyncMode QDeclarativeTimeLine::syncMode() const |
|
341 { |
|
342 return d->syncMode; |
|
343 } |
|
344 |
|
345 /*! |
|
346 Set the timeline's synchronization mode to \a syncMode. |
|
347 */ |
|
348 void QDeclarativeTimeLine::setSyncMode(SyncMode syncMode) |
|
349 { |
|
350 d->syncMode = syncMode; |
|
351 } |
|
352 |
|
353 /*! |
|
354 Pause \a obj for \a time milliseconds. |
|
355 */ |
|
356 void QDeclarativeTimeLine::pause(QDeclarativeTimeLineObject &obj, int time) |
|
357 { |
|
358 if (time <= 0) return; |
|
359 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Pause, time, 0., 0., d->order++); |
|
360 d->add(obj, op); |
|
361 } |
|
362 |
|
363 /*! |
|
364 Execute the \a event. |
|
365 */ |
|
366 void QDeclarativeTimeLine::callback(const QDeclarativeTimeLineCallback &callback) |
|
367 { |
|
368 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback); |
|
369 d->add(*callback.callbackObject(), op); |
|
370 } |
|
371 |
|
372 /*! |
|
373 Set the \a value of \a timeLineValue. |
|
374 */ |
|
375 void QDeclarativeTimeLine::set(QDeclarativeTimeLineValue &timeLineValue, qreal value) |
|
376 { |
|
377 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Set, 0, value, 0., d->order++); |
|
378 d->add(timeLineValue, op); |
|
379 } |
|
380 |
|
381 /*! |
|
382 Decelerate \a timeLineValue from the starting \a velocity to zero at the |
|
383 given \a acceleration rate. Although the \a acceleration is technically |
|
384 a deceleration, it should always be positive. The QDeclarativeTimeLine will ensure |
|
385 that the deceleration is in the opposite direction to the initial velocity. |
|
386 */ |
|
387 int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration) |
|
388 { |
|
389 if (acceleration == 0.0f) |
|
390 return -1; |
|
391 |
|
392 if ((velocity > 0.0f) == (acceleration > 0.0f)) |
|
393 acceleration = acceleration * -1.0f; |
|
394 |
|
395 int time = static_cast<int>(-1000 * velocity / acceleration); |
|
396 |
|
397 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); |
|
398 d->add(timeLineValue, op); |
|
399 |
|
400 return time; |
|
401 } |
|
402 |
|
403 /*! |
|
404 \overload |
|
405 |
|
406 Decelerate \a timeLineValue from the starting \a velocity to zero at the |
|
407 given \a acceleration rate over a maximum distance of maxDistance. |
|
408 |
|
409 If necessary, QDeclarativeTimeLine will reduce the acceleration to ensure that the |
|
410 entire operation does not require a move of more than \a maxDistance. |
|
411 \a maxDistance should always be positive. |
|
412 */ |
|
413 int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance) |
|
414 { |
|
415 if (maxDistance == 0.0f || acceleration == 0.0f) |
|
416 return -1; |
|
417 |
|
418 Q_ASSERT(acceleration > 0.0f && maxDistance > 0.0f); |
|
419 |
|
420 qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance); |
|
421 if (maxAccel > acceleration) |
|
422 acceleration = maxAccel; |
|
423 |
|
424 if ((velocity > 0.0f) == (acceleration > 0.0f)) |
|
425 acceleration = acceleration * -1.0f; |
|
426 |
|
427 int time = static_cast<int>(-1000 * velocity / acceleration); |
|
428 |
|
429 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); |
|
430 d->add(timeLineValue, op); |
|
431 |
|
432 return time; |
|
433 } |
|
434 |
|
435 /*! |
|
436 Decelerate \a timeLineValue from the starting \a velocity to zero over the given |
|
437 \a distance. This is like accel(), but the QDeclarativeTimeLine calculates the exact |
|
438 deceleration to use. |
|
439 |
|
440 \a distance should be positive. |
|
441 */ |
|
442 int QDeclarativeTimeLine::accelDistance(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal distance) |
|
443 { |
|
444 if (distance == 0.0f || velocity == 0.0f) |
|
445 return -1; |
|
446 |
|
447 Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f)); |
|
448 |
|
449 int time = static_cast<int>(1000 * (2.0f * distance) / velocity); |
|
450 |
|
451 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++); |
|
452 d->add(timeLineValue, op); |
|
453 |
|
454 return time; |
|
455 } |
|
456 |
|
457 /*! |
|
458 Linearly change the \a timeLineValue from its current value to the given |
|
459 \a destination value over \a time milliseconds. |
|
460 */ |
|
461 void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, int time) |
|
462 { |
|
463 if (time <= 0) return; |
|
464 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++); |
|
465 d->add(timeLineValue, op); |
|
466 } |
|
467 |
|
468 /*! |
|
469 Change the \a timeLineValue from its current value to the given \a destination |
|
470 value over \a time milliseconds using the \a easing curve. |
|
471 */ |
|
472 void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time) |
|
473 { |
|
474 if (time <= 0) return; |
|
475 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); |
|
476 d->add(timeLineValue, op); |
|
477 } |
|
478 |
|
479 /*! |
|
480 Linearly change the \a timeLineValue from its current value by the \a change amount |
|
481 over \a time milliseconds. |
|
482 */ |
|
483 void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, int time) |
|
484 { |
|
485 if (time <= 0) return; |
|
486 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++); |
|
487 d->add(timeLineValue, op); |
|
488 } |
|
489 |
|
490 /*! |
|
491 Change the \a timeLineValue from its current value by the \a change amount over |
|
492 \a time milliseconds using the \a easing curve. |
|
493 */ |
|
494 void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time) |
|
495 { |
|
496 if (time <= 0) return; |
|
497 QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing); |
|
498 d->add(timeLineValue, op); |
|
499 } |
|
500 |
|
501 /*! |
|
502 Cancel (but don't complete) all scheduled actions for \a timeLineValue. |
|
503 */ |
|
504 void QDeclarativeTimeLine::reset(QDeclarativeTimeLineValue &timeLineValue) |
|
505 { |
|
506 if (!timeLineValue._t) |
|
507 return; |
|
508 if (timeLineValue._t != this) { |
|
509 qWarning() << "QDeclarativeTimeLine: Cannot reset a QDeclarativeTimeLineValue owned by another timeline."; |
|
510 return; |
|
511 } |
|
512 remove(&timeLineValue); |
|
513 timeLineValue._t = 0; |
|
514 } |
|
515 |
|
516 int QDeclarativeTimeLine::duration() const |
|
517 { |
|
518 return -1; |
|
519 } |
|
520 |
|
521 /*! |
|
522 Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo |
|
523 within this timeline. |
|
524 |
|
525 Following operations on \a timeLineValue in this timeline will be scheduled after |
|
526 all the currently scheduled actions on \a syncTo are complete. In |
|
527 psuedo-code this is equivalent to: |
|
528 \code |
|
529 QDeclarativeTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue))) |
|
530 \endcode |
|
531 */ |
|
532 void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue, QDeclarativeTimeLineValue &syncTo) |
|
533 { |
|
534 QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo); |
|
535 if (iter == d->ops.end()) |
|
536 return; |
|
537 int length = iter->length; |
|
538 |
|
539 iter = d->ops.find(&timeLineValue); |
|
540 if (iter == d->ops.end()) { |
|
541 pause(timeLineValue, length); |
|
542 } else { |
|
543 int glength = iter->length; |
|
544 pause(timeLineValue, length - glength); |
|
545 } |
|
546 } |
|
547 |
|
548 /*! |
|
549 Synchronize the end point of \a timeLineValue to the endpoint of the longest |
|
550 action cursrently scheduled in the timeline. |
|
551 |
|
552 In psuedo-code, this is equivalent to: |
|
553 \code |
|
554 QDeclarativeTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue)) |
|
555 \endcode |
|
556 */ |
|
557 void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue) |
|
558 { |
|
559 QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue); |
|
560 if (iter == d->ops.end()) { |
|
561 pause(timeLineValue, d->length); |
|
562 } else { |
|
563 pause(timeLineValue, d->length - iter->length); |
|
564 } |
|
565 } |
|
566 |
|
567 /* |
|
568 Synchronize all currently and future scheduled values in this timeline to |
|
569 the longest action currently scheduled. |
|
570 |
|
571 For example: |
|
572 \code |
|
573 value1->setValue(0.); |
|
574 value2->setValue(0.); |
|
575 value3->setValue(0.); |
|
576 QDeclarativeTimeLine tl; |
|
577 ... |
|
578 tl.move(value1, 10, 200); |
|
579 tl.move(value2, 10, 100); |
|
580 tl.sync(); |
|
581 tl.move(value2, 20, 100); |
|
582 tl.move(value3, 20, 100); |
|
583 \endcode |
|
584 |
|
585 will result in: |
|
586 |
|
587 \table |
|
588 \header \o \o 0ms \o 50ms \o 100ms \o 150ms \o 200ms \o 250ms \o 300ms |
|
589 \row \o value1 \o 0 \o 2.5 \o 5.0 \o 7.5 \o 10 \o 10 \o 10 |
|
590 \row \o value2 \o 0 \o 5.0 \o 10.0 \o 10.0 \o 10.0 \o 15.0 \o 20.0 |
|
591 \row \o value2 \o 0 \o 0 \o 0 \o 0 \o 0 \o 10.0 \o 20.0 |
|
592 \endtable |
|
593 */ |
|
594 |
|
595 /*void QDeclarativeTimeLine::sync() |
|
596 { |
|
597 for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); |
|
598 iter != d->ops.end(); |
|
599 ++iter) |
|
600 pause(*iter.key(), d->length - iter->length); |
|
601 d->syncPoint = d->length; |
|
602 }*/ |
|
603 |
|
604 /*! |
|
605 \internal |
|
606 |
|
607 Temporary hack. |
|
608 */ |
|
609 void QDeclarativeTimeLine::setSyncPoint(int sp) |
|
610 { |
|
611 d->syncPoint = sp; |
|
612 } |
|
613 |
|
614 /*! |
|
615 \internal |
|
616 |
|
617 Temporary hack. |
|
618 */ |
|
619 int QDeclarativeTimeLine::syncPoint() const |
|
620 { |
|
621 return d->syncPoint; |
|
622 } |
|
623 |
|
624 /*! |
|
625 Returns true if the timeline is active. An active timeline is one where |
|
626 QDeclarativeTimeLineValue actions are still pending. |
|
627 */ |
|
628 bool QDeclarativeTimeLine::isActive() const |
|
629 { |
|
630 return !d->ops.isEmpty(); |
|
631 } |
|
632 |
|
633 /*! |
|
634 Completes the timeline. All queued actions are played to completion, and then discarded. For example, |
|
635 \code |
|
636 QDeclarativeTimeLineValue v(0.); |
|
637 QDeclarativeTimeLine tl; |
|
638 tl.move(v, 100., 1000.); |
|
639 // 500 ms passes |
|
640 // v.value() == 50. |
|
641 tl.complete(); |
|
642 // v.value() == 100. |
|
643 \endcode |
|
644 */ |
|
645 void QDeclarativeTimeLine::complete() |
|
646 { |
|
647 d->advance(d->length); |
|
648 } |
|
649 |
|
650 /*! |
|
651 Resets the timeline. All queued actions are discarded and QDeclarativeTimeLineValue's retain their current value. For example, |
|
652 \code |
|
653 QDeclarativeTimeLineValue v(0.); |
|
654 QDeclarativeTimeLine tl; |
|
655 tl.move(v, 100., 1000.); |
|
656 // 500 ms passes |
|
657 // v.value() == 50. |
|
658 tl.clear(); |
|
659 // v.value() == 50. |
|
660 \endcode |
|
661 */ |
|
662 void QDeclarativeTimeLine::clear() |
|
663 { |
|
664 for (QDeclarativeTimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter) |
|
665 iter.key()->_t = 0; |
|
666 d->ops.clear(); |
|
667 d->length = 0; |
|
668 d->syncPoint = 0; |
|
669 //XXX need stop here? |
|
670 } |
|
671 |
|
672 int QDeclarativeTimeLine::time() const |
|
673 { |
|
674 return d->prevTime; |
|
675 } |
|
676 |
|
677 /*! |
|
678 \fn void QDeclarativeTimeLine::updated() |
|
679 |
|
680 Emitted each time the timeline modifies QDeclarativeTimeLineValues. Even if multiple |
|
681 QDeclarativeTimeLineValues are changed, this signal is only emitted once for each clock tick. |
|
682 */ |
|
683 |
|
684 void QDeclarativeTimeLine::updateCurrentTime(int v) |
|
685 { |
|
686 if (d->syncAdj == -1) |
|
687 d->syncAdj = v; |
|
688 v -= d->syncAdj; |
|
689 |
|
690 int timeChanged = v - d->prevTime; |
|
691 #if 0 |
|
692 if (!timeChanged) |
|
693 return; |
|
694 #endif |
|
695 d->prevTime = v; |
|
696 d->advance(timeChanged); |
|
697 emit updated(); |
|
698 |
|
699 // Do we need to stop the clock? |
|
700 if (d->ops.isEmpty()) { |
|
701 stop(); |
|
702 d->prevTime = 0; |
|
703 d->clockRunning = false; |
|
704 emit completed(); |
|
705 } /*else if (pauseTime > 0) { |
|
706 GfxClock::cancelClock(); |
|
707 d->prevTime = 0; |
|
708 GfxClock::pauseFor(pauseTime); |
|
709 d->syncAdj = 0; |
|
710 d->clockRunning = false; |
|
711 }*/ else if (/*!GfxClock::isActive()*/ state() != Running) { |
|
712 stop(); |
|
713 d->prevTime = 0; |
|
714 d->clockRunning = true; |
|
715 d->syncAdj = 0; |
|
716 start(); |
|
717 } |
|
718 } |
|
719 |
|
720 bool operator<(const QPair<int, Update> &lhs, |
|
721 const QPair<int, Update> &rhs) |
|
722 { |
|
723 return lhs.first < rhs.first; |
|
724 } |
|
725 |
|
726 int QDeclarativeTimeLinePrivate::advance(int t) |
|
727 { |
|
728 int pauseTime = -1; |
|
729 |
|
730 // XXX - surely there is a more efficient way? |
|
731 do { |
|
732 pauseTime = -1; |
|
733 // Minimal advance time |
|
734 int advanceTime = t; |
|
735 for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) { |
|
736 TimeLine &tl = *iter; |
|
737 Op &op = tl.ops.first(); |
|
738 int length = op.length - tl.consumedOpLength; |
|
739 |
|
740 if (length < advanceTime) { |
|
741 advanceTime = length; |
|
742 if (advanceTime == 0) |
|
743 break; |
|
744 } |
|
745 } |
|
746 t -= advanceTime; |
|
747 |
|
748 // Process until then. A zero length advance time will only process |
|
749 // sets. |
|
750 QList<QPair<int, Update> > updates; |
|
751 |
|
752 for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) { |
|
753 QDeclarativeTimeLineValue *v = static_cast<QDeclarativeTimeLineValue *>(iter.key()); |
|
754 TimeLine &tl = *iter; |
|
755 Q_ASSERT(!tl.ops.isEmpty()); |
|
756 |
|
757 do { |
|
758 Op &op = tl.ops.first(); |
|
759 if (advanceTime == 0 && op.length != 0) |
|
760 continue; |
|
761 |
|
762 if (tl.consumedOpLength == 0 && |
|
763 op.type != Op::Pause && |
|
764 op.type != Op::Execute) |
|
765 tl.base = v->value(); |
|
766 |
|
767 if ((tl.consumedOpLength + advanceTime) == op.length) { |
|
768 if (op.type == Op::Execute) { |
|
769 updates << qMakePair(op.order, Update(op.event)); |
|
770 } else { |
|
771 bool changed = false; |
|
772 qreal val = value(op, op.length, tl.base, &changed); |
|
773 if (changed) |
|
774 updates << qMakePair(op.order, Update(v, val)); |
|
775 } |
|
776 tl.length -= qMin(advanceTime, tl.length); |
|
777 tl.consumedOpLength = 0; |
|
778 tl.ops.removeFirst(); |
|
779 } else { |
|
780 tl.consumedOpLength += advanceTime; |
|
781 bool changed = false; |
|
782 qreal val = value(op, tl.consumedOpLength, tl.base, &changed); |
|
783 if (changed) |
|
784 updates << qMakePair(op.order, Update(v, val)); |
|
785 tl.length -= qMin(advanceTime, tl.length); |
|
786 break; |
|
787 } |
|
788 |
|
789 } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0); |
|
790 |
|
791 |
|
792 if (tl.ops.isEmpty()) { |
|
793 iter = ops.erase(iter); |
|
794 v->_t = 0; |
|
795 } else { |
|
796 if (tl.ops.first().type == Op::Pause && pauseTime != 0) { |
|
797 int opPauseTime = tl.ops.first().length - tl.consumedOpLength; |
|
798 if (pauseTime == -1 || opPauseTime < pauseTime) |
|
799 pauseTime = opPauseTime; |
|
800 } else { |
|
801 pauseTime = 0; |
|
802 } |
|
803 ++iter; |
|
804 } |
|
805 } |
|
806 |
|
807 length -= qMin(length, advanceTime); |
|
808 syncPoint -= advanceTime; |
|
809 |
|
810 qSort(updates.begin(), updates.end()); |
|
811 updateQueue = &updates; |
|
812 for (int ii = 0; ii < updates.count(); ++ii) { |
|
813 const Update &v = updates.at(ii).second; |
|
814 if (v.g) { |
|
815 v.g->setValue(v.v); |
|
816 } else { |
|
817 v.e.d0(v.e.d1); |
|
818 } |
|
819 } |
|
820 updateQueue = 0; |
|
821 } while(t); |
|
822 |
|
823 return pauseTime; |
|
824 } |
|
825 |
|
826 void QDeclarativeTimeLine::remove(QDeclarativeTimeLineObject *v) |
|
827 { |
|
828 QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(v); |
|
829 Q_ASSERT(iter != d->ops.end()); |
|
830 |
|
831 int len = iter->length; |
|
832 d->ops.erase(iter); |
|
833 if (len == d->length) { |
|
834 // We need to recalculate the length |
|
835 d->length = 0; |
|
836 for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin(); |
|
837 iter != d->ops.end(); |
|
838 ++iter) { |
|
839 |
|
840 if (iter->length > d->length) |
|
841 d->length = iter->length; |
|
842 |
|
843 } |
|
844 } |
|
845 if (d->ops.isEmpty()) { |
|
846 stop(); |
|
847 d->clockRunning = false; |
|
848 } else if (/*!GfxClock::isActive()*/ state() != Running) { |
|
849 stop(); |
|
850 d->prevTime = 0; |
|
851 d->clockRunning = true; |
|
852 |
|
853 if (d->syncMode == QDeclarativeTimeLine::LocalSync) { |
|
854 d->syncAdj = -1; |
|
855 } else { |
|
856 d->syncAdj = 0; |
|
857 } |
|
858 start(); |
|
859 } |
|
860 |
|
861 if (d->updateQueue) { |
|
862 for (int ii = 0; ii < d->updateQueue->count(); ++ii) { |
|
863 if (d->updateQueue->at(ii).second.g == v || |
|
864 d->updateQueue->at(ii).second.e.callbackObject() == v) { |
|
865 d->updateQueue->removeAt(ii); |
|
866 --ii; |
|
867 } |
|
868 } |
|
869 } |
|
870 |
|
871 |
|
872 } |
|
873 |
|
874 /*! |
|
875 \internal |
|
876 \class QDeclarativeTimeLineValue |
|
877 \brief The QDeclarativeTimeLineValue class provides a value that can be modified by QDeclarativeTimeLine. |
|
878 */ |
|
879 |
|
880 /*! |
|
881 \fn QDeclarativeTimeLineValue::QDeclarativeTimeLineValue(qreal value = 0) |
|
882 |
|
883 Construct a new QDeclarativeTimeLineValue with an initial \a value. |
|
884 */ |
|
885 |
|
886 /*! |
|
887 \fn qreal QDeclarativeTimeLineValue::value() const |
|
888 |
|
889 Return the current value. |
|
890 */ |
|
891 |
|
892 /*! |
|
893 \fn void QDeclarativeTimeLineValue::setValue(qreal value) |
|
894 |
|
895 Set the current \a value. |
|
896 */ |
|
897 |
|
898 /*! |
|
899 \fn QDeclarativeTimeLine *QDeclarativeTimeLineValue::timeLine() const |
|
900 |
|
901 If a QDeclarativeTimeLine is operating on this value, return a pointer to it, |
|
902 otherwise return null. |
|
903 */ |
|
904 |
|
905 |
|
906 QDeclarativeTimeLineObject::QDeclarativeTimeLineObject() |
|
907 : _t(0) |
|
908 { |
|
909 } |
|
910 |
|
911 QDeclarativeTimeLineObject::~QDeclarativeTimeLineObject() |
|
912 { |
|
913 if (_t) { |
|
914 _t->remove(this); |
|
915 _t = 0; |
|
916 } |
|
917 } |
|
918 |
|
919 QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback() |
|
920 : d0(0), d1(0), d2(0) |
|
921 { |
|
922 } |
|
923 |
|
924 QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback f, void *d) |
|
925 : d0(f), d1(d), d2(b) |
|
926 { |
|
927 } |
|
928 |
|
929 QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o) |
|
930 : d0(o.d0), d1(o.d1), d2(o.d2) |
|
931 { |
|
932 } |
|
933 |
|
934 QDeclarativeTimeLineCallback &QDeclarativeTimeLineCallback::operator=(const QDeclarativeTimeLineCallback &o) |
|
935 { |
|
936 d0 = o.d0; |
|
937 d1 = o.d1; |
|
938 d2 = o.d2; |
|
939 return *this; |
|
940 } |
|
941 |
|
942 QDeclarativeTimeLineObject *QDeclarativeTimeLineCallback::callbackObject() const |
|
943 { |
|
944 return d2; |
|
945 } |
|
946 |
|
947 QT_END_NAMESPACE |