1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "QtQuick1/private/qdeclarativestate_p_p.h"
43 #include "QtQuick1/private/qdeclarativestate_p.h"
45 #include "QtQuick1/private/qdeclarativetransition_p.h"
46 #include "QtQuick1/private/qdeclarativestategroup_p.h"
47 #include "QtQuick1/private/qdeclarativestateoperations_p.h"
48 #include "QtQuick1/private/qdeclarativeanimation_p.h"
49 #include "QtQuick1/private/qdeclarativeanimation_p_p.h"
51 #include <QtDeclarative/private/qdeclarativebinding_p.h>
52 #include <QtDeclarative/private/qdeclarativeglobal_p.h>
54 #include <QtCore/qdebug.h>
60 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
62 QDeclarative1Action::QDeclarative1Action()
63 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0),
68 QDeclarative1Action::QDeclarative1Action(QObject *target, const QString &propertyName,
69 const QVariant &value)
70 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
71 property(target, propertyName, qmlEngine(target)), toValue(value),
72 fromBinding(0), event(0),
73 specifiedObject(target), specifiedProperty(propertyName)
75 if (property.isValid())
76 fromValue = property.read();
79 QDeclarative1Action::QDeclarative1Action(QObject *target, const QString &propertyName,
80 QDeclarativeContext *context, const QVariant &value)
81 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
82 property(target, propertyName, context), toValue(value),
83 fromBinding(0), event(0),
84 specifiedObject(target), specifiedProperty(propertyName)
86 if (property.isValid())
87 fromValue = property.read();
91 QDeclarative1ActionEvent::~QDeclarative1ActionEvent()
95 QString QDeclarative1ActionEvent::typeName() const
100 void QDeclarative1ActionEvent::execute(Reason)
104 bool QDeclarative1ActionEvent::isReversable()
109 void QDeclarative1ActionEvent::reverse(Reason)
113 bool QDeclarative1ActionEvent::changesBindings()
118 void QDeclarative1ActionEvent::clearBindings()
122 bool QDeclarative1ActionEvent::override(QDeclarative1ActionEvent *other)
128 QDeclarative1StateOperation::QDeclarative1StateOperation(QObjectPrivate &dd, QObject *parent)
129 : QObject(dd, parent)
134 \qmlclass State QDeclarative1State
135 \ingroup qml-state-elements
137 \brief The State element defines configurations of objects and properties.
139 A \e state is a set of batched changes from the default configuration.
141 All items have a default state that defines the default configuration of objects
142 and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to
143 allow items to switch between different configurations. These configurations
144 can, for example, be used to apply different sets of property values or execute
147 The following example displays a single \l Rectangle. In the default state, the rectangle
148 is colored black. In the "clicked" state, a PropertyChanges element changes the
149 rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state
150 between the default state and the "clicked" state, thus toggling the color of the
151 rectangle between black and red.
153 \snippet doc/src/snippets/declarative/state.qml 0
155 Notice the default state is referred to using an empty string ("").
157 States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide
158 animations when state changes occur.
160 \note Setting the state of an object from within another state of the same object is
163 \sa {declarative/animation/states}{states example}, {qmlstates}{States},
164 {QML Animation and Transitions}{Transitions}, QtDeclarative
166 QDeclarative1State::QDeclarative1State(QObject *parent)
167 : QObject(*(new QDeclarative1StatePrivate), parent)
169 Q_D(QDeclarative1State);
170 d->transitionManager.setState(this);
173 QDeclarative1State::~QDeclarative1State()
175 Q_D(QDeclarative1State);
177 d->group->removeState(this);
181 \qmlproperty string State::name
182 This property holds the name of the state.
184 Each state should have a unique name within its item.
186 QString QDeclarative1State::name() const
188 Q_D(const QDeclarative1State);
192 void QDeclarative1State::setName(const QString &n)
194 Q_D(QDeclarative1State);
199 bool QDeclarative1State::isNamed() const
201 Q_D(const QDeclarative1State);
205 bool QDeclarative1State::isWhenKnown() const
207 Q_D(const QDeclarative1State);
212 \qmlproperty bool State::when
213 This property holds when the state should be applied.
215 This should be set to an expression that evaluates to \c true when you want the state to
216 be applied. For example, the following \l Rectangle changes in and out of the "hidden"
217 state when the \l MouseArea is pressed:
219 \snippet doc/src/snippets/declarative/state-when.qml 0
221 If multiple states in a group have \c when clauses that evaluate to \c true
222 at the same time, the first matching state will be applied. For example, in
223 the following snippet \c state1 will always be selected rather than
224 \c state2 when sharedCondition becomes \c true.
228 State { name: "state1"; when: sharedCondition },
229 State { name: "state2"; when: sharedCondition }
235 QDeclarativeBinding *QDeclarative1State::when() const
237 Q_D(const QDeclarative1State);
241 void QDeclarative1State::setWhen(QDeclarativeBinding *when)
243 Q_D(QDeclarative1State);
246 d->group->updateAutoState();
250 \qmlproperty string State::extend
251 This property holds the state that this state extends.
253 When a state extends another state, it inherits all the changes of that state.
255 The state being extended is treated as the base state in regards to
256 the changes specified by the extending state.
258 QString QDeclarative1State::extends() const
260 Q_D(const QDeclarative1State);
264 void QDeclarative1State::setExtends(const QString &extends)
266 Q_D(QDeclarative1State);
267 d->extends = extends;
271 \qmlproperty list<Change> State::changes
272 This property holds the changes to apply for this state
275 By default these changes are applied against the default state. If the state
276 extends another state, then the changes are applied against the state being
279 QDeclarativeListProperty<QDeclarative1StateOperation> QDeclarative1State::changes()
281 Q_D(QDeclarative1State);
282 return QDeclarativeListProperty<QDeclarative1StateOperation>(this, &d->operations, QDeclarative1StatePrivate::operations_append,
283 QDeclarative1StatePrivate::operations_count, QDeclarative1StatePrivate::operations_at,
284 QDeclarative1StatePrivate::operations_clear);
287 int QDeclarative1State::operationCount() const
289 Q_D(const QDeclarative1State);
290 return d->operations.count();
293 QDeclarative1StateOperation *QDeclarative1State::operationAt(int index) const
295 Q_D(const QDeclarative1State);
296 return d->operations.at(index);
299 QDeclarative1State &QDeclarative1State::operator<<(QDeclarative1StateOperation *op)
301 Q_D(QDeclarative1State);
302 d->operations.append(QDeclarative1StatePrivate::OperationGuard(op, &d->operations));
306 void QDeclarative1StatePrivate::complete()
308 Q_Q(QDeclarative1State);
310 for (int ii = 0; ii < reverting.count(); ++ii) {
311 for (int jj = 0; jj < revertList.count(); ++jj) {
312 if (revertList.at(jj).property() == reverting.at(ii)) {
313 revertList.removeAt(jj);
323 // Generate a list of actions for this state. This includes coelescing state
324 // actions that this state "extends"
325 QDeclarative1StateOperation::ActionList
326 QDeclarative1StatePrivate::generateActionList(QDeclarative1StateGroup *group) const
328 QDeclarative1StateOperation::ActionList applyList;
332 // Prevent "extends" recursion
335 if (!extends.isEmpty()) {
336 QList<QDeclarative1State *> states = group->states();
337 for (int ii = 0; ii < states.count(); ++ii)
338 if (states.at(ii)->name() == extends) {
339 qmlExecuteDeferred(states.at(ii));
340 applyList = static_cast<QDeclarative1StatePrivate*>(states.at(ii)->d_func())->generateActionList(group);
344 foreach(QDeclarative1StateOperation *op, operations)
345 applyList << op->actions();
351 QDeclarative1StateGroup *QDeclarative1State::stateGroup() const
353 Q_D(const QDeclarative1State);
357 void QDeclarative1State::setStateGroup(QDeclarative1StateGroup *group)
359 Q_D(QDeclarative1State);
363 void QDeclarative1State::cancel()
365 Q_D(QDeclarative1State);
366 d->transitionManager.cancel();
369 void QDeclarative1Action::deleteFromBinding()
372 QDeclarativePropertyPrivate::setBinding(property, 0);
373 fromBinding->destroy();
378 bool QDeclarative1State::containsPropertyInRevertList(QObject *target, const QString &name) const
380 Q_D(const QDeclarative1State);
382 if (isStateActive()) {
383 QListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList);
385 while (revertListIterator.hasNext()) {
386 const QDeclarative1SimpleAction &simpleAction = revertListIterator.next();
387 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
395 bool QDeclarative1State::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue)
397 Q_D(QDeclarative1State);
399 if (isStateActive()) {
400 QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList);
402 while (revertListIterator.hasNext()) {
403 QDeclarative1SimpleAction &simpleAction = revertListIterator.next();
404 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
405 simpleAction.setValue(revertValue);
414 bool QDeclarative1State::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding)
416 Q_D(QDeclarative1State);
418 if (isStateActive()) {
419 QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList);
421 while (revertListIterator.hasNext()) {
422 QDeclarative1SimpleAction &simpleAction = revertListIterator.next();
423 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
424 if (simpleAction.binding())
425 simpleAction.binding()->destroy();
427 simpleAction.setBinding(binding);
436 bool QDeclarative1State::removeEntryFromRevertList(QObject *target, const QString &name)
438 Q_D(QDeclarative1State);
440 if (isStateActive()) {
441 QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList);
443 while (revertListIterator.hasNext()) {
444 QDeclarative1SimpleAction &simpleAction = revertListIterator.next();
445 if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
446 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
448 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
449 oldBinding->destroy();
452 simpleAction.property().write(simpleAction.value());
453 if (simpleAction.binding())
454 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
456 revertListIterator.remove();
465 void QDeclarative1State::addEntryToRevertList(const QDeclarative1Action &action)
467 Q_D(QDeclarative1State);
469 QDeclarative1SimpleAction simpleAction(action);
471 d->revertList.append(simpleAction);
474 void QDeclarative1State::removeAllEntriesFromRevertList(QObject *target)
476 Q_D(QDeclarative1State);
478 if (isStateActive()) {
479 QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList);
481 while (revertListIterator.hasNext()) {
482 QDeclarative1SimpleAction &simpleAction = revertListIterator.next();
483 if (simpleAction.property().object() == target) {
484 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
486 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
487 oldBinding->destroy();
490 simpleAction.property().write(simpleAction.value());
491 if (simpleAction.binding())
492 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
494 revertListIterator.remove();
500 void QDeclarative1State::addEntriesToRevertList(const QList<QDeclarative1Action> &actionList)
502 Q_D(QDeclarative1State);
503 if (isStateActive()) {
504 QList<QDeclarative1SimpleAction> simpleActionList;
506 QListIterator<QDeclarative1Action> actionListIterator(actionList);
507 while(actionListIterator.hasNext()) {
508 const QDeclarative1Action &action = actionListIterator.next();
509 QDeclarative1SimpleAction simpleAction(action);
510 action.property.write(action.toValue);
511 if (!action.toBinding.isNull()) {
512 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
514 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
515 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding);
518 simpleActionList.append(simpleAction);
521 d->revertList.append(simpleActionList);
525 QVariant QDeclarative1State::valueInRevertList(QObject *target, const QString &name) const
527 Q_D(const QDeclarative1State);
529 if (isStateActive()) {
530 QListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList);
532 while (revertListIterator.hasNext()) {
533 const QDeclarative1SimpleAction &simpleAction = revertListIterator.next();
534 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
535 return simpleAction.value();
542 QDeclarativeAbstractBinding *QDeclarative1State::bindingInRevertList(QObject *target, const QString &name) const
544 Q_D(const QDeclarative1State);
546 if (isStateActive()) {
547 QListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList);
549 while (revertListIterator.hasNext()) {
550 const QDeclarative1SimpleAction &simpleAction = revertListIterator.next();
551 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
552 return simpleAction.binding();
559 bool QDeclarative1State::isStateActive() const
561 return stateGroup() && stateGroup()->state() == name();
564 void QDeclarative1State::apply(QDeclarative1StateGroup *group, QDeclarative1Transition *trans, QDeclarative1State *revert)
566 Q_D(QDeclarative1State);
568 qmlExecuteDeferred(this);
573 d->revertList.clear();
574 d->reverting.clear();
577 QDeclarative1StatePrivate *revertPrivate =
578 static_cast<QDeclarative1StatePrivate*>(revert->d_func());
579 d->revertList = revertPrivate->revertList;
580 revertPrivate->revertList.clear();
583 // List of actions caused by this state
584 QDeclarative1StateOperation::ActionList applyList = d->generateActionList(group);
586 // List of actions that need to be reverted to roll back (just) this state
587 QDeclarative1StatePrivate::SimpleActionList additionalReverts;
588 // First add the reverse of all the applyList actions
589 for (int ii = 0; ii < applyList.count(); ++ii) {
590 QDeclarative1Action &action = applyList[ii];
593 if (!action.event->isReversable())
596 for (int jj = 0; jj < d->revertList.count(); ++jj) {
597 QDeclarative1ActionEvent *event = d->revertList.at(jj).event();
598 if (event && event->typeName() == action.event->typeName()) {
599 if (action.event->override(event)) {
602 if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) {
603 action.event->copyOriginals(d->revertList.at(jj).event());
605 QDeclarative1SimpleAction r(action);
606 additionalReverts << r;
607 d->revertList.removeAt(jj);
609 } else if (action.event->isRewindable()) //###why needed?
610 action.event->saveCurrentValues();
617 action.event->saveOriginals();
618 // Only need to revert the applyList action if the previous
619 // state doesn't have a higher priority revert already
620 QDeclarative1SimpleAction r(action);
621 additionalReverts << r;
625 action.fromBinding = QDeclarativePropertyPrivate::binding(action.property);
627 for (int jj = 0; jj < d->revertList.count(); ++jj) {
628 if (d->revertList.at(jj).property() == action.property) {
630 if (d->revertList.at(jj).binding() != action.fromBinding) {
631 action.deleteFromBinding();
638 if (!action.restore) {
639 action.deleteFromBinding();;
641 // Only need to revert the applyList action if the previous
642 // state doesn't have a higher priority revert already
643 QDeclarative1SimpleAction r(action);
644 additionalReverts << r;
650 // Any reverts from a previous state that aren't carried forth
651 // into this state need to be translated into apply actions
652 for (int ii = 0; ii < d->revertList.count(); ++ii) {
654 if (d->revertList.at(ii).event()) {
655 QDeclarative1ActionEvent *event = d->revertList.at(ii).event();
656 if (!event->isReversable())
658 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
659 const QDeclarative1Action &action = applyList.at(jj);
660 if (action.event && action.event->typeName() == event->typeName()) {
661 if (action.event->override(event))
666 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
667 const QDeclarative1Action &action = applyList.at(jj);
668 if (action.property == d->revertList.at(ii).property())
673 QVariant cur = d->revertList.at(ii).property().read();
674 QDeclarativeAbstractBinding *delBinding =
675 QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
677 delBinding->destroy();
679 QDeclarative1Action a;
680 a.property = d->revertList.at(ii).property();
682 a.toValue = d->revertList.at(ii).value();
683 a.toBinding = QDeclarativeAbstractBinding::getPointer(d->revertList.at(ii).binding());
684 a.specifiedObject = d->revertList.at(ii).specifiedObject();
685 a.specifiedProperty = d->revertList.at(ii).specifiedProperty();
686 a.event = d->revertList.at(ii).event();
687 a.reverseEvent = d->revertList.at(ii).reverseEvent();
688 if (a.event && a.event->isRewindable())
689 a.event->saveCurrentValues();
691 // Store these special reverts in the reverting list
692 d->reverting << d->revertList.at(ii).property();
695 // All the local reverts now become part of the ongoing revertList
696 d->revertList << additionalReverts;
698 #ifndef QT_NO_DEBUG_STREAM
699 // Output for debugging
700 if (stateChangeDebug()) {
701 foreach(const QDeclarative1Action &action, applyList) {
703 qWarning() << " QDeclarative1Action event:" << action.event->typeName();
705 qWarning() << " QDeclarative1Action:" << action.property.object()
706 << action.property.name() << "From:" << action.fromValue
707 << "To:" << action.toValue;
712 d->transitionManager.transition(applyList, trans);
715 QDeclarative1StateOperation::ActionList QDeclarative1StateOperation::actions()
720 QDeclarative1State *QDeclarative1StateOperation::state() const
722 Q_D(const QDeclarative1StateOperation);
726 void QDeclarative1StateOperation::setState(QDeclarative1State *state)
728 Q_D(QDeclarative1StateOperation);