From 39604894946c3c0d623c0073ccf027a8e6df120a Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 29 Sep 2009 11:59:01 +0200 Subject: Do synchronous processing of events in state machine if possible Avoid delayed scheduling in the cases where there's no need to delay it (e.g. when the state machine intercepts a signal or event). Task-number: QTBUG-4491 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/corelib/statemachine/qstatemachine.cpp | 46 +++++++++++++++++++------- src/corelib/statemachine/qstatemachine_p.h | 7 +++- tests/auto/qstatemachine/tst_qstatemachine.cpp | 33 ++++++++++++++++++ 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 503eec0..8d50870c 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -804,6 +804,14 @@ void QStateMachinePrivate::applyProperties(const QList &tr } if (hasValidEndValue) { + if (anim->state() == QAbstractAnimation::Running) { + // The animation is still running. This can happen if the + // animation is a group, and one of its children just finished, + // and that caused a state to emit its polished() signal, and + // that triggered a transition in the machine. + // Just stop the animation so it is correctly restarted again. + anim->stop(); + } anim->start(); } } @@ -1268,12 +1276,19 @@ void QStateMachinePrivate::_q_process() } } -void QStateMachinePrivate::scheduleProcess() +void QStateMachinePrivate::processEvents(EventProcessingMode processingMode) { if ((state != Running) || processing || processingScheduled) return; - processingScheduled = true; - QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection); + switch (processingMode) { + case DirectProcessing: + _q_process(); + break; + case QueuedProcessing: + processingScheduled = true; + QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection); + break; + } } namespace { @@ -1335,7 +1350,7 @@ void QStateMachinePrivate::goToState(QAbstractState *targetState) trans->setTargetState(targetState); } - scheduleProcess(); + processEvents(QueuedProcessing); } void QStateMachinePrivate::registerTransitions(QAbstractState *state) @@ -1512,6 +1527,15 @@ void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transitio } QEventTransitionPrivate::get(transition)->registered = false; } + +void QStateMachinePrivate::handleFilteredEvent(QObject *watched, QEvent *event) +{ + Q_ASSERT(qobjectEvents.contains(watched)); + if (qobjectEvents[watched].contains(event->type())) { + internalEventQueue.append(new QStateMachine::WrappedEvent(watched, handler->cloneEvent(event))); + processEvents(DirectProcessing); + } +} #endif void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex, @@ -1533,7 +1557,7 @@ void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalInd << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')'; #endif internalEventQueue.append(new QStateMachine::SignalEvent(sender, signalIndex, vargs)); - scheduleProcess(); + processEvents(DirectProcessing); } /*! @@ -1768,7 +1792,7 @@ void QStateMachine::stop() break; case QStateMachinePrivate::Running: d->stop = true; - d->scheduleProcess(); + d->processEvents(QStateMachinePrivate::QueuedProcessing); break; } } @@ -1798,7 +1822,7 @@ void QStateMachine::postEvent(QEvent *event, int delay) d->delayedEvents[tid] = event; } else { d->externalEventQueue.append(event); - d->scheduleProcess(); + d->processEvents(QStateMachinePrivate::QueuedProcessing); } } @@ -1814,7 +1838,7 @@ void QStateMachine::postInternalEvent(QEvent *event) qDebug() << this << ": posting internal event" << event; #endif d->internalEventQueue.append(event); - d->scheduleProcess(); + d->processEvents(QStateMachinePrivate::QueuedProcessing); } /*! @@ -1862,7 +1886,7 @@ bool QStateMachine::event(QEvent *e) killTimer(tid); QEvent *ee = d->delayedEvents.take(tid); d->externalEventQueue.append(ee); - d->scheduleProcess(); + d->processEvents(QStateMachinePrivate::DirectProcessing); return true; } } @@ -1876,9 +1900,7 @@ bool QStateMachine::event(QEvent *e) bool QStateMachine::eventFilter(QObject *watched, QEvent *event) { Q_D(QStateMachine); - Q_ASSERT(d->qobjectEvents.contains(watched)); - if (d->qobjectEvents[watched].contains(event->type())) - postEvent(new QStateMachine::WrappedEvent(watched, d->handler->cloneEvent(event))); + d->handleFilteredEvent(watched, event); return false; } #endif diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index a1b6de2..141bc5c 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -88,6 +88,10 @@ public: Starting, Running }; + enum EventProcessingMode { + DirectProcessing, + QueuedProcessing + }; enum StopProcessingReason { EventQueueEmpty, Finished, @@ -149,12 +153,13 @@ public: #ifndef QT_NO_STATEMACHINE_EVENTFILTER void registerEventTransition(QEventTransition *transition); void unregisterEventTransition(QEventTransition *transition); + void handleFilteredEvent(QObject *watched, QEvent *event); #endif void unregisterTransition(QAbstractTransition *transition); void unregisterAllTransitions(); void handleTransitionSignal(QObject *sender, int signalIndex, void **args); - void scheduleProcess(); + void processEvents(EventProcessingMode processingMode); #ifndef QT_NO_PROPERTIES typedef QPair RestorableId; diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 7244d72..37b34bf 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -176,6 +176,7 @@ private slots: void twoAnimatedTransitions(); void playAnimationTwice(); void nestedTargetStateForAnimation(); + void polishedSignalTransitionsReuseAnimationGroup(); void animatedGlobalRestoreProperty(); void specificTargetValueOfAnimation(); @@ -3115,6 +3116,38 @@ void tst_QStateMachine::nestedTargetStateForAnimation() QCOMPARE(counter.counter, 2); } +void tst_QStateMachine::polishedSignalTransitionsReuseAnimationGroup() +{ + QStateMachine machine; + QObject *object = new QObject(&machine); + object->setProperty("foo", 0); + + QState *s1 = new QState(&machine); + s1->assignProperty(object, "foo", 123); + QState *s2 = new QState(&machine); + s2->assignProperty(object, "foo", 456); + QState *s3 = new QState(&machine); + s3->assignProperty(object, "foo", 789); + QFinalState *s4 = new QFinalState(&machine); + + QParallelAnimationGroup animationGroup; + animationGroup.addAnimation(new QPropertyAnimation(object, "foo")); + QSignalSpy animationFinishedSpy(&animationGroup, SIGNAL(finished())); + s1->addTransition(s1, SIGNAL(polished()), s2)->addAnimation(&animationGroup); + s2->addTransition(s2, SIGNAL(polished()), s3)->addAnimation(&animationGroup); + s3->addTransition(s3, SIGNAL(polished()), s4); + + machine.setInitialState(s1); + QSignalSpy machineFinishedSpy(&machine, SIGNAL(finished())); + machine.start(); + QTRY_COMPARE(machineFinishedSpy.count(), 1); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s4)); + QCOMPARE(object->property("foo").toInt(), 789); + + QCOMPARE(animationFinishedSpy.count(), 2); +} + void tst_QStateMachine::animatedGlobalRestoreProperty() { QStateMachine machine; -- cgit v0.12