Esempio n. 1
0
QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &actions,
                        QQmlProperties &modified,
                        TransitionDirection direction,
                        QObject *defaultTarget)
{
    Q_D(QQuickParentAnimation);

    struct QQuickParentAnimationData : public QAbstractAnimationAction
    {
        QQuickParentAnimationData() : reverse(false) {}
        ~QQuickParentAnimationData() { qDeleteAll(pc); }

        QQuickStateActions actions;
        //### reverse should probably apply on a per-action basis
        bool reverse;
        QList<QQuickParentChange *> pc;
        virtual void doAction()
        {
            for (int ii = 0; ii < actions.count(); ++ii) {
                const QQuickAction &action = actions.at(ii);
                if (reverse)
                    action.event->reverse();
                else
                    action.event->execute();
            }
        }
    };

    QQuickParentAnimationData *data = new QQuickParentAnimationData;
    QQuickParentAnimationData *viaData = new QQuickParentAnimationData;

    bool hasExplicit = false;
    if (d->target && d->newParent) {
        data->reverse = false;
        QQuickAction myAction;
        QQuickParentChange *pc = new QQuickParentChange;
        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;
            QQuickAction myVAction;
            QQuickParentChange *vpc = new QQuickParentChange;
            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) {
        QQuickAction &action = actions[i];
        if (action.event && action.event->type() == QQuickActionEvent::ParentChange
            && (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {

            QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
            QQuickAction myAction = action;
            data->reverse = action.reverseEvent;

            //### this logic differs from PropertyAnimation
            //    (probably a result of modified vs. done)
            if (d->newParent) {
                QQuickParentChange *epc = new QQuickParentChange;
                epc->setObject(static_cast<QQuickParentChange*>(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;
                QQuickAction myAction;
                QQuickParentChange *vpc = new QQuickParentChange;
                vpc->setObject(pc->object());
                vpc->setParent(d->via);
                myAction.event = vpc;
                viaData->pc << vpc;
                viaData->actions << myAction;
                QQuickAction dummyAction;
                QQuickAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
                QQuickAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
                QQuickAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
                QQuickAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
                QQuickItem *target = pc->object();
                QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();

                //### this mirrors the logic in QQuickParentChange.
                bool ok;
                const QTransform &transform = targetParent->itemTransform(d->via, &ok);
                if (transform.type() >= QTransform::TxShear || !ok) {
                    qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
                    ok = false;
                }

                qreal scale = 1;
                qreal rotation = 0;
                bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
                if (ok && !isRotate) {
                    if (transform.m11() == transform.m22())
                        scale = transform.m11();
                    else {
                        qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
                        ok = false;
                    }
                } else if (ok && isRotate) {
                    if (transform.m11() == transform.m22())
                        scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
                    else {
                        qmlInfo(this) << QQuickParentAnimation::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) << QQuickParentAnimation::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() != QQuickItem::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()) {
        QSequentialAnimationGroupJob *topLevelGroup = new QSequentialAnimationGroupJob;
        QActionAnimation *viaAction = d->via ? new QActionAnimation : 0;
        QActionAnimation *targetAction = new QActionAnimation;
        //we'll assume the common case by far is to have children, and always create ag
        QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;

        if (d->via)
            viaAction->setAnimAction(viaData);
        targetAction->setAnimAction(data);

        //take care of any child animations
        bool valid = d->defaultProperty.isValid();
        QAbstractAnimationJob* anim;
        for (int ii = 0; ii < d->animations.count(); ++ii) {
            if (valid)
                d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
            anim = d->animations.at(ii)->transition(actions, modified, direction, defaultTarget);
            if (anim)
                ag->appendAnimation(anim);
        }

        //TODO: simplify/clarify logic
        bool forwards = direction == QQuickAbstractAnimation::Forward;
        if (forwards) {
            topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
            topLevelGroup->appendAnimation(ag);
            if (d->via)
                topLevelGroup->appendAnimation(targetAction);
        } else {
            if (d->via)
                topLevelGroup->appendAnimation(targetAction);
            topLevelGroup->appendAnimation(ag);
            topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
        }
        return initInstance(topLevelGroup);
    } else {
        delete data;
        delete viaData;
    }
    return 0;
}
void tst_QPauseAnimationJob::multipleSequentialGroups()
{
    EnableConsistentTiming enabled;

    QParallelAnimationGroupJob group;
    group.setLoopCount(2);

    QSequentialAnimationGroupJob subgroup1;
    group.appendAnimation(&subgroup1);

    TestableGenericAnimation animation(300);
    subgroup1.appendAnimation(&animation);

    TestablePauseAnimation pause(200);
    subgroup1.appendAnimation(&pause);

    QSequentialAnimationGroupJob subgroup2;
    group.appendAnimation(&subgroup2);

    TestableGenericAnimation animation2(200);
    subgroup2.appendAnimation(&animation2);

    TestablePauseAnimation pause2(250);
    subgroup2.appendAnimation(&pause2);

    QSequentialAnimationGroupJob subgroup3;
    group.appendAnimation(&subgroup3);

    TestablePauseAnimation pause3(400);
    subgroup3.appendAnimation(&pause3);

    TestableGenericAnimation animation3(200);
    subgroup3.appendAnimation(&animation3);

    QSequentialAnimationGroupJob subgroup4;
    group.appendAnimation(&subgroup4);

    TestablePauseAnimation pause4(310);
    subgroup4.appendAnimation(&pause4);

    TestablePauseAnimation pause5(60);
    subgroup4.appendAnimation(&pause5);

    group.start();

    QVERIFY(group.state() == QAbstractAnimationJob::Running);
    QVERIFY(subgroup1.state() == QAbstractAnimationJob::Running);
    QVERIFY(subgroup2.state() == QAbstractAnimationJob::Running);
    QVERIFY(subgroup3.state() == QAbstractAnimationJob::Running);
    QVERIFY(subgroup4.state() == QAbstractAnimationJob::Running);

    // This is a pretty long animation so it tends to get rather out of sync
    // when using the consistent timer, so run for an extra half second for good
    // measure...
    QTest::qWait(group.totalDuration() + 500);

#ifdef Q_OS_WIN
    if (group.state() != QAbstractAnimationJob::Stopped)
        QEXPECT_FAIL("", winTimerError, Abort);
#endif
    QTRY_VERIFY(group.state() == QAbstractAnimationJob::Stopped);

#ifdef Q_OS_WIN
    if (subgroup1.state() != QAbstractAnimationJob::Stopped)
        QEXPECT_FAIL("", winTimerError, Abort);
#endif
    QVERIFY(subgroup1.state() == QAbstractAnimationJob::Stopped);

#ifdef Q_OS_WIN
    if (subgroup2.state() != QAbstractAnimationJob::Stopped)
        QEXPECT_FAIL("", winTimerError, Abort);
#endif
    QVERIFY(subgroup2.state() == QAbstractAnimationJob::Stopped);

#ifdef Q_OS_WIN
    if (subgroup3.state() != QAbstractAnimationJob::Stopped)
        QEXPECT_FAIL("", winTimerError, Abort);
#endif
    QVERIFY(subgroup3.state() == QAbstractAnimationJob::Stopped);

#ifdef Q_OS_WIN
    if (subgroup4.state() != QAbstractAnimationJob::Stopped)
        QEXPECT_FAIL("", winTimerError, Abort);
#endif
    QVERIFY(subgroup4.state() == QAbstractAnimationJob::Stopped);

#ifdef Q_OS_WIN
    if (pause5.m_updateCurrentTimeCount != 4)
        QEXPECT_FAIL("", winTimerError, Abort);
#endif
    QCOMPARE(pause5.m_updateCurrentTimeCount, 4);
}