1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativestate_p_p.h"
43 #include "qdeclarativestate_p.h"
45 #include "qdeclarativestategroup_p.h"
46 #include "qdeclarativestateoperations_p.h"
48 #include <private/qdeclarativeglobal_p.h>
50 #include <QtCore/qdebug.h>
54 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
56 QDeclarativeAction::QDeclarativeAction()
57 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0),
62 QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName,
63 const QVariant &value)
64 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
65 property(target, propertyName, qmlEngine(target)), toValue(value),
66 fromBinding(0), event(0),
67 specifiedObject(target), specifiedProperty(propertyName)
69 if (property.isValid())
70 fromValue = property.read();
73 QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName,
74 QDeclarativeContext *context, const QVariant &value)
75 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
76 property(target, propertyName, context), toValue(value),
77 fromBinding(0), event(0),
78 specifiedObject(target), specifiedProperty(propertyName)
80 if (property.isValid())
81 fromValue = property.read();
85 QDeclarativeActionEvent::~QDeclarativeActionEvent()
89 QString QDeclarativeActionEvent::typeName() const
94 void QDeclarativeActionEvent::execute(Reason)
98 bool QDeclarativeActionEvent::isReversable()
103 void QDeclarativeActionEvent::reverse(Reason)
107 bool QDeclarativeActionEvent::changesBindings()
112 void QDeclarativeActionEvent::clearBindings()
116 bool QDeclarativeActionEvent::override(QDeclarativeActionEvent *other)
122 QDeclarativeStateOperation::QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent)
123 : QObject(dd, parent)
128 \qmlclass State QDeclarativeState
129 \inqmlmodule QtQuick 2
130 \ingroup qml-state-elements
131 \brief The State element defines configurations of objects and properties.
133 A \e state is a set of batched changes from the default configuration.
135 All items have a default state that defines the default configuration of objects
136 and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to
137 allow items to switch between different configurations. These configurations
138 can, for example, be used to apply different sets of property values or execute
141 The following example displays a single \l Rectangle. In the default state, the rectangle
142 is colored black. In the "clicked" state, a PropertyChanges element changes the
143 rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state
144 between the default state and the "clicked" state, thus toggling the color of the
145 rectangle between black and red.
147 \snippet doc/src/snippets/declarative/state.qml 0
149 Notice the default state is referred to using an empty string ("").
151 States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide
152 animations when state changes occur.
154 \note Setting the state of an object from within another state of the same object is
157 \sa {declarative/animation/states}{states example}, {qmlstates}{States},
158 {QML Animation and Transitions}{Transitions}, QtDeclarative
160 QDeclarativeState::QDeclarativeState(QObject *parent)
161 : QObject(*(new QDeclarativeStatePrivate), parent)
163 Q_D(QDeclarativeState);
164 d->transitionManager.setState(this);
167 QDeclarativeState::~QDeclarativeState()
169 Q_D(QDeclarativeState);
171 d->group->removeState(this);
175 \qmlproperty string QtQuick2::State::name
176 This property holds the name of the state.
178 Each state should have a unique name within its item.
180 QString QDeclarativeState::name() const
182 Q_D(const QDeclarativeState);
186 void QDeclarativeState::setName(const QString &n)
188 Q_D(QDeclarativeState);
193 bool QDeclarativeState::isNamed() const
195 Q_D(const QDeclarativeState);
199 bool QDeclarativeState::isWhenKnown() const
201 Q_D(const QDeclarativeState);
206 \qmlproperty bool QtQuick2::State::when
207 This property holds when the state should be applied.
209 This should be set to an expression that evaluates to \c true when you want the state to
210 be applied. For example, the following \l Rectangle changes in and out of the "hidden"
211 state when the \l MouseArea is pressed:
213 \snippet doc/src/snippets/declarative/state-when.qml 0
215 If multiple states in a group have \c when clauses that evaluate to \c true
216 at the same time, the first matching state will be applied. For example, in
217 the following snippet \c state1 will always be selected rather than
218 \c state2 when sharedCondition becomes \c true.
222 State { name: "state1"; when: sharedCondition },
223 State { name: "state2"; when: sharedCondition }
229 QDeclarativeBinding *QDeclarativeState::when() const
231 Q_D(const QDeclarativeState);
235 void QDeclarativeState::setWhen(QDeclarativeBinding *when)
237 Q_D(QDeclarativeState);
240 d->group->updateAutoState();
244 \qmlproperty string QtQuick2::State::extend
245 This property holds the state that this state extends.
247 When a state extends another state, it inherits all the changes of that state.
249 The state being extended is treated as the base state in regards to
250 the changes specified by the extending state.
252 QString QDeclarativeState::extends() const
254 Q_D(const QDeclarativeState);
258 void QDeclarativeState::setExtends(const QString &extends)
260 Q_D(QDeclarativeState);
261 d->extends = extends;
265 \qmlproperty list<Change> QtQuick2::State::changes
266 This property holds the changes to apply for this state
269 By default these changes are applied against the default state. If the state
270 extends another state, then the changes are applied against the state being
273 QDeclarativeListProperty<QDeclarativeStateOperation> QDeclarativeState::changes()
275 Q_D(QDeclarativeState);
276 return QDeclarativeListProperty<QDeclarativeStateOperation>(this, &d->operations, QDeclarativeStatePrivate::operations_append,
277 QDeclarativeStatePrivate::operations_count, QDeclarativeStatePrivate::operations_at,
278 QDeclarativeStatePrivate::operations_clear);
281 int QDeclarativeState::operationCount() const
283 Q_D(const QDeclarativeState);
284 return d->operations.count();
287 QDeclarativeStateOperation *QDeclarativeState::operationAt(int index) const
289 Q_D(const QDeclarativeState);
290 return d->operations.at(index);
293 QDeclarativeState &QDeclarativeState::operator<<(QDeclarativeStateOperation *op)
295 Q_D(QDeclarativeState);
296 d->operations.append(QDeclarativeStatePrivate::OperationGuard(op, &d->operations));
300 void QDeclarativeStatePrivate::complete()
302 Q_Q(QDeclarativeState);
304 for (int ii = 0; ii < reverting.count(); ++ii) {
305 for (int jj = 0; jj < revertList.count(); ++jj) {
306 const QDeclarativeRevertAction &revert = reverting.at(ii);
307 const QDeclarativeSimpleAction &simple = revertList.at(jj);
308 if ((revert.event && simple.event() == revert.event) ||
309 simple.property() == revert.property) {
310 revertList.removeAt(jj);
318 group->stateAboutToComplete();
322 // Generate a list of actions for this state. This includes coelescing state
323 // actions that this state "extends"
324 QDeclarativeStateOperation::ActionList
325 QDeclarativeStatePrivate::generateActionList() const
327 QDeclarativeStateOperation::ActionList applyList;
331 // Prevent "extends" recursion
334 if (!extends.isEmpty()) {
335 QList<QDeclarativeState *> states = group ? group->states() : QList<QDeclarativeState *>();
336 for (int ii = 0; ii < states.count(); ++ii)
337 if (states.at(ii)->name() == extends) {
338 qmlExecuteDeferred(states.at(ii));
339 applyList = static_cast<QDeclarativeStatePrivate*>(states.at(ii)->d_func())->generateActionList();
343 foreach(QDeclarativeStateOperation *op, operations)
344 applyList << op->actions();
350 QDeclarativeStateGroup *QDeclarativeState::stateGroup() const
352 Q_D(const QDeclarativeState);
356 void QDeclarativeState::setStateGroup(QDeclarativeStateGroup *group)
358 Q_D(QDeclarativeState);
362 void QDeclarativeState::cancel()
364 Q_D(QDeclarativeState);
365 d->transitionManager.cancel();
368 void QDeclarativeAction::deleteFromBinding()
371 QDeclarativePropertyPrivate::setBinding(property, 0);
372 fromBinding->destroy();
377 bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QString &name) const
379 Q_D(const QDeclarativeState);
381 if (isStateActive()) {
382 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
384 while (revertListIterator.hasNext()) {
385 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
386 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
394 bool QDeclarativeState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue)
396 Q_D(QDeclarativeState);
398 if (isStateActive()) {
399 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
401 while (revertListIterator.hasNext()) {
402 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
403 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
404 simpleAction.setValue(revertValue);
413 bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding)
415 Q_D(QDeclarativeState);
417 if (isStateActive()) {
418 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
420 while (revertListIterator.hasNext()) {
421 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
422 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
423 if (simpleAction.binding())
424 simpleAction.binding()->destroy();
426 simpleAction.setBinding(binding);
435 bool QDeclarativeState::removeEntryFromRevertList(QObject *target, const QString &name)
437 Q_D(QDeclarativeState);
439 if (isStateActive()) {
440 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
442 while (revertListIterator.hasNext()) {
443 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
444 if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
445 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
447 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
448 oldBinding->destroy();
451 simpleAction.property().write(simpleAction.value());
452 if (simpleAction.binding())
453 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
455 revertListIterator.remove();
464 void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action)
466 Q_D(QDeclarativeState);
468 QDeclarativeSimpleAction simpleAction(action);
470 d->revertList.append(simpleAction);
473 void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target)
475 Q_D(QDeclarativeState);
477 if (isStateActive()) {
478 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
480 while (revertListIterator.hasNext()) {
481 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
482 if (simpleAction.property().object() == target) {
483 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
485 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
486 oldBinding->destroy();
489 simpleAction.property().write(simpleAction.value());
490 if (simpleAction.binding())
491 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
493 revertListIterator.remove();
499 void QDeclarativeState::addEntriesToRevertList(const QList<QDeclarativeAction> &actionList)
501 Q_D(QDeclarativeState);
502 if (isStateActive()) {
503 QList<QDeclarativeSimpleAction> simpleActionList;
505 QListIterator<QDeclarativeAction> actionListIterator(actionList);
506 while(actionListIterator.hasNext()) {
507 const QDeclarativeAction &action = actionListIterator.next();
508 QDeclarativeSimpleAction simpleAction(action);
509 action.property.write(action.toValue);
510 if (!action.toBinding.isNull()) {
511 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
513 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
514 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding);
517 simpleActionList.append(simpleAction);
520 d->revertList.append(simpleActionList);
524 QVariant QDeclarativeState::valueInRevertList(QObject *target, const QString &name) const
526 Q_D(const QDeclarativeState);
528 if (isStateActive()) {
529 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
531 while (revertListIterator.hasNext()) {
532 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
533 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
534 return simpleAction.value();
541 QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QString &name) const
543 Q_D(const QDeclarativeState);
545 if (isStateActive()) {
546 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
548 while (revertListIterator.hasNext()) {
549 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
550 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
551 return simpleAction.binding();
558 bool QDeclarativeState::isStateActive() const
560 return stateGroup() && stateGroup()->state() == name();
563 void QDeclarativeState::apply(QDeclarativeTransition *trans, QDeclarativeState *revert)
565 Q_D(QDeclarativeState);
567 qmlExecuteDeferred(this);
572 d->revertList.clear();
573 d->reverting.clear();
576 QDeclarativeStatePrivate *revertPrivate =
577 static_cast<QDeclarativeStatePrivate*>(revert->d_func());
578 d->revertList = revertPrivate->revertList;
579 revertPrivate->revertList.clear();
582 // List of actions caused by this state
583 QDeclarativeStateOperation::ActionList applyList = d->generateActionList();
585 // List of actions that need to be reverted to roll back (just) this state
586 QDeclarativeStatePrivate::SimpleActionList additionalReverts;
587 // First add the reverse of all the applyList actions
588 for (int ii = 0; ii < applyList.count(); ++ii) {
589 QDeclarativeAction &action = applyList[ii];
592 if (!action.event->isReversable())
595 for (int jj = 0; jj < d->revertList.count(); ++jj) {
596 QDeclarativeActionEvent *event = d->revertList.at(jj).event();
597 if (event && event->typeName() == action.event->typeName()) {
598 if (action.event->override(event)) {
601 if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) {
602 action.event->copyOriginals(d->revertList.at(jj).event());
604 QDeclarativeSimpleAction r(action);
605 additionalReverts << r;
606 d->revertList.removeAt(jj);
608 } else if (action.event->isRewindable()) //###why needed?
609 action.event->saveCurrentValues();
616 action.event->saveOriginals();
617 // Only need to revert the applyList action if the previous
618 // state doesn't have a higher priority revert already
619 QDeclarativeSimpleAction r(action);
620 additionalReverts << r;
624 action.fromBinding = QDeclarativePropertyPrivate::binding(action.property);
626 for (int jj = 0; jj < d->revertList.count(); ++jj) {
627 if (d->revertList.at(jj).property() == action.property) {
629 if (d->revertList.at(jj).binding() != action.fromBinding) {
630 action.deleteFromBinding();
637 if (!action.restore) {
638 action.deleteFromBinding();;
640 // Only need to revert the applyList action if the previous
641 // state doesn't have a higher priority revert already
642 QDeclarativeSimpleAction r(action);
643 additionalReverts << r;
649 // Any reverts from a previous state that aren't carried forth
650 // into this state need to be translated into apply actions
651 for (int ii = 0; ii < d->revertList.count(); ++ii) {
653 if (d->revertList.at(ii).event()) {
654 QDeclarativeActionEvent *event = d->revertList.at(ii).event();
655 if (!event->isReversable())
657 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
658 const QDeclarativeAction &action = applyList.at(jj);
659 if (action.event && action.event->typeName() == event->typeName()) {
660 if (action.event->override(event))
665 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
666 const QDeclarativeAction &action = applyList.at(jj);
667 if (action.property == d->revertList.at(ii).property())
672 QVariant cur = d->revertList.at(ii).property().read();
673 QDeclarativeAbstractBinding *delBinding =
674 QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
676 delBinding->destroy();
678 QDeclarativeAction a;
679 a.property = d->revertList.at(ii).property();
681 a.toValue = d->revertList.at(ii).value();
682 a.toBinding = QDeclarativeAbstractBinding::getPointer(d->revertList.at(ii).binding());
683 a.specifiedObject = d->revertList.at(ii).specifiedObject();
684 a.specifiedProperty = d->revertList.at(ii).specifiedProperty();
685 a.event = d->revertList.at(ii).event();
686 a.reverseEvent = d->revertList.at(ii).reverseEvent();
687 if (a.event && a.event->isRewindable())
688 a.event->saveCurrentValues();
690 // Store these special reverts in the reverting list
692 d->reverting << a.event;
694 d->reverting << a.property;
697 // All the local reverts now become part of the ongoing revertList
698 d->revertList << additionalReverts;
700 #ifndef QT_NO_DEBUG_STREAM
701 // Output for debugging
702 if (stateChangeDebug()) {
703 foreach(const QDeclarativeAction &action, applyList) {
705 qWarning() << " QDeclarativeAction event:" << action.event->typeName();
707 qWarning() << " QDeclarativeAction:" << action.property.object()
708 << action.property.name() << "From:" << action.fromValue
709 << "To:" << action.toValue;
714 d->transitionManager.transition(applyList, trans);
717 QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions()
722 QDeclarativeState *QDeclarativeStateOperation::state() const
724 Q_D(const QDeclarativeStateOperation);
728 void QDeclarativeStateOperation::setState(QDeclarativeState *state)
730 Q_D(QDeclarativeStateOperation);