Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsganimation.cpp
1 // Commit: 91501cc9b542de644cd70098a6bc5ff738cdeb49
2 /****************************************************************************
3 **
4 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 **
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
15 ** this package.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qsganimation_p.h"
44 #include "qsganimation_p_p.h"
45 #include "qsgstateoperations_p.h"
46
47 #include <QtDeclarative/qdeclarativeinfo.h>
48 #include <QtCore/qmath.h>
49 #include <QtCore/qsequentialanimationgroup.h>
50 #include <QtCore/qparallelanimationgroup.h>
51
52 QT_BEGIN_NAMESPACE
53
54 QSGParentAnimation::QSGParentAnimation(QObject *parent)
55     : QDeclarativeAnimationGroup(*(new QSGParentAnimationPrivate), parent)
56 {
57     Q_D(QSGParentAnimation);
58     d->topLevelGroup = new QSequentialAnimationGroup;
59     QDeclarative_setParent_noEvent(d->topLevelGroup, this);
60
61     d->startAction = new QActionAnimation;
62     QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup);
63     d->topLevelGroup->addAnimation(d->startAction);
64
65     d->ag = new QParallelAnimationGroup;
66     QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup);
67     d->topLevelGroup->addAnimation(d->ag);
68
69     d->endAction = new QActionAnimation;
70     QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup);
71     d->topLevelGroup->addAnimation(d->endAction);
72 }
73
74 QSGParentAnimation::~QSGParentAnimation()
75 {
76 }
77
78 QSGItem *QSGParentAnimation::target() const
79 {
80     Q_D(const QSGParentAnimation);
81     return d->target;
82 }
83
84 void QSGParentAnimation::setTarget(QSGItem *target)
85 {
86     Q_D(QSGParentAnimation);
87     if (target == d->target)
88         return;
89
90     d->target = target;
91     emit targetChanged();
92 }
93
94 QSGItem *QSGParentAnimation::newParent() const
95 {
96     Q_D(const QSGParentAnimation);
97     return d->newParent;
98 }
99
100 void QSGParentAnimation::setNewParent(QSGItem *newParent)
101 {
102     Q_D(QSGParentAnimation);
103     if (newParent == d->newParent)
104         return;
105
106     d->newParent = newParent;
107     emit newParentChanged();
108 }
109
110 QSGItem *QSGParentAnimation::via() const
111 {
112     Q_D(const QSGParentAnimation);
113     return d->via;
114 }
115
116 void QSGParentAnimation::setVia(QSGItem *via)
117 {
118     Q_D(QSGParentAnimation);
119     if (via == d->via)
120         return;
121
122     d->via = via;
123     emit viaChanged();
124 }
125
126 //### mirrors same-named function in QSGItem
127 QPointF QSGParentAnimationPrivate::computeTransformOrigin(QSGItem::TransformOrigin origin, qreal width, qreal height) const
128 {
129     switch(origin) {
130     default:
131     case QSGItem::TopLeft:
132         return QPointF(0, 0);
133     case QSGItem::Top:
134         return QPointF(width / 2., 0);
135     case QSGItem::TopRight:
136         return QPointF(width, 0);
137     case QSGItem::Left:
138         return QPointF(0, height / 2.);
139     case QSGItem::Center:
140         return QPointF(width / 2., height / 2.);
141     case QSGItem::Right:
142         return QPointF(width, height / 2.);
143     case QSGItem::BottomLeft:
144         return QPointF(0, height);
145     case QSGItem::Bottom:
146         return QPointF(width / 2., height);
147     case QSGItem::BottomRight:
148         return QPointF(width, height);
149     }
150 }
151
152 void QSGParentAnimation::transition(QDeclarativeStateActions &actions,
153                         QDeclarativeProperties &modified,
154                         TransitionDirection direction)
155 {
156     Q_D(QSGParentAnimation);
157
158     struct QSGParentAnimationData : public QAbstractAnimationAction
159     {
160         QSGParentAnimationData() {}
161         ~QSGParentAnimationData() { qDeleteAll(pc); }
162
163         QDeclarativeStateActions actions;
164         //### reverse should probably apply on a per-action basis
165         bool reverse;
166         QList<QSGParentChange *> pc;
167         virtual void doAction()
168         {
169             for (int ii = 0; ii < actions.count(); ++ii) {
170                 const QDeclarativeAction &action = actions.at(ii);
171                 if (reverse)
172                     action.event->reverse();
173                 else
174                     action.event->execute();
175             }
176         }
177     };
178
179     QSGParentAnimationData *data = new QSGParentAnimationData;
180     QSGParentAnimationData *viaData = new QSGParentAnimationData;
181
182     bool hasExplicit = false;
183     if (d->target && d->newParent) {
184         data->reverse = false;
185         QDeclarativeAction myAction;
186         QSGParentChange *pc = new QSGParentChange;
187         pc->setObject(d->target);
188         pc->setParent(d->newParent);
189         myAction.event = pc;
190         data->pc << pc;
191         data->actions << myAction;
192         hasExplicit = true;
193         if (d->via) {
194             viaData->reverse = false;
195             QDeclarativeAction myVAction;
196             QSGParentChange *vpc = new QSGParentChange;
197             vpc->setObject(d->target);
198             vpc->setParent(d->via);
199             myVAction.event = vpc;
200             viaData->pc << vpc;
201             viaData->actions << myVAction;
202         }
203         //### once actions have concept of modified,
204         //    loop to match appropriate ParentChanges and mark as modified
205     }
206
207     if (!hasExplicit)
208     for (int i = 0; i < actions.size(); ++i) {
209         QDeclarativeAction &action = actions[i];
210         if (action.event && action.event->typeName() == QLatin1String("ParentChange")
211             && (!d->target || static_cast<QSGParentChange*>(action.event)->object() == d->target)) {
212
213             QSGParentChange *pc = static_cast<QSGParentChange*>(action.event);
214             QDeclarativeAction myAction = action;
215             data->reverse = action.reverseEvent;
216
217             //### this logic differs from PropertyAnimation
218             //    (probably a result of modified vs. done)
219             if (d->newParent) {
220                 QSGParentChange *epc = new QSGParentChange;
221                 epc->setObject(static_cast<QSGParentChange*>(action.event)->object());
222                 epc->setParent(d->newParent);
223                 myAction.event = epc;
224                 data->pc << epc;
225                 data->actions << myAction;
226                 pc = epc;
227             } else {
228                 action.actionDone = true;
229                 data->actions << myAction;
230             }
231
232             if (d->via) {
233                 viaData->reverse = false;
234                 QDeclarativeAction myAction;
235                 QSGParentChange *vpc = new QSGParentChange;
236                 vpc->setObject(pc->object());
237                 vpc->setParent(d->via);
238                 myAction.event = vpc;
239                 viaData->pc << vpc;
240                 viaData->actions << myAction;
241                 QDeclarativeAction dummyAction;
242                 QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
243                 QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
244                 QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
245                 QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
246                 QSGItem *target = pc->object();
247                 QSGItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
248
249                 //### this mirrors the logic in QSGParentChange.
250                 bool ok;
251                 const QTransform &transform = targetParent->itemTransform(d->via, &ok);
252                 if (transform.type() >= QTransform::TxShear || !ok) {
253                     qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under complex transform");
254                     ok = false;
255                 }
256
257                 qreal scale = 1;
258                 qreal rotation = 0;
259                 bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
260                 if (ok && !isRotate) {
261                     if (transform.m11() == transform.m22())
262                         scale = transform.m11();
263                     else {
264                         qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
265                         ok = false;
266                     }
267                 } else if (ok && isRotate) {
268                     if (transform.m11() == transform.m22())
269                         scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
270                     else {
271                         qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
272                         ok = false;
273                     }
274
275                     if (scale != 0)
276                         rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
277                     else {
278                         qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under scale of 0");
279                         ok = false;
280                     }
281                 }
282
283                 const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
284                 qreal x = point.x();
285                 qreal y = point.y();
286                 if (ok && target->transformOrigin() != QSGItem::TopLeft) {
287                     qreal w = target->width();
288                     qreal h = target->height();
289                     if (pc->widthIsSet() && i < actions.size() - 1)
290                         w = actions[++i].toValue.toReal();
291                     if (pc->heightIsSet() && i < actions.size() - 1)
292                         h = actions[++i].toValue.toReal();
293                     const QPointF &transformOrigin
294                             = d->computeTransformOrigin(target->transformOrigin(), w,h);
295                     qreal tempxt = transformOrigin.x();
296                     qreal tempyt = transformOrigin.y();
297                     QTransform t;
298                     t.translate(-tempxt, -tempyt);
299                     t.rotate(rotation);
300                     t.scale(scale, scale);
301                     t.translate(tempxt, tempyt);
302                     const QPointF &offset = t.map(QPointF(0,0));
303                     x += offset.x();
304                     y += offset.y();
305                 }
306
307                 if (ok) {
308                     //qDebug() << x << y << rotation << scale;
309                     xAction.toValue = x;
310                     yAction.toValue = y;
311                     sAction.toValue = sAction.toValue.toReal() * scale;
312                     rAction.toValue = rAction.toValue.toReal() + rotation;
313                 }
314             }
315         }
316     }
317
318     if (data->actions.count()) {
319         if (direction == QDeclarativeAbstractAnimation::Forward) {
320             d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
321             d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
322         } else {
323             d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
324             d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
325         }
326     } else {
327         delete data;
328         delete viaData;
329     }
330
331     //take care of any child animations
332     bool valid = d->defaultProperty.isValid();
333     for (int ii = 0; ii < d->animations.count(); ++ii) {
334         if (valid)
335             d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
336         d->animations.at(ii)->transition(actions, modified, direction);
337     }
338
339 }
340
341 QAbstractAnimation *QSGParentAnimation::qtAnimation()
342 {
343     Q_D(QSGParentAnimation);
344     return d->topLevelGroup;
345 }
346
347 QSGAnchorAnimation::QSGAnchorAnimation(QObject *parent)
348 : QDeclarativeAbstractAnimation(*(new QSGAnchorAnimationPrivate), parent)
349 {
350     Q_D(QSGAnchorAnimation);
351     d->va = new QDeclarativeBulkValueAnimator;
352     QDeclarative_setParent_noEvent(d->va, this);
353 }
354
355 QSGAnchorAnimation::~QSGAnchorAnimation()
356 {
357 }
358
359 QAbstractAnimation *QSGAnchorAnimation::qtAnimation()
360 {
361     Q_D(QSGAnchorAnimation);
362     return d->va;
363 }
364
365 QDeclarativeListProperty<QSGItem> QSGAnchorAnimation::targets()
366 {
367     Q_D(QSGAnchorAnimation);
368     return QDeclarativeListProperty<QSGItem>(this, d->targets);
369 }
370
371 int QSGAnchorAnimation::duration() const
372 {
373     Q_D(const QSGAnchorAnimation);
374     return d->va->duration();
375 }
376
377 void QSGAnchorAnimation::setDuration(int duration)
378 {
379     if (duration < 0) {
380         qmlInfo(this) << tr("Cannot set a duration of < 0");
381         return;
382     }
383
384     Q_D(QSGAnchorAnimation);
385     if (d->va->duration() == duration)
386         return;
387     d->va->setDuration(duration);
388     emit durationChanged(duration);
389 }
390
391 QEasingCurve QSGAnchorAnimation::easing() const
392 {
393     Q_D(const QSGAnchorAnimation);
394     return d->va->easingCurve();
395 }
396
397 void QSGAnchorAnimation::setEasing(const QEasingCurve &e)
398 {
399     Q_D(QSGAnchorAnimation);
400     if (d->va->easingCurve() == e)
401         return;
402
403     d->va->setEasingCurve(e);
404     emit easingChanged(e);
405 }
406
407 void QSGAnchorAnimation::transition(QDeclarativeStateActions &actions,
408                         QDeclarativeProperties &modified,
409                         TransitionDirection direction)
410 {
411     Q_UNUSED(modified);
412     Q_D(QSGAnchorAnimation);
413     QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
414     data->interpolatorType = QMetaType::QReal;
415     data->interpolator = d->interpolator;
416
417     data->reverse = direction == Backward ? true : false;
418     data->fromSourced = false;
419     data->fromDefined = false;
420
421     for (int ii = 0; ii < actions.count(); ++ii) {
422         QDeclarativeAction &action = actions[ii];
423         if (action.event && action.event->typeName() == QLatin1String("AnchorChanges")
424             && (d->targets.isEmpty() || d->targets.contains(static_cast<QSGAnchorChanges*>(action.event)->object()))) {
425             data->actions << static_cast<QSGAnchorChanges*>(action.event)->additionalActions();
426         }
427     }
428
429     if (data->actions.count()) {
430         if (!d->rangeIsSet) {
431             d->va->setStartValue(qreal(0));
432             d->va->setEndValue(qreal(1));
433             d->rangeIsSet = true;
434         }
435         d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
436         d->va->setFromSourcedValue(&data->fromSourced);
437     } else {
438         delete data;
439     }
440 }
441
442 QT_END_NAMESPACE