From c07f5b801bd6a94fe862073eb1f1965115a56385 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 13 Apr 2015 13:54:20 +0200 Subject: QStateMachine: add internal transitions. The behavior of "external" and "internal" transitions is identical, except in the case of a transition whose source state is a compound state and whose target(s) is a descendant of the source. In such a case, an internal transition will not exit and re-enter its source state, while an external one will. [ChangeLog][State machine] Added support for internal transitions. Change-Id: I9efb1e7368ee52aa2544eb84709a00ae3d5350d3 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/corelib/statemachine/qabstracttransition.cpp | 46 ++++++++++++++++++++++ src/corelib/statemachine/qabstracttransition.h | 10 +++++ src/corelib/statemachine/qabstracttransition_p.h | 1 + src/corelib/statemachine/qstatemachine.cpp | 25 ++++++------ .../qstatemachine/tst_qstatemachine.cpp | 44 +++++++++++++++++++++ 5 files changed, 113 insertions(+), 13 deletions(-) diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index f128acd54e..81b38ea4c4 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -101,7 +101,35 @@ QT_BEGIN_NAMESPACE parallel group state. */ +/*! + \property QAbstractTransition::transitionType + + \brief indicates whether this transition is an internal transition, or an external transition. + + Internal and external transitions behave the same, except for the case of a transition whose + source state is a compound state and whose target(s) is a descendant of the source. In such a + case, an internal transition will not exit and re-enter its source state, while an external one + will. + + By default, the type is an external transition. +*/ + +/*! + \enum QAbstractTransition::TransitionType + + This enum specifies the kind of transition. By default, the type is an external transition. + + \value ExternalTransition Any state that is the source state of a transition (which is not a + target-less transition) is left, and re-entered when necessary. + \value InternalTransition If the target state of a transition is a sub-state of a compound state, + and that compound state is the source state, an internal transition will + not leave the source state. + + \sa QAbstractTransition::transitionType +*/ + QAbstractTransitionPrivate::QAbstractTransitionPrivate() + : transitionType(QAbstractTransition::ExternalTransition) { } @@ -248,6 +276,24 @@ void QAbstractTransition::setTargetStates(const QList &targets) emit targetStatesChanged(QPrivateSignal()); } +/*! + Returns the type of the transition. +*/ +QAbstractTransition::TransitionType QAbstractTransition::transitionType() const +{ + Q_D(const QAbstractTransition); + return d->transitionType; +} + +/*! + Sets the type of the transition to \a type. +*/ +void QAbstractTransition::setTransitionType(TransitionType type) +{ + Q_D(QAbstractTransition); + d->transitionType = type; +} + /*! Returns the state machine that this transition is part of, or 0 if the transition is not part of a state machine. diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h index 768a364a4b..bf32b3e825 100644 --- a/src/corelib/statemachine/qabstracttransition.h +++ b/src/corelib/statemachine/qabstracttransition.h @@ -59,7 +59,14 @@ class Q_CORE_EXPORT QAbstractTransition : public QObject Q_PROPERTY(QState* sourceState READ sourceState) Q_PROPERTY(QAbstractState* targetState READ targetState WRITE setTargetState NOTIFY targetStateChanged) Q_PROPERTY(QList targetStates READ targetStates WRITE setTargetStates NOTIFY targetStatesChanged) + Q_PROPERTY(TransitionType transitionType READ transitionType WRITE setTransitionType) public: + enum TransitionType { + ExternalTransition, + InternalTransition + }; + Q_ENUM(TransitionType) + QAbstractTransition(QState *sourceState = 0); virtual ~QAbstractTransition(); @@ -69,6 +76,9 @@ public: QList targetStates() const; void setTargetStates(const QList &targets); + TransitionType transitionType() const; + void setTransitionType(TransitionType type); + QStateMachine *machine() const; #ifndef QT_NO_ANIMATION diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h index d89d057497..4b0644acd9 100644 --- a/src/corelib/statemachine/qabstracttransition_p.h +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -73,6 +73,7 @@ public: void emitTriggered(); QList > targetStates; + QAbstractTransition::TransitionType transitionType; #ifndef QT_NO_ANIMATION QList animations; diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index d91b4ba14a..6e36f93c40 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -889,23 +889,22 @@ QAbstractState *QStateMachinePrivate::getTransitionDomain(QAbstractTransition *t if (cache->transitionDomain(t, &domain)) return domain; -#if 0 - // Qt only has external transitions, so skip the special case for the internal transitions - if (QState *tSource = t->sourceState()) { - if (isCompound(tSource)) { - bool allDescendants = true; - foreach (QAbstractState *s, effectiveTargetStates) { - if (!isDescendant(s, tSource)) { - allDescendants = false; - break; + if (t->transitionType() == QAbstractTransition::InternalTransition) { + if (QState *tSource = t->sourceState()) { + if (isCompound(tSource)) { + bool allDescendants = true; + foreach (QAbstractState *s, effectiveTargetStates) { + if (!isDescendant(s, tSource)) { + allDescendants = false; + break; + } } - } - if (allDescendants) - return tSource; + if (allDescendants) + return tSource; + } } } -#endif QList states(effectiveTargetStates); if (QAbstractState *src = t->sourceState()) diff --git a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp index 9fb2e40cb8..6ddfb828e8 100644 --- a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp @@ -247,6 +247,7 @@ private slots: void qtbug_44963(); void qtbug_44783(); + void internalTransition(); }; class TestState : public QState @@ -6338,5 +6339,48 @@ void tst_QStateMachine::qtbug_44783() QVERIFY(machine.isRunning()); } +void tst_QStateMachine::internalTransition() +{ + SignalEmitter emitter; + + QStateMachine machine; + QState *s = new QState(&machine); + QState *s1 = new QState(s); + QState *s11 = new QState(s1); + + DEFINE_ACTIVE_SPY(s); + DEFINE_ACTIVE_SPY(s1); + DEFINE_ACTIVE_SPY(s11); + + machine.setInitialState(s); + s->setInitialState(s1); + s1->setInitialState(s11); + QSignalTransition *t = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s11); + t->setObjectName("s1->s11"); + t->setTransitionType(QAbstractTransition::InternalTransition); + + s->setObjectName("s"); + s1->setObjectName("s1"); + s11->setObjectName("s11"); + + machine.start(); + + QTRY_COMPARE(machine.configuration().contains(s), true); + QTRY_COMPARE(machine.configuration().contains(s1), true); + QTRY_COMPARE(machine.configuration().contains(s11), true); + TEST_ACTIVE_CHANGED(s, 1); + TEST_ACTIVE_CHANGED(s1, 1); + TEST_ACTIVE_CHANGED(s11, 1); + + emitter.emitSignalWithNoArg(); + + QTRY_COMPARE(machine.configuration().contains(s), true); + QTRY_COMPARE(machine.configuration().contains(s1), true); + QTRY_COMPARE(machine.configuration().contains(s11), true); + TEST_ACTIVE_CHANGED(s11, 3); + TEST_ACTIVE_CHANGED(s1, 1); // external transitions will return 3, internal transitions should return 1. + TEST_ACTIVE_CHANGED(s, 1); +} + QTEST_MAIN(tst_QStateMachine) #include "tst_qstatemachine.moc" -- cgit v1.2.3