/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt scene graph research project. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qxparentanimation_p.h" #include "qxparentanimation_p_p.h" #include "qxstateoperations_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QxParentAnimation::QxParentAnimation(QObject *parent) : QDeclarativeAnimationGroup(*(new QxParentAnimationPrivate), parent) { Q_D(QxParentAnimation); d->topLevelGroup = new QSequentialAnimationGroup; QDeclarative_setParent_noEvent(d->topLevelGroup, this); d->startAction = new QActionAnimation; QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup); d->topLevelGroup->addAnimation(d->startAction); d->ag = new QParallelAnimationGroup; QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup); d->topLevelGroup->addAnimation(d->ag); d->endAction = new QActionAnimation; QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup); d->topLevelGroup->addAnimation(d->endAction); } QxParentAnimation::~QxParentAnimation() { } /*! \qmlproperty Item ParentAnimation::target The item to reparent. When used in a transition, if no target is specified all ParentChanges will be animated by the ParentAnimation. */ QxItem *QxParentAnimation::target() const { Q_D(const QxParentAnimation); return d->target; } void QxParentAnimation::setTarget(QxItem *target) { Q_D(QxParentAnimation); if (target == d->target) return; d->target = target; emit targetChanged(); } /*! \qmlproperty Item ParentAnimation::newParent The new parent to animate to. If not set, then the parent defined in the end state of the transition. */ QxItem *QxParentAnimation::newParent() const { Q_D(const QxParentAnimation); return d->newParent; } void QxParentAnimation::setNewParent(QxItem *newParent) { Q_D(QxParentAnimation); if (newParent == d->newParent) return; d->newParent = newParent; emit newParentChanged(); } /*! \qmlproperty Item ParentAnimation::via The item to reparent via. This provides a way to do an unclipped animation when both the old parent and new parent are clipped \qml ParentAnimation { target: myItem via: topLevelItem ... } \endqml */ QxItem *QxParentAnimation::via() const { Q_D(const QxParentAnimation); return d->via; } void QxParentAnimation::setVia(QxItem *via) { Q_D(QxParentAnimation); if (via == d->via) return; d->via = via; emit viaChanged(); } //### mirrors same-named function in QxItem QPointF QxParentAnimationPrivate::computeTransformOrigin(QxItem::TransformOrigin origin, qreal width, qreal height) const { switch(origin) { default: case QxItem::TopLeft: return QPointF(0, 0); case QxItem::Top: return QPointF(width / 2., 0); case QxItem::TopRight: return QPointF(width, 0); case QxItem::Left: return QPointF(0, height / 2.); case QxItem::Center: return QPointF(width / 2., height / 2.); case QxItem::Right: return QPointF(width, height / 2.); case QxItem::BottomLeft: return QPointF(0, height); case QxItem::Bottom: return QPointF(width / 2., height); case QxItem::BottomRight: return QPointF(width, height); } } void QxParentAnimation::transition(QDeclarativeStateActions &actions, QDeclarativeProperties &modified, TransitionDirection direction) { Q_D(QxParentAnimation); struct QxParentAnimationData : public QAbstractAnimationAction { QxParentAnimationData() {} ~QxParentAnimationData() { qDeleteAll(pc); } QDeclarativeStateActions actions; //### reverse should probably apply on a per-action basis bool reverse; QList pc; virtual void doAction() { for (int ii = 0; ii < actions.count(); ++ii) { const QDeclarativeAction &action = actions.at(ii); if (reverse) action.event->reverse(); else action.event->execute(); } } }; QxParentAnimationData *data = new QxParentAnimationData; QxParentAnimationData *viaData = new QxParentAnimationData; bool hasExplicit = false; if (d->target && d->newParent) { data->reverse = false; QDeclarativeAction myAction; QxParentChange *pc = new QxParentChange; pc->setObject(d->target); pc->setParent(d->newParent); myAction.event = pc; data->pc << pc; data->actions << myAction; hasExplicit = true; if (d->via) { viaData->reverse = false; QDeclarativeAction myVAction; QxParentChange *vpc = new QxParentChange; vpc->setObject(d->target); vpc->setParent(d->via); myVAction.event = vpc; viaData->pc << vpc; viaData->actions << myVAction; } //### once actions have concept of modified, // loop to match appropriate ParentChanges and mark as modified } if (!hasExplicit) for (int i = 0; i < actions.size(); ++i) { QDeclarativeAction &action = actions[i]; if (action.event && action.event->typeName() == QLatin1String("ParentChange") && (!d->target || static_cast(action.event)->object() == d->target)) { QxParentChange *pc = static_cast(action.event); QDeclarativeAction myAction = action; data->reverse = action.reverseEvent; //### this logic differs from PropertyAnimation // (probably a result of modified vs. done) if (d->newParent) { QxParentChange *epc = new QxParentChange; epc->setObject(static_cast(action.event)->object()); epc->setParent(d->newParent); myAction.event = epc; data->pc << epc; data->actions << myAction; pc = epc; } else { action.actionDone = true; data->actions << myAction; } if (d->via) { viaData->reverse = false; QDeclarativeAction myAction; QxParentChange *vpc = new QxParentChange; vpc->setObject(pc->object()); vpc->setParent(d->via); myAction.event = vpc; viaData->pc << vpc; viaData->actions << myAction; QDeclarativeAction dummyAction; QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; QxItem *target = pc->object(); QxItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent(); //### this mirrors the logic in QxParentChange. bool ok; const QTransform &transform = targetParent->itemTransform(d->via, &ok); if (transform.type() >= QTransform::TxShear || !ok) { qmlInfo(this) << QxParentAnimation::tr("Unable to preserve appearance under complex transform"); ok = false; } qreal scale = 1; qreal rotation = 0; if (ok && transform.type() != QTransform::TxRotate) { if (transform.m11() == transform.m22()) scale = transform.m11(); else { qmlInfo(this) << QxParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); ok = false; } } else if (ok && transform.type() == QTransform::TxRotate) { if (transform.m11() == transform.m22()) scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); else { qmlInfo(this) << QxParentAnimation::tr("Unable to preserve appearance under non-uniform scale"); ok = false; } if (scale != 0) rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; else { qmlInfo(this) << QxParentAnimation::tr("Unable to preserve appearance under scale of 0"); ok = false; } } const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal())); qreal x = point.x(); qreal y = point.y(); if (ok && target->transformOrigin() != QxItem::TopLeft) { qreal w = target->width(); qreal h = target->height(); if (pc->widthIsSet() && i < actions.size() - 1) w = actions[++i].toValue.toReal(); if (pc->heightIsSet() && i < actions.size() - 1) h = actions[++i].toValue.toReal(); const QPointF &transformOrigin = d->computeTransformOrigin(target->transformOrigin(), w,h); qreal tempxt = transformOrigin.x(); qreal tempyt = transformOrigin.y(); QTransform t; t.translate(-tempxt, -tempyt); t.rotate(rotation); t.scale(scale, scale); t.translate(tempxt, tempyt); const QPointF &offset = t.map(QPointF(0,0)); x += offset.x(); y += offset.y(); } if (ok) { //qDebug() << x << y << rotation << scale; xAction.toValue = x; yAction.toValue = y; sAction.toValue = sAction.toValue.toReal() * scale; rAction.toValue = rAction.toValue.toReal() + rotation; } } } } if (data->actions.count()) { if (direction == QDeclarativeAbstractAnimation::Forward) { d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); } else { d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped); d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped); } } else { delete data; delete viaData; } //take care of any child animations bool valid = d->defaultProperty.isValid(); for (int ii = 0; ii < d->animations.count(); ++ii) { if (valid) d->animations.at(ii)->setDefaultTarget(d->defaultProperty); d->animations.at(ii)->transition(actions, modified, direction); } } QAbstractAnimation *QxParentAnimation::qtAnimation() { Q_D(QxParentAnimation); return d->topLevelGroup; }