summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-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
6 files changed, 171 insertions, 131 deletions
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;