1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickanimation_p.h"
43 #include "qquickanimation_p_p.h"
44 #include "qquickstateoperations_p.h"
46 #include <private/qdeclarativeproperty_p.h>
47 #include <private/qdeclarativepath_p.h>
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qsequentialanimationgroup.h>
52 #include <QtCore/qparallelanimationgroup.h>
53 #include <QtGui/qtransform.h>
58 \qmlclass ParentAnimation QQuickParentAnimation
59 \inqmlmodule QtQuick 2
60 \ingroup qml-animation-transition
63 \brief The ParentAnimation element animates changes in parent values.
65 ParentAnimation is used to animate a parent change for an \l Item.
67 For example, the following ParentChange changes \c blueRect to become
68 a child of \c redRect when it is clicked. The inclusion of the
69 ParentAnimation, which defines a NumberAnimation to be applied during
70 the transition, ensures the item animates smoothly as it moves to
73 \snippet doc/src/snippets/declarative/parentanimation.qml 0
75 A ParentAnimation can contain any number of animations. These animations will
76 be run in parallel; to run them sequentially, define them within a
79 In some cases, such as when reparenting between items with clipping enabled, it is useful
80 to animate the parent change via another item that does not have clipping
81 enabled. Such an item can be set using the \l via property.
83 For convenience, when a ParentAnimation is used in a \l Transition, it will
84 animate any ParentChange that has occurred during the state change.
85 This can be overridden by setting a specific target item using the
88 Like any other animation element, a ParentAnimation can be applied in a
89 number of ways, including transitions, behaviors and property value
90 sources. The \l {QML Animation and Transitions} documentation shows a
91 variety of methods for creating animations.
93 \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
95 QQuickParentAnimation::QQuickParentAnimation(QObject *parent)
96 : QDeclarativeAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
98 Q_D(QQuickParentAnimation);
99 d->topLevelGroup = new QSequentialAnimationGroup;
100 QDeclarative_setParent_noEvent(d->topLevelGroup, this);
102 d->startAction = new QActionAnimation;
103 QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup);
104 d->topLevelGroup->addAnimation(d->startAction);
106 d->ag = new QParallelAnimationGroup;
107 QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup);
108 d->topLevelGroup->addAnimation(d->ag);
110 d->endAction = new QActionAnimation;
111 QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup);
112 d->topLevelGroup->addAnimation(d->endAction);
115 QQuickParentAnimation::~QQuickParentAnimation()
120 \qmlproperty Item QtQuick2::ParentAnimation::target
121 The item to reparent.
123 When used in a transition, if no target is specified, all
124 ParentChange occurrences are animated by the ParentAnimation.
126 QQuickItem *QQuickParentAnimation::target() const
128 Q_D(const QQuickParentAnimation);
132 void QQuickParentAnimation::setTarget(QQuickItem *target)
134 Q_D(QQuickParentAnimation);
135 if (target == d->target)
139 emit targetChanged();
143 \qmlproperty Item QtQuick2::ParentAnimation::newParent
144 The new parent to animate to.
146 If the ParentAnimation is defined within a \l Transition or \l Behavior,
147 this value defaults to the value defined in the end state of the
148 \l Transition, or the value of the property change that triggered the
151 QQuickItem *QQuickParentAnimation::newParent() const
153 Q_D(const QQuickParentAnimation);
157 void QQuickParentAnimation::setNewParent(QQuickItem *newParent)
159 Q_D(QQuickParentAnimation);
160 if (newParent == d->newParent)
163 d->newParent = newParent;
164 emit newParentChanged();
168 \qmlproperty Item QtQuick2::ParentAnimation::via
169 The item to reparent via. This provides a way to do an unclipped animation
170 when both the old parent and new parent are clipped.
180 QQuickItem *QQuickParentAnimation::via() const
182 Q_D(const QQuickParentAnimation);
186 void QQuickParentAnimation::setVia(QQuickItem *via)
188 Q_D(QQuickParentAnimation);
196 //### mirrors same-named function in QQuickItem
197 QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const
201 case QQuickItem::TopLeft:
202 return QPointF(0, 0);
203 case QQuickItem::Top:
204 return QPointF(width / 2., 0);
205 case QQuickItem::TopRight:
206 return QPointF(width, 0);
207 case QQuickItem::Left:
208 return QPointF(0, height / 2.);
209 case QQuickItem::Center:
210 return QPointF(width / 2., height / 2.);
211 case QQuickItem::Right:
212 return QPointF(width, height / 2.);
213 case QQuickItem::BottomLeft:
214 return QPointF(0, height);
215 case QQuickItem::Bottom:
216 return QPointF(width / 2., height);
217 case QQuickItem::BottomRight:
218 return QPointF(width, height);
222 void QQuickParentAnimation::transition(QDeclarativeStateActions &actions,
223 QDeclarativeProperties &modified,
224 TransitionDirection direction)
226 Q_D(QQuickParentAnimation);
228 struct QQuickParentAnimationData : public QAbstractAnimationAction
230 QQuickParentAnimationData() {}
231 ~QQuickParentAnimationData() { qDeleteAll(pc); }
233 QDeclarativeStateActions actions;
234 //### reverse should probably apply on a per-action basis
236 QList<QQuickParentChange *> pc;
237 virtual void doAction()
239 for (int ii = 0; ii < actions.count(); ++ii) {
240 const QDeclarativeAction &action = actions.at(ii);
242 action.event->reverse();
244 action.event->execute();
249 QQuickParentAnimationData *data = new QQuickParentAnimationData;
250 QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
252 bool hasExplicit = false;
253 if (d->target && d->newParent) {
254 data->reverse = false;
255 QDeclarativeAction myAction;
256 QQuickParentChange *pc = new QQuickParentChange;
257 pc->setObject(d->target);
258 pc->setParent(d->newParent);
261 data->actions << myAction;
264 viaData->reverse = false;
265 QDeclarativeAction myVAction;
266 QQuickParentChange *vpc = new QQuickParentChange;
267 vpc->setObject(d->target);
268 vpc->setParent(d->via);
269 myVAction.event = vpc;
271 viaData->actions << myVAction;
273 //### once actions have concept of modified,
274 // loop to match appropriate ParentChanges and mark as modified
278 for (int i = 0; i < actions.size(); ++i) {
279 QDeclarativeAction &action = actions[i];
280 if (action.event && action.event->typeName() == QLatin1String("ParentChange")
281 && (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {
283 QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
284 QDeclarativeAction myAction = action;
285 data->reverse = action.reverseEvent;
287 //### this logic differs from PropertyAnimation
288 // (probably a result of modified vs. done)
290 QQuickParentChange *epc = new QQuickParentChange;
291 epc->setObject(static_cast<QQuickParentChange*>(action.event)->object());
292 epc->setParent(d->newParent);
293 myAction.event = epc;
295 data->actions << myAction;
298 action.actionDone = true;
299 data->actions << myAction;
303 viaData->reverse = false;
304 QDeclarativeAction myAction;
305 QQuickParentChange *vpc = new QQuickParentChange;
306 vpc->setObject(pc->object());
307 vpc->setParent(d->via);
308 myAction.event = vpc;
310 viaData->actions << myAction;
311 QDeclarativeAction dummyAction;
312 QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
313 QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
314 QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
315 QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
316 QQuickItem *target = pc->object();
317 QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
319 //### this mirrors the logic in QQuickParentChange.
321 const QTransform &transform = targetParent->itemTransform(d->via, &ok);
322 if (transform.type() >= QTransform::TxShear || !ok) {
323 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
329 bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
330 if (ok && !isRotate) {
331 if (transform.m11() == transform.m22())
332 scale = transform.m11();
334 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
337 } else if (ok && isRotate) {
338 if (transform.m11() == transform.m22())
339 scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
341 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
346 rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
348 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
353 const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
356 if (ok && target->transformOrigin() != QQuickItem::TopLeft) {
357 qreal w = target->width();
358 qreal h = target->height();
359 if (pc->widthIsSet() && i < actions.size() - 1)
360 w = actions[++i].toValue.toReal();
361 if (pc->heightIsSet() && i < actions.size() - 1)
362 h = actions[++i].toValue.toReal();
363 const QPointF &transformOrigin
364 = d->computeTransformOrigin(target->transformOrigin(), w,h);
365 qreal tempxt = transformOrigin.x();
366 qreal tempyt = transformOrigin.y();
368 t.translate(-tempxt, -tempyt);
370 t.scale(scale, scale);
371 t.translate(tempxt, tempyt);
372 const QPointF &offset = t.map(QPointF(0,0));
378 //qDebug() << x << y << rotation << scale;
381 sAction.toValue = sAction.toValue.toReal() * scale;
382 rAction.toValue = rAction.toValue.toReal() + rotation;
388 if (data->actions.count()) {
389 if (direction == QDeclarativeAbstractAnimation::Forward) {
390 d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
391 d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
393 d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
394 d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
401 //take care of any child animations
402 bool valid = d->defaultProperty.isValid();
403 for (int ii = 0; ii < d->animations.count(); ++ii) {
405 d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
406 d->animations.at(ii)->transition(actions, modified, direction);
411 QAbstractAnimation *QQuickParentAnimation::qtAnimation()
413 Q_D(QQuickParentAnimation);
414 return d->topLevelGroup;
418 \qmlclass AnchorAnimation QQuickAnchorAnimation
419 \inqmlmodule QtQuick 2
420 \ingroup qml-animation-transition
422 \brief The AnchorAnimation element animates changes in anchor values.
424 AnchorAnimation is used to animate an anchor change.
426 In the following snippet we animate the addition of a right anchor to a \l Rectangle:
428 \snippet doc/src/snippets/declarative/anchoranimation.qml 0
430 For convenience, when an AnchorAnimation is used in a \l Transition, it will
431 animate any AnchorChanges that have occurred during the state change.
432 This can be overridden by setting a specific target item using the
435 Like any other animation element, an AnchorAnimation can be applied in a
436 number of ways, including transitions, behaviors and property value
437 sources. The \l {QML Animation and Transitions} documentation shows a
438 variety of methods for creating animations.
440 \sa {QML Animation and Transitions}, AnchorChanges
442 QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
443 : QDeclarativeAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
445 Q_D(QQuickAnchorAnimation);
446 d->va = new QDeclarativeBulkValueAnimator;
447 QDeclarative_setParent_noEvent(d->va, this);
450 QQuickAnchorAnimation::~QQuickAnchorAnimation()
454 QAbstractAnimation *QQuickAnchorAnimation::qtAnimation()
456 Q_D(QQuickAnchorAnimation);
461 \qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
462 The items to reanchor.
464 If no targets are specified all AnchorChanges will be
465 animated by the AnchorAnimation.
467 QDeclarativeListProperty<QQuickItem> QQuickAnchorAnimation::targets()
469 Q_D(QQuickAnchorAnimation);
470 return QDeclarativeListProperty<QQuickItem>(this, d->targets);
474 \qmlproperty int QtQuick2::AnchorAnimation::duration
475 This property holds the duration of the animation, in milliseconds.
477 The default value is 250.
479 int QQuickAnchorAnimation::duration() const
481 Q_D(const QQuickAnchorAnimation);
482 return d->va->duration();
485 void QQuickAnchorAnimation::setDuration(int duration)
488 qmlInfo(this) << tr("Cannot set a duration of < 0");
492 Q_D(QQuickAnchorAnimation);
493 if (d->va->duration() == duration)
495 d->va->setDuration(duration);
496 emit durationChanged(duration);
500 \qmlproperty enumeration QtQuick2::AnchorAnimation::easing.type
501 \qmlproperty real QtQuick2::AnchorAnimation::easing.amplitude
502 \qmlproperty real QtQuick2::AnchorAnimation::easing.overshoot
503 \qmlproperty real QtQuick2::AnchorAnimation::easing.period
504 \brief the easing curve used for the animation.
506 To specify an easing curve you need to specify at least the type. For some curves you can also specify
507 amplitude, period and/or overshoot. The default easing curve is
511 AnchorAnimation { easing.type: Easing.InOutQuad }
514 See the \l{PropertyAnimation::easing.type} documentation for information
515 about the different types of easing curves.
517 QEasingCurve QQuickAnchorAnimation::easing() const
519 Q_D(const QQuickAnchorAnimation);
520 return d->va->easingCurve();
523 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
525 Q_D(QQuickAnchorAnimation);
526 if (d->va->easingCurve() == e)
529 d->va->setEasingCurve(e);
530 emit easingChanged(e);
533 void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions,
534 QDeclarativeProperties &modified,
535 TransitionDirection direction)
538 Q_D(QQuickAnchorAnimation);
539 QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
540 data->interpolatorType = QMetaType::QReal;
541 data->interpolator = d->interpolator;
543 data->reverse = direction == Backward ? true : false;
544 data->fromSourced = false;
545 data->fromDefined = false;
547 for (int ii = 0; ii < actions.count(); ++ii) {
548 QDeclarativeAction &action = actions[ii];
549 if (action.event && action.event->typeName() == QLatin1String("AnchorChanges")
550 && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
551 data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
555 if (data->actions.count()) {
556 if (!d->rangeIsSet) {
557 d->va->setStartValue(qreal(0));
558 d->va->setEndValue(qreal(1));
559 d->rangeIsSet = true;
561 d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
562 d->va->setFromSourcedValue(&data->fromSourced);
569 \qmlclass PathAnimation QQuickPathAnimation
570 \inqmlmodule QtQuick 2
571 \ingroup qml-animation-transition
574 \brief The PathAnimation element animates an item along a path.
576 When used in a transition, the path can be specified without start
577 or end points, for example:
582 PathCurve { x: 100; y: 100}
583 PathCurve {} //last element is empty with no end point specified
588 In the above case, the path start will be the item's current position, and the
589 path end will be the item's target position in the target state.
591 \sa {QML Animation and Transitions}, PathInterpolator
593 QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
594 : QDeclarativeAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
596 Q_D(QQuickPathAnimation);
597 d->pa = new QDeclarativeBulkValueAnimator;
598 QDeclarative_setParent_noEvent(d->pa, this);
601 QQuickPathAnimation::~QQuickPathAnimation()
606 \qmlproperty int QtQuick2::PathAnimation::duration
607 This property holds the duration of the animation, in milliseconds.
609 The default value is 250.
611 int QQuickPathAnimation::duration() const
613 Q_D(const QQuickPathAnimation);
614 return d->pa->duration();
617 void QQuickPathAnimation::setDuration(int duration)
620 qmlInfo(this) << tr("Cannot set a duration of < 0");
624 Q_D(QQuickPathAnimation);
625 if (d->pa->duration() == duration)
627 d->pa->setDuration(duration);
628 emit durationChanged(duration);
632 \qmlproperty enumeration QtQuick2::PathAnimation::easing.type
633 \qmlproperty real QtQuick2::PathAnimation::easing.amplitude
634 \qmlproperty list<real> QtQuick2::PathAnimation::easing.bezierCurve
635 \qmlproperty real QtQuick2::PathAnimation::easing.overshoot
636 \qmlproperty real QtQuick2::PathAnimation::easing.period
637 \brief the easing curve used for the animation.
639 To specify an easing curve you need to specify at least the type. For some curves you can also specify
640 amplitude, period, overshoot or custom bezierCurve data. The default easing curve is \c Easing.Linear.
642 See the \l{PropertyAnimation::easing.type} documentation for information
643 about the different types of easing curves.
645 QEasingCurve QQuickPathAnimation::easing() const
647 Q_D(const QQuickPathAnimation);
648 return d->pa->easingCurve();
651 void QQuickPathAnimation::setEasing(const QEasingCurve &e)
653 Q_D(QQuickPathAnimation);
654 if (d->pa->easingCurve() == e)
657 d->pa->setEasingCurve(e);
658 emit easingChanged(e);
662 \qmlproperty Path QtQuick2::PathAnimation::path
663 This property holds the path to animate along.
665 For more information on defining a path see the \l Path documentation.
667 QDeclarativePath *QQuickPathAnimation::path() const
669 Q_D(const QQuickPathAnimation);
673 void QQuickPathAnimation::setPath(QDeclarativePath *path)
675 Q_D(QQuickPathAnimation);
684 \qmlproperty Item QtQuick2::PathAnimation::target
685 This property holds the item to animate.
687 QQuickItem *QQuickPathAnimation::target() const
689 Q_D(const QQuickPathAnimation);
693 void QQuickPathAnimation::setTarget(QQuickItem *target)
695 Q_D(QQuickPathAnimation);
696 if (d->target == target)
700 emit targetChanged();
704 \qmlproperty enumeration QtQuick2::PathAnimation::orientation
705 This property controls the rotation of the item as it animates along the path.
707 If a value other than \c Fixed is specified, the PathAnimation will rotate the
708 item to achieve the specified orientation as it travels along the path.
711 \o PathAnimation.Fixed (default) - the PathAnimation will not control
712 the rotation of the item.
713 \o PathAnimation.RightFirst - The right side of the item will lead along the path.
714 \o PathAnimation.LeftFirst - The left side of the item will lead along the path.
715 \o PathAnimation.BottomFirst - The bottom of the item will lead along the path.
716 \o PathAnimation.TopFirst - The top of the item will lead along the path.
719 QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
721 Q_D(const QQuickPathAnimation);
722 return d->orientation;
725 void QQuickPathAnimation::setOrientation(Orientation orientation)
727 Q_D(QQuickPathAnimation);
728 if (d->orientation == orientation)
731 d->orientation = orientation;
732 emit orientationChanged(d->orientation);
736 \qmlproperty point QtQuick2::PathAnimation::anchorPoint
737 This property holds the anchor point for the item being animated.
739 By default, the upper-left corner of the target (its 0,0 point)
740 will be anchored to (or follow) the path. The anchorPoint property can be used to
741 specify a different point for anchoring. For example, specifying an anchorPoint of
742 5,5 for a 10x10 item means the center of the item will follow the path.
744 QPointF QQuickPathAnimation::anchorPoint() const
746 Q_D(const QQuickPathAnimation);
747 return d->anchorPoint;
750 void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
752 Q_D(QQuickPathAnimation);
753 if (d->anchorPoint == point)
756 d->anchorPoint = point;
757 emit anchorPointChanged(point);
761 \qmlproperty real QtQuick2::PathAnimation::orientationEntryDuration
762 This property holds the duration (in milliseconds) of the transition in to the orientation.
764 If an orientation has been specified for the PathAnimation, and the starting
765 rotation of the item does not match that given by the orientation,
766 orientationEntryDuration can be used to smoothly transition from the item's
767 starting rotation to the rotation given by the path orientation.
769 int QQuickPathAnimation::orientationEntryDuration() const
771 Q_D(const QQuickPathAnimation);
772 return d->entryDuration;
775 void QQuickPathAnimation::setOrientationEntryDuration(int duration)
777 Q_D(QQuickPathAnimation);
778 if (d->entryDuration == duration)
780 d->entryDuration = duration;
781 emit orientationEntryDurationChanged(duration);
785 \qmlproperty real QtQuick2::PathAnimation::orientationExitDuration
786 This property holds the duration (in milliseconds) of the transition out of the orientation.
788 If an orientation and endRotation have been specified for the PathAnimation,
789 orientationExitDuration can be used to smoothly transition from the rotation given
790 by the path orientation to the specified endRotation.
792 int QQuickPathAnimation::orientationExitDuration() const
794 Q_D(const QQuickPathAnimation);
795 return d->exitDuration;
798 void QQuickPathAnimation::setOrientationExitDuration(int duration)
800 Q_D(QQuickPathAnimation);
801 if (d->exitDuration == duration)
803 d->exitDuration = duration;
804 emit orientationExitDurationChanged(duration);
808 \qmlproperty real QtQuick2::PathAnimation::endRotation
809 This property holds the ending rotation for the target.
811 If an orientation has been specified for the PathAnimation,
812 and the path doesn't end with the item at the desired rotation,
813 the endRotation property can be used to manually specify an end
816 This property is typically used with orientationExitDuration, as specifying
817 an endRotation without an orientationExitDuration may cause a jump to
818 the final rotation, rather than a smooth transition.
820 qreal QQuickPathAnimation::endRotation() const
822 Q_D(const QQuickPathAnimation);
823 return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
826 void QQuickPathAnimation::setEndRotation(qreal rotation)
828 Q_D(QQuickPathAnimation);
829 if (!d->endRotation.isNull && d->endRotation == rotation)
832 d->endRotation = rotation;
833 emit endRotationChanged(d->endRotation);
837 QAbstractAnimation *QQuickPathAnimation::qtAnimation()
839 Q_D(QQuickPathAnimation);
843 void QQuickPathAnimation::transition(QDeclarativeStateActions &actions,
844 QDeclarativeProperties &modified,
845 TransitionDirection direction)
847 Q_D(QQuickPathAnimation);
848 QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater;
850 data->orientation = d->orientation;
851 data->anchorPoint = d->anchorPoint;
852 data->entryInterval = duration() ? qreal(d->entryDuration) / duration() : qreal(0);
853 data->exitInterval = duration() ? qreal(d->exitDuration) / duration() : qreal(0);
854 data->endRotation = d->endRotation;
855 data->reverse = direction == Backward ? true : false;
856 data->fromSourced = false;
857 data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
858 data->toDefined = d->path ? d->path->hasEnd() : false;
859 int origModifiedSize = modified.count();
861 for (int i = 0; i < actions.count(); ++i) {
862 QDeclarativeAction &action = actions[i];
865 if (action.specifiedObject == d->target && action.property.name() == QLatin1String("x")) {
866 data->toX = action.toValue.toReal();
867 modified << action.property;
868 action.fromValue = action.toValue;
870 if (action.specifiedObject == d->target && action.property.name() == QLatin1String("y")) {
871 data->toY = action.toValue.toReal();
872 modified << action.property;
873 action.fromValue = action.toValue;
877 if (d->target && d->path &&
878 (modified.count() > origModifiedSize || data->toDefined)) {
879 data->target = d->target;
880 data->path = d->path;
881 data->path->invalidateSequentialHistory();
882 if (!d->rangeIsSet) {
883 d->pa->setStartValue(qreal(0));
884 d->pa->setEndValue(qreal(1));
885 d->rangeIsSet = true;
888 NOTE: The following block relies on the fact that the previous value hasn't
889 yet been deleted, and has the same target, etc, which may be a bit fragile.
891 if (d->pa->getAnimValue()) {
892 QQuickPathAnimationUpdater *prevData = static_cast<QQuickPathAnimationUpdater*>(d->pa->getAnimValue());
894 // get the original start angle that was used (so we can exactly reverse).
895 data->startRotation = prevData->startRotation;
897 // treat interruptions specially, otherwise we end up with strange paths
898 if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) {
899 if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) {
900 QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV);
901 if (!prevData->anchorPoint.isNull())
902 pathPos -= prevData->anchorPoint;
903 if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
904 data->painterPath = prevData->painterPath;
905 data->toDefined = data->fromDefined = data->fromSourced = true;
906 data->prevBez.isValid = false;
907 data->interruptStart = prevData->currentV;
908 data->startRotation = prevData->startRotation;
909 data->pathLength = prevData->pathLength;
910 data->attributePoints = prevData->attributePoints;
915 d->pa->setFromSourcedValue(&data->fromSourced);
916 d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
918 d->pa->setFromSourcedValue(0);
919 d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped);
924 void QQuickPathAnimationUpdater::setValue(qreal v)
926 if (interruptStart.isValid()) {
929 qreal end = reverse ? 0.0 : 1.0;
930 v = interruptStart + v * (end-interruptStart);
933 bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
934 if (!fromSourced && (!fromDefined || !toDefined)) {
935 qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
936 qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
937 qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
938 qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
940 prevBez.isValid = false;
941 painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
946 bool fixed = orientation == QQuickPathAnimation::Fixed;
947 QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
949 //adjust position according to anchor point
950 if (!anchorPoint.isNull()) {
951 currentPos -= anchorPoint;
953 if (!anchorPoint.isNull() && !fixed)
954 target->setTransformOriginPoint(anchorPoint);
958 //### could cache properties rather than reconstructing each time
959 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("x")), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
960 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("y")), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
962 //adjust angle according to orientation
964 switch (orientation) {
965 case QQuickPathAnimation::RightFirst:
968 case QQuickPathAnimation::TopFirst:
971 case QQuickPathAnimation::LeftFirst:
972 angle = -angle + 180;
974 case QQuickPathAnimation::BottomFirst:
975 angle = -angle + 270;
982 if (atStart && !reverse) {
983 startRotation = target->rotation();
985 //shortest distance to correct orientation
986 qreal diff = angle - startRotation;
987 while (diff > 180.0) {
988 startRotation.value += 360.0;
991 while (diff < -180.0) {
992 startRotation.value -= 360.0;
997 //smoothly transition to the desired orientation
998 //TODO: shortest distance calculations
999 if (startRotation.isValid()) {
1000 if (reverse && v == 0.0)
1001 angle = startRotation;
1002 else if (v < entryInterval)
1003 angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
1005 if (endRotation.isValid()) {
1006 qreal exitStart = 1 - entryInterval;
1007 if (!reverse && v == 1.0)
1008 angle = endRotation;
1009 else if (v > exitStart)
1010 angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
1012 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("rotation")), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
1016 NOTE: we don't always reset the transform origin, as it can cause a
1017 visual jump if ending on an angle. This means that in some cases
1018 (anchor point and orientation both specified, and ending at an angle)
1019 the transform origin will always be set after running the path animation.
1021 if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
1022 if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
1023 target->setTransformOriginPoint(QPointF());