summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/animation/stickman/lifecycle.cpp4
-rw-r--r--examples/statemachine/errorstate/mainwindow.cpp16
-rw-r--r--examples/statemachine/errorstate/mainwindow.h1
-rw-r--r--src/corelib/animation/qvariantanimation.cpp24
-rw-r--r--src/corelib/statemachine/qstate.cpp16
-rw-r--r--src/corelib/statemachine/qstate_p.h3
-rw-r--r--src/corelib/statemachine/qstatemachine.cpp247
-rw-r--r--src/corelib/statemachine/qstatemachine.h4
-rw-r--r--src/corelib/statemachine/qstatemachine_p.h8
-rw-r--r--tests/auto/qstatemachine/tst_qstatemachine.cpp180
10 files changed, 358 insertions, 145 deletions
diff --git a/examples/animation/stickman/lifecycle.cpp b/examples/animation/stickman/lifecycle.cpp
index df69d47..b22af55 100644
--- a/examples/animation/stickman/lifecycle.cpp
+++ b/examples/animation/stickman/lifecycle.cpp
@@ -203,14 +203,14 @@ QState *LifeCycle::makeState(QState *parentState, const QString &animationFileNa
topLevel->setInitialState(frameState);
} else {
connectByAnimation(previousState, frameState,
- new QSignalTransition(m_machine, SIGNAL(animationsFinished())));
+ new QSignalTransition(previousState, SIGNAL(polished())));
}
previousState = frameState;
}
// Loop
connectByAnimation(previousState, topLevel->initialState(),
- new QSignalTransition(m_machine, SIGNAL(animationsFinished())));
+ new QSignalTransition(previousState, SIGNAL(polished())));
return topLevel;
diff --git a/examples/statemachine/errorstate/mainwindow.cpp b/examples/statemachine/errorstate/mainwindow.cpp
index f5edf60..e04ca94 100644
--- a/examples/statemachine/errorstate/mainwindow.cpp
+++ b/examples/statemachine/errorstate/mainwindow.cpp
@@ -110,44 +110,41 @@ void MainWindow::init()
QAction *quitAction = menuBar()->addAction("&Quit");
connect(addTankAction, SIGNAL(triggered()), this, SLOT(addTank()));
- connect(stopGameAction, SIGNAL(triggered()), this, SIGNAL(gameOver()));
connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
m_machine = new QStateMachine(this);
m_machine->setGlobalRestorePolicy(QStateMachine::RestoreProperties);
- QState *stoppedState = new QState(m_machine->rootState());
+ QState *stoppedState = new QState(m_machine->rootState());
stoppedState->setObjectName("stoppedState");
-
stoppedState->assignProperty(runGameAction, "enabled", true);
stoppedState->assignProperty(stopGameAction, "enabled", false);
stoppedState->assignProperty(this, "started", false);
m_machine->setInitialState(stoppedState);
QState *spawnsAvailable = new QState(stoppedState);
- spawnsAvailable->assignProperty(addTankAction, "enabled", true);
spawnsAvailable->setObjectName("spawnsAvailable");
+ spawnsAvailable->assignProperty(addTankAction, "enabled", true);
QState *noSpawnsAvailable = new QState(stoppedState);
+ noSpawnsAvailable->setObjectName("noSpawnsAvailable");
noSpawnsAvailable->assignProperty(addTankAction, "enabled", false);
spawnsAvailable->addTransition(this, SIGNAL(mapFull()), noSpawnsAvailable);
QHistoryState *hs = new QHistoryState(stoppedState);
- hs->setObjectName("hs");
hs->setDefaultState(spawnsAvailable);
stoppedState->setInitialState(hs);
- m_runningState = new QState(QState::ParallelGroup, m_machine->rootState());
+ m_runningState = new QState(QState::ParallelStates, m_machine->rootState());
+ m_runningState->setObjectName("runningState");
m_runningState->assignProperty(addTankAction, "enabled", false);
m_runningState->assignProperty(runGameAction, "enabled", false);
m_runningState->assignProperty(stopGameAction, "enabled", true);
stoppedState->addTransition(runGameAction, SIGNAL(triggered()), m_runningState);
- m_runningState->addTransition(this, SIGNAL(gameOver()), stoppedState);
-
- m_machine->start();
+ m_runningState->addTransition(stopGameAction, SIGNAL(triggered()), stoppedState);
QTimer *timer = new QTimer(this);
timer->setInterval(100);
@@ -155,6 +152,7 @@ void MainWindow::init()
connect(m_runningState, SIGNAL(entered()), timer, SLOT(start()));
connect(m_runningState, SIGNAL(exited()), timer, SLOT(stop()));
+ m_machine->start();
m_time.start();
}
diff --git a/examples/statemachine/errorstate/mainwindow.h b/examples/statemachine/errorstate/mainwindow.h
index 33122eb..622dabe 100644
--- a/examples/statemachine/errorstate/mainwindow.h
+++ b/examples/statemachine/errorstate/mainwindow.h
@@ -25,7 +25,6 @@ public slots:
void runStep();
signals:
- void gameOver();
void mapFull();
private:
diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp
index 9f8cbf0..bb6cf1c 100644
--- a/src/corelib/animation/qvariantanimation.cpp
+++ b/src/corelib/animation/qvariantanimation.cpp
@@ -175,26 +175,16 @@ void QVariantAnimationPrivate::updateCurrentValue()
endProgress = currentInterval.end.first;
const qreal localProgress = (progress - startProgress) / (endProgress - startProgress);
- bool changed = false;
- {
- //we do that here in a limited scope so that ret is dereferenced and frees memory
- //and the call to updateCurrentValue can recreate QVariant of the same type (for ex. in
- //QGraphicsItem::setPos
- QVariant ret = q->interpolated(currentInterval.start.second,
- currentInterval.end.second,
- localProgress);
- if (currentValue != ret) {
- changed = true;
- qSwap(currentValue, ret);
- }
- }
-
- if (changed) {
- //the value has changed
- q->updateCurrentValue(currentValue);
+ QVariant ret = q->interpolated(currentInterval.start.second,
+ currentInterval.end.second,
+ localProgress);
+ qSwap(currentValue, ret);
+ q->updateCurrentValue(currentValue);
#ifndef QT_EXPERIMENTAL_SOLUTION
if (connectedSignals & changedSignalMask)
#endif
+ if (currentValue != ret) {
+ //the value has changed
emit q->valueChanged(currentValue);
}
}
diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp
index 173cc8d..2d59ab8 100644
--- a/src/corelib/statemachine/qstate.cpp
+++ b/src/corelib/statemachine/qstate.cpp
@@ -139,6 +139,12 @@ void QStatePrivate::emitFinished()
emit q->finished();
}
+void QStatePrivate::emitPolished()
+{
+ Q_Q(QState);
+ emit q->polished();
+}
+
/*!
Constructs a new state with the given \a parent state.
*/
@@ -222,6 +228,8 @@ QList<QAbstractTransition*> QStatePrivate::transitions() const
/*!
Instructs this state to set the property with the given \a name of the given
\a object to the given \a value when the state is entered.
+
+ \sa polished()
*/
void QState::assignProperty(QObject *object, const char *name,
const QVariant &value)
@@ -444,4 +452,12 @@ bool QState::event(QEvent *e)
This signal is emitted when a final child state of this state is entered.
*/
+/*!
+ \fn QState::polished()
+
+ This signal is emitted when all properties have been assigned their final value.
+
+ \sa QState::assignProperty(), QAbstractTransition::addAnimation()
+*/
+
QT_END_NAMESPACE
diff --git a/src/corelib/statemachine/qstate_p.h b/src/corelib/statemachine/qstate_p.h
index 0c8c858..1f913b4 100644
--- a/src/corelib/statemachine/qstate_p.h
+++ b/src/corelib/statemachine/qstate_p.h
@@ -63,6 +63,8 @@ QT_BEGIN_NAMESPACE
struct QPropertyAssignment
{
+ QPropertyAssignment()
+ : object(0) {}
QPropertyAssignment(QObject *o, const QByteArray &n,
const QVariant &v, bool es = true)
: object(o), propertyName(n), value(v), explicitlySet(es)
@@ -92,6 +94,7 @@ public:
QList<QAbstractTransition*> transitions() const;
void emitFinished();
+ void emitPolished();
QAbstractState *errorState;
QAbstractState *initialState;
diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
index 8dfb229..9060f0e 100644
--- a/src/corelib/statemachine/qstatemachine.cpp
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -375,13 +375,13 @@ void QStateMachinePrivate::microstep(const QList<QAbstractTransition*> &enabledT
qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ")";
qDebug() << q_func() << ": configuration before exiting states:" << configuration;
#endif
- exitStates(enabledTransitions);
+ QList<QAbstractState*> exitedStates = exitStates(enabledTransitions);
#ifdef QSTATEMACHINE_DEBUG
qDebug() << q_func() << ": configuration after exiting states:" << configuration;
#endif
executeTransitionContent(enabledTransitions);
QList<QAbstractState*> enteredStates = enterStates(enabledTransitions);
- applyProperties(enabledTransitions, enteredStates);
+ applyProperties(enabledTransitions, exitedStates, enteredStates);
#ifdef QSTATEMACHINE_DEBUG
qDebug() << q_func() << ": configuration after entering states:" << configuration;
qDebug() << q_func() << ": end microstep";
@@ -632,39 +632,15 @@ void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root,
}
void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &transitionList,
+ const QList<QAbstractState*> &exitedStates,
const QList<QAbstractState*> &enteredStates)
{
-#ifndef QT_NO_ANIMATION
Q_Q(QStateMachine);
- // Gracefully terminate playing animations.
- for (int i = 0; i < playingAnimations.size(); ++i)
- playingAnimations.at(i)->stop();
- playingAnimations.clear();
- for (int i = 0; i < resetEndValues.size(); ++i)
- qobject_cast<QVariantAnimation*>(resetEndValues.at(i))->setEndValue(QVariant()); // ### generalize
- resetEndValues.clear();
-
- // Find the animations to use for the state change.
- QList<QAbstractAnimation*> selectedAnimations;
- if (animationsEnabled) {
- for (int i = 0; i < transitionList.size(); ++i) {
- QAbstractTransition *transition = transitionList.at(i);
-
- selectedAnimations << transition->animations();
- selectedAnimations << defaultAnimationsForSource.values(transition->sourceState());
-
- QList<QAbstractState *> targetStates = transition->targetStates();
- for (int j=0; j<targetStates.size(); ++j)
- selectedAnimations << defaultAnimationsForTarget.values(targetStates.at(j));
- }
- selectedAnimations << defaultAnimations;
- }
-#else
+#ifdef QT_NO_ANIMATION
Q_UNUSED(transitionList);
#endif
-
- // Process the SetProperty definitions of the entered states.
- QList<QPropertyAssignment> propertyAssignments;
+ // Process the property assignments of the entered states.
+ QHash<QAbstractState*, QList<QPropertyAssignment> > propertyAssignmentsForState;
QHash<RestorableId, QVariant> pendingRestorables = registeredRestorables;
for (int i = 0; i < enteredStates.size(); ++i) {
QState *s = qobject_cast<QState*>(enteredStates.at(i));
@@ -678,53 +654,114 @@ void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &tr
registerRestorable(assn.object, assn.propertyName);
}
pendingRestorables.remove(RestorableId(assn.object, assn.propertyName));
- propertyAssignments.append(assn);
+ propertyAssignmentsForState[s].append(assn);
}
}
- propertyAssignments << restorablesToPropertyList(pendingRestorables);
+ if (!pendingRestorables.isEmpty()) {
+ QAbstractState *s;
+ if (!enteredStates.isEmpty())
+ s = enteredStates.last(); // ### handle if parallel
+ else
+ s = 0;
+ propertyAssignmentsForState[s] << restorablesToPropertyList(pendingRestorables);
+ }
#ifndef QT_NO_ANIMATION
- // Set the animated properties that did not finish animating and that are not
- // set in the new state.
- for (int i = 0; i < propertiesForAnimations.size(); ++i) {
- QPropertyAssignment assn = propertiesForAnimations.at(i).second;
- bool found = false;
- for (int j = 0; j < propertyAssignments.size(); ++j) {
- if ((propertyAssignments.at(j).object == assn.object)
- && (propertyAssignments.at(j).propertyName == assn.propertyName)) {
- found = true;
- break;
+ // Gracefully terminate playing animations for states that are exited.
+ for (int i = 0; i < exitedStates.size(); ++i) {
+ QAbstractState *s = exitedStates.at(i);
+ QList<QAbstractAnimation*> animations = animationsForState.take(s);
+ for (int j = 0; j < animations.size(); ++j) {
+ QAbstractAnimation *anim = animations.at(j);
+ QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
+ stateForAnimation.remove(anim);
+ if (resetAnimationEndValues.contains(anim)) {
+ qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize
+ resetAnimationEndValues.remove(anim);
+ }
+ QPropertyAssignment assn = propertyForAnimation.take(anim);
+ Q_ASSERT(assn.object != 0);
+ // If there is no property assignment that sets this property,
+ // set the property to its target value.
+ bool found = false;
+ QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it;
+ for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) {
+ const QList<QPropertyAssignment> &assignments = it.value();
+ for (int k = 0; k < assignments.size(); ++k) {
+ if ((assignments.at(k).object == assn.object)
+ && (assignments.at(k).propertyName == assn.propertyName)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ assn.object->setProperty(assn.propertyName, assn.value);
}
+ // Stop the (top-level) animation.
+ // ### Stopping nested animation has weird behavior.
+ while (QAnimationGroup *group = anim->group())
+ anim = group;
+ anim->stop();
}
- if (!found) {
- assn.object->setProperty(assn.propertyName, assn.value);
+ }
+
+ // Find the animations to use for the state change.
+ QList<QAbstractAnimation*> selectedAnimations;
+ if (animationsEnabled) {
+ for (int i = 0; i < transitionList.size(); ++i) {
+ QAbstractTransition *transition = transitionList.at(i);
+
+ selectedAnimations << transition->animations();
+ selectedAnimations << defaultAnimationsForSource.values(transition->sourceState());
+
+ QList<QAbstractState *> targetStates = transition->targetStates();
+ for (int j=0; j<targetStates.size(); ++j)
+ selectedAnimations << defaultAnimationsForTarget.values(targetStates.at(j));
}
+ selectedAnimations << defaultAnimations;
}
- // Initialize animations from SetProperty definitions.
- propertiesForAnimations.clear();
+ // Initialize animations from property assignments.
for (int i = 0; i < selectedAnimations.size(); ++i) {
QAbstractAnimation *anim = selectedAnimations.at(i);
- QList<QPropertyAssignment>::iterator it;
- for (it = propertyAssignments.begin(); it != propertyAssignments.end(); ) {
- QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret;
- ret = initializeAnimation(anim, *it);
- QList<QAbstractAnimation*> handlers = ret.first;
- if (!handlers.isEmpty()) {
- for (int j = 0; j < handlers.size(); ++j)
- propertiesForAnimations.append(qMakePair(handlers.at(j), *it));
- it = propertyAssignments.erase(it);
- } else {
- ++it;
+ QHash<QAbstractState*, QList<QPropertyAssignment> >::iterator it;
+ for (it = propertyAssignmentsForState.begin(); it != propertyAssignmentsForState.end(); ) {
+ QList<QPropertyAssignment>::iterator it2;
+ QAbstractState *s = it.key();
+ QList<QPropertyAssignment> &assignments = it.value();
+ for (it2 = assignments.begin(); it2 != assignments.end(); ) {
+ QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret;
+ ret = initializeAnimation(anim, *it2);
+ QList<QAbstractAnimation*> handlers = ret.first;
+ if (!handlers.isEmpty()) {
+ for (int j = 0; j < handlers.size(); ++j) {
+ QAbstractAnimation *a = handlers.at(j);
+ propertyForAnimation.insert(a, *it2);
+ stateForAnimation.insert(a, s);
+ animationsForState[s].append(a);
+ // ### connect to just the top-level animation?
+ QObject::disconnect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
+ QObject::connect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
+ }
+ it2 = assignments.erase(it2);
+ } else {
+ ++it2;
+ }
+ for (int j = 0; j < ret.second.size(); ++j)
+ resetAnimationEndValues.insert(ret.second.at(j));
}
- resetEndValues << ret.second;
+ if (assignments.isEmpty())
+ it = propertyAssignmentsForState.erase(it);
+ else
+ ++it;
}
-
// We require that at least one animation is valid.
// ### generalize
QList<QVariantAnimation*> variantAnims = qFindChildren<QVariantAnimation*>(anim);
if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(anim))
variantAnims.append(va);
+
bool hasValidEndValue = false;
for (int j = 0; j < variantAnims.size(); ++j) {
if (variantAnims.at(j)->endValue().isValid()) {
@@ -734,18 +771,28 @@ void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &tr
}
if (hasValidEndValue) {
- QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
- QObject::connect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
anim->start();
- playingAnimations.append(anim);
}
}
#endif // !QT_NO_ANIMATION
// Immediately set the properties that are not animated.
- for (int i = 0; i < propertyAssignments.size(); ++i) {
- const QPropertyAssignment &assn = propertyAssignments.at(i);
- assn.object->setProperty(assn.propertyName, assn.value);
+ {
+ QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it;
+ for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) {
+ const QList<QPropertyAssignment> &assignments = it.value();
+ for (int i = 0; i < assignments.size(); ++i) {
+ const QPropertyAssignment &assn = assignments.at(i);
+ assn.object->setProperty(assn.propertyName, assn.value);
+ }
+ }
+ }
+
+ // Emit polished signal for entered states that have no animated properties.
+ for (int i = 0; i < enteredStates.size(); ++i) {
+ QState *s = qobject_cast<QState*>(enteredStates.at(i));
+ if (s && !animationsForState.contains(s))
+ QStatePrivate::get(s)->emitPolished();
}
}
@@ -772,9 +819,8 @@ bool QStateMachinePrivate::isCompound(const QAbstractState *s)
bool QStateMachinePrivate::isAtomic(const QAbstractState *s)
{
const QState *ss = qobject_cast<const QState*>(s);
- return (ss && (QStatePrivate::get(ss)->childMode != QState::ParallelStates)
- && QStatePrivate::get(ss)->childStates().isEmpty())
- || isFinal(s);
+ return (ss && QStatePrivate::get(ss)->childStates().isEmpty())
+ || isFinal(s);
}
@@ -966,37 +1012,35 @@ QStateMachinePrivate::initializeAnimation(QAbstractAnimation *abstractAnimation,
return qMakePair(handledAnimations, localResetEndValues);
}
-static bool isAncestorOf(QObject *anc, QObject *o)
-{
- for (o = o->parent() ; o != 0; o = o->parent()) {
- if (o == anc)
- return true;
- }
- return false;
-}
-
void QStateMachinePrivate::_q_animationFinished()
{
Q_Q(QStateMachine);
- QAbstractAnimation *animation = qobject_cast<QAbstractAnimation*>(q->sender());
- Q_ASSERT(animation != 0);
- QList<QPair<QAbstractAnimation*, QPropertyAssignment> >::iterator it;
- for (it = propertiesForAnimations.begin(); it != propertiesForAnimations.end(); ) {
- QAbstractAnimation *a = (*it).first;
- if (a == animation || isAncestorOf(animation, a)) {
- QPropertyAssignment assn = (*it).second;
- assn.object->setProperty(assn.propertyName, assn.value);
- if (!assn.explicitlySet)
- unregisterRestorable(assn.object, assn.propertyName);
- it = propertiesForAnimations.erase(it);
- } else {
- ++it;
- }
+ QAbstractAnimation *anim = qobject_cast<QAbstractAnimation*>(q->sender());
+ Q_ASSERT(anim != 0);
+ QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
+ if (resetAnimationEndValues.contains(anim)) {
+ qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize
+ resetAnimationEndValues.remove(anim);
}
- playingAnimations.removeOne(animation);
- if (playingAnimations.isEmpty())
- emit q->animationsFinished();
+ // Set the final property value.
+ QPropertyAssignment assn = propertyForAnimation.take(anim);
+ Q_ASSERT(assn.object != 0);
+ assn.object->setProperty(assn.propertyName, assn.value);
+ if (!assn.explicitlySet)
+ unregisterRestorable(assn.object, assn.propertyName);
+
+ QAbstractState *state = stateForAnimation.take(anim);
+ Q_ASSERT(state != 0);
+ QHash<QAbstractState*, QList<QAbstractAnimation*> >::iterator it;
+ it = animationsForState.find(state);
+ Q_ASSERT(it != animationsForState.end());
+ QList<QAbstractAnimation*> &animations = it.value();
+ animations.removeOne(anim);
+ if (animations.isEmpty()) {
+ animationsForState.erase(it);
+ QStatePrivate::get(qobject_cast<QState*>(state))->emitPolished();
+ }
}
#endif // !QT_NO_ANIMATION
@@ -1057,7 +1101,8 @@ void QStateMachinePrivate::_q_start()
transitions.append(initialTransition);
executeTransitionContent(transitions);
enterStates(transitions);
- applyProperties(transitions, QList<QAbstractState*>() << initial);
+ applyProperties(transitions, QList<QAbstractState*>() << start,
+ QList<QAbstractState*>() << initial);
delete start;
#ifdef QSTATEMACHINE_DEBUG
@@ -1775,18 +1820,6 @@ QSet<QAbstractState*> QStateMachine::configuration() const
\sa QStateMachine::stop()
*/
-#ifndef QT_NO_ANIMATION
-
-/*!
- \fn QStateMachine::animationsFinished()
-
- This signal is emitted when the state machine has finished playing all
- animations associated with the latest transition (i.e., all properties have
- reached their target values).
-*/
-
-#endif
-
/*!
\reimp
*/
diff --git a/src/corelib/statemachine/qstatemachine.h b/src/corelib/statemachine/qstatemachine.h
index 6e504d0..10bd443 100644
--- a/src/corelib/statemachine/qstatemachine.h
+++ b/src/corelib/statemachine/qstatemachine.h
@@ -136,10 +136,6 @@ Q_SIGNALS:
void stopped();
void finished();
-#ifndef QT_NO_ANIMATION
- void animationsFinished();
-#endif
-
protected:
void postInternalEvent(QEvent *event);
diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h
index 139fee8..b3707ea 100644
--- a/src/corelib/statemachine/qstatemachine_p.h
+++ b/src/corelib/statemachine/qstatemachine_p.h
@@ -131,6 +131,7 @@ public:
QSet<QAbstractState*> &statesForDefaultEntry);
void applyProperties(const QList<QAbstractTransition*> &transitionList,
+ const QList<QAbstractState*> &exitedStates,
const QList<QAbstractState*> &enteredStates);
bool isInFinalState(QAbstractState *s) const;
@@ -186,9 +187,10 @@ public:
initializeAnimation(QAbstractAnimation *abstractAnimation,
const QPropertyAssignment &prop);
- QList<QPair<QAbstractAnimation*, QPropertyAssignment> > propertiesForAnimations;
- QList<QAbstractAnimation*> playingAnimations;
- QList<QAbstractAnimation*> resetEndValues;
+ QHash<QAbstractState*, QList<QAbstractAnimation*> > animationsForState;
+ QHash<QAbstractAnimation*, QPropertyAssignment> propertyForAnimation;
+ QHash<QAbstractAnimation*, QAbstractState*> stateForAnimation;
+ QSet<QAbstractAnimation*> resetAnimationEndValues;
QList<QAbstractAnimation *> defaultAnimations;
QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForSource;
diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp
index 5ce0f35..7f0d39b 100644
--- a/tests/auto/qstatemachine/tst_qstatemachine.cpp
+++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp
@@ -95,6 +95,8 @@ private slots:
void rootState();
void addAndRemoveState();
void stateEntryAndExit();
+ void assignProperty();
+ void assignPropertyWithAnimation();
void postEvent();
void stateFinished();
void parallelStates();
@@ -135,6 +137,7 @@ private slots:
//void restorePolicyOnChildState();
void transitionWithParent();
+ void transitionsFromParallelStateWithNoChildren();
void parallelStateTransition();
void parallelStateAssignmentsDone();
@@ -1185,6 +1188,155 @@ void tst_QStateMachine::stateEntryAndExit()
}
}
+void tst_QStateMachine::assignProperty()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(machine.rootState());
+ s1->assignProperty(s1, "objectName", "s1");
+ QFinalState *s2 = new QFinalState(machine.rootState());
+ s1->addTransition(s2);
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ QCOMPARE(s1->objectName(), QString::fromLatin1("s1"));
+
+ s1->assignProperty(s1, "objectName", "foo");
+ machine.start();
+ QCoreApplication::processEvents();
+ QCOMPARE(s1->objectName(), QString::fromLatin1("foo"));
+
+ s1->assignProperty(s1, "noSuchProperty", 123);
+ machine.start();
+ QCoreApplication::processEvents();
+ QCOMPARE(s1->objectName(), QString::fromLatin1("foo"));
+ QCOMPARE(s1->dynamicPropertyNames().size(), 1);
+ QCOMPARE(s1->dynamicPropertyNames().at(0), QByteArray("noSuchProperty"));
+
+ QSignalSpy polishedSpy(s1, SIGNAL(polished()));
+ machine.start();
+ QCoreApplication::processEvents();
+ QCOMPARE(polishedSpy.count(), 1);
+}
+
+void tst_QStateMachine::assignPropertyWithAnimation()
+{
+ // Single animation
+ {
+ QStateMachine machine;
+ QObject obj;
+ QState *s1 = new QState(machine.rootState());
+ s1->assignProperty(&obj, "foo", 123);
+ QState *s2 = new QState(machine.rootState());
+ s2->assignProperty(&obj, "foo", 456);
+ s2->assignProperty(&obj, "bar", 789);
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QPropertyAnimation anim(&obj, "foo");
+ anim.setDuration(250);
+ trans->addAnimation(&anim);
+ QFinalState *s3 = new QFinalState(machine.rootState());
+ s2->addTransition(s2, SIGNAL(polished()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
+ machine.start();
+ QTRY_COMPARE(finishedSpy.count(), 1);
+ QCOMPARE(obj.property("foo").toInt(), 456);
+ QCOMPARE(obj.property("bar").toInt(), 789);
+ }
+ // Two animations
+ {
+ QStateMachine machine;
+ QObject obj;
+ QState *s1 = new QState(machine.rootState());
+ s1->assignProperty(&obj, "foo", 123);
+ QState *s2 = new QState(machine.rootState());
+ s2->assignProperty(&obj, "foo", 456);
+ s2->assignProperty(&obj, "bar", 789);
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QPropertyAnimation anim(&obj, "foo");
+ anim.setDuration(150);
+ trans->addAnimation(&anim);
+ QPropertyAnimation anim2(&obj, "bar");
+ anim2.setDuration(150);
+ trans->addAnimation(&anim2);
+ QFinalState *s3 = new QFinalState(machine.rootState());
+ s2->addTransition(s2, SIGNAL(polished()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
+ machine.start();
+ QTRY_COMPARE(finishedSpy.count(), 1);
+ QCOMPARE(obj.property("foo").toInt(), 456);
+ QCOMPARE(obj.property("bar").toInt(), 789);
+ }
+ // Animation group
+ {
+ QStateMachine machine;
+ QObject obj;
+ QState *s1 = new QState(machine.rootState());
+ s1->assignProperty(&obj, "foo", 123);
+ s1->assignProperty(&obj, "bar", 321);
+ QState *s2 = new QState(machine.rootState());
+ s2->assignProperty(&obj, "foo", 456);
+ s2->assignProperty(&obj, "bar", 654);
+ s2->assignProperty(&obj, "baz", 789);
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QSequentialAnimationGroup group;
+ group.addAnimation(new QPropertyAnimation(&obj, "foo"));
+ group.addAnimation(new QPropertyAnimation(&obj, "bar"));
+ trans->addAnimation(&group);
+ QFinalState *s3 = new QFinalState(machine.rootState());
+ s2->addTransition(s2, SIGNAL(polished()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
+ machine.start();
+ QTRY_COMPARE(finishedSpy.count(), 1);
+ QCOMPARE(obj.property("foo").toInt(), 456);
+ QCOMPARE(obj.property("bar").toInt(), 654);
+ QCOMPARE(obj.property("baz").toInt(), 789);
+ }
+ // Nested states
+ {
+ QStateMachine machine;
+ QObject obj;
+ QState *s1 = new QState(machine.rootState());
+ s1->setObjectName("s1");
+ s1->assignProperty(&obj, "foo", 123);
+ s1->assignProperty(&obj, "bar", 456);
+ QState *s2 = new QState(machine.rootState());
+ s2->setObjectName("s2");
+ s2->assignProperty(&obj, "foo", 321);
+ QState *s21 = new QState(s2);
+ s21->setObjectName("s21");
+ s21->assignProperty(&obj, "bar", 654);
+ QState *s22 = new QState(s2);
+ s22->setObjectName("s22");
+ s22->assignProperty(&obj, "bar", 789);
+ s2->setInitialState(s21);
+
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QPropertyAnimation anim(&obj, "foo");
+ anim.setDuration(500);
+ trans->addAnimation(&anim);
+ QPropertyAnimation anim2(&obj, "bar");
+ anim2.setDuration(250);
+ trans->addAnimation(&anim2);
+
+ s21->addTransition(s21, SIGNAL(polished()), s22);
+
+ QFinalState *s3 = new QFinalState(machine.rootState());
+ s22->addTransition(s2, SIGNAL(polished()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
+ machine.start();
+ QTRY_COMPARE(finishedSpy.count(), 1);
+ QCOMPARE(obj.property("foo").toInt(), 321);
+ QCOMPARE(obj.property("bar").toInt(), 789);
+ }
+}
+
struct StringEvent : public QEvent
{
public:
@@ -2112,7 +2264,7 @@ void tst_QStateMachine::twoAnimations()
QState *s3 = new QState(machine.rootState());
QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
- s2->addTransition(&machine, SIGNAL(animationsFinished()), s3);
+ s2->addTransition(s2, SIGNAL(polished()), s3);
machine.setInitialState(s1);
machine.start();
@@ -2254,7 +2406,7 @@ void tst_QStateMachine::nestedTargetStateForAnimation()
QState *s3 = new QState(machine.rootState());
QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
- s2->addTransition(&machine, SIGNAL(animationsFinished()), s3);
+ s2->addTransition(s2, SIGNAL(polished()), s3);
machine.setInitialState(s1);
machine.start();
@@ -2889,6 +3041,30 @@ void tst_QStateMachine::parallelStateAssignmentsDone()
QCOMPARE(propertyHolder->property("zoot").toInt(), 987);
}
+void tst_QStateMachine::transitionsFromParallelStateWithNoChildren()
+{
+ QStateMachine machine;
+
+ QState *parallelState = new QState(QState::ParallelGroup, machine.rootState());
+ machine.setInitialState(parallelState);
+
+ QState *s1 = new QState(machine.rootState());
+ parallelState->addTransition(new EventTransition(QEvent::User, s1));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QCOMPARE(1, machine.configuration().size());
+ QVERIFY(machine.configuration().contains(parallelState));
+
+ machine.postEvent(new QEvent(QEvent::User));
+
+ QCoreApplication::processEvents();
+
+ QCOMPARE(1, machine.configuration().size());
+ QVERIFY(machine.configuration().contains(s1));
+}
+
void tst_QStateMachine::parallelStateTransition()
{
QStateMachine machine;