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 QtQml 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 "qquickstate_p_p.h"
43 #include "qquickstate_p.h"
45 #include "qquickstategroup_p.h"
46 #include "qquickstatechangescript_p.h"
48 #include <private/qqmlglobal_p.h>
50 #include <QtCore/qdebug.h>
54 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
56 QQuickAction::QQuickAction()
57 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0),
62 QQuickAction::QQuickAction(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 QQuickAction::QQuickAction(QObject *target, const QString &propertyName,
74 QQmlContext *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 QQuickActionEvent::~QQuickActionEvent()
89 void QQuickActionEvent::execute(Reason)
93 bool QQuickActionEvent::isReversable()
98 void QQuickActionEvent::reverse(Reason)
102 bool QQuickActionEvent::changesBindings()
107 void QQuickActionEvent::clearBindings()
111 bool QQuickActionEvent::override(QQuickActionEvent *other)
117 QQuickStateOperation::QQuickStateOperation(QObjectPrivate &dd, QObject *parent)
118 : QObject(dd, parent)
123 \qmlclass State QQuickState
124 \inqmlmodule QtQuick 2
125 \ingroup qml-state-elements
126 \brief The State element defines configurations of objects and properties.
128 A \e state is a set of batched changes from the default configuration.
130 All items have a default state that defines the default configuration of objects
131 and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to
132 allow items to switch between different configurations. These configurations
133 can, for example, be used to apply different sets of property values or execute
136 The following example displays a single \l Rectangle. In the default state, the rectangle
137 is colored black. In the "clicked" state, a PropertyChanges element changes the
138 rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state
139 between the default state and the "clicked" state, thus toggling the color of the
140 rectangle between black and red.
142 \snippet doc/snippets/qml/state.qml 0
144 Notice the default state is referred to using an empty string ("").
146 States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide
147 animations when state changes occur.
149 \note Setting the state of an object from within another state of the same object is
152 \sa {declarative/animation/states}{states example}, {qmlstates}{States},
153 {QML Animation and Transitions}{Transitions}, QtQml
155 QQuickState::QQuickState(QObject *parent)
156 : QObject(*(new QQuickStatePrivate), parent)
159 d->transitionManager.setState(this);
162 QQuickState::~QQuickState()
166 d->group->removeState(this);
170 \qmlproperty string QtQuick2::State::name
171 This property holds the name of the state.
173 Each state should have a unique name within its item.
175 QString QQuickState::name() const
177 Q_D(const QQuickState);
181 void QQuickState::setName(const QString &n)
188 bool QQuickState::isNamed() const
190 Q_D(const QQuickState);
194 bool QQuickState::isWhenKnown() const
196 Q_D(const QQuickState);
201 \qmlproperty bool QtQuick2::State::when
202 This property holds when the state should be applied.
204 This should be set to an expression that evaluates to \c true when you want the state to
205 be applied. For example, the following \l Rectangle changes in and out of the "hidden"
206 state when the \l MouseArea is pressed:
208 \snippet doc/snippets/qml/state-when.qml 0
210 If multiple states in a group have \c when clauses that evaluate to \c true
211 at the same time, the first matching state will be applied. For example, in
212 the following snippet \c state1 will always be selected rather than
213 \c state2 when sharedCondition becomes \c true.
217 State { name: "state1"; when: sharedCondition },
218 State { name: "state2"; when: sharedCondition }
224 QQmlBinding *QQuickState::when() const
226 Q_D(const QQuickState);
230 void QQuickState::setWhen(QQmlBinding *when)
235 d->group->updateAutoState();
239 \qmlproperty string QtQuick2::State::extend
240 This property holds the state that this state extends.
242 When a state extends another state, it inherits all the changes of that state.
244 The state being extended is treated as the base state in regards to
245 the changes specified by the extending state.
247 QString QQuickState::extends() const
249 Q_D(const QQuickState);
253 void QQuickState::setExtends(const QString &extends)
256 d->extends = extends;
260 \qmlproperty list<Change> QtQuick2::State::changes
261 This property holds the changes to apply for this state
264 By default these changes are applied against the default state. If the state
265 extends another state, then the changes are applied against the state being
268 QQmlListProperty<QQuickStateOperation> QQuickState::changes()
271 return QQmlListProperty<QQuickStateOperation>(this, &d->operations, QQuickStatePrivate::operations_append,
272 QQuickStatePrivate::operations_count, QQuickStatePrivate::operations_at,
273 QQuickStatePrivate::operations_clear);
276 int QQuickState::operationCount() const
278 Q_D(const QQuickState);
279 return d->operations.count();
282 QQuickStateOperation *QQuickState::operationAt(int index) const
284 Q_D(const QQuickState);
285 return d->operations.at(index);
288 QQuickState &QQuickState::operator<<(QQuickStateOperation *op)
291 d->operations.append(QQuickStatePrivate::OperationGuard(op, &d->operations));
295 void QQuickStatePrivate::complete()
299 for (int ii = 0; ii < reverting.count(); ++ii) {
300 for (int jj = 0; jj < revertList.count(); ++jj) {
301 const QQuickRevertAction &revert = reverting.at(ii);
302 const QQuickSimpleAction &simple = revertList.at(jj);
303 if ((revert.event && simple.event() == revert.event) ||
304 simple.property() == revert.property) {
305 revertList.removeAt(jj);
313 group->stateAboutToComplete();
317 // Generate a list of actions for this state. This includes coelescing state
318 // actions that this state "extends"
319 QQuickStateOperation::ActionList
320 QQuickStatePrivate::generateActionList() const
322 QQuickStateOperation::ActionList applyList;
326 // Prevent "extends" recursion
329 if (!extends.isEmpty()) {
330 QList<QQuickState *> states = group ? group->states() : QList<QQuickState *>();
331 for (int ii = 0; ii < states.count(); ++ii)
332 if (states.at(ii)->name() == extends) {
333 qmlExecuteDeferred(states.at(ii));
334 applyList = static_cast<QQuickStatePrivate*>(states.at(ii)->d_func())->generateActionList();
338 foreach(QQuickStateOperation *op, operations)
339 applyList << op->actions();
345 QQuickStateGroup *QQuickState::stateGroup() const
347 Q_D(const QQuickState);
351 void QQuickState::setStateGroup(QQuickStateGroup *group)
357 void QQuickState::cancel()
360 d->transitionManager.cancel();
363 void QQuickAction::deleteFromBinding()
366 QQmlPropertyPrivate::setBinding(property, 0);
367 fromBinding->destroy();
372 bool QQuickState::containsPropertyInRevertList(QObject *target, const QString &name) const
374 Q_D(const QQuickState);
376 if (isStateActive()) {
377 QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
379 while (revertListIterator.hasNext()) {
380 const QQuickSimpleAction &simpleAction = revertListIterator.next();
381 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
389 bool QQuickState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue)
393 if (isStateActive()) {
394 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
396 while (revertListIterator.hasNext()) {
397 QQuickSimpleAction &simpleAction = revertListIterator.next();
398 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
399 simpleAction.setValue(revertValue);
408 bool QQuickState::changeBindingInRevertList(QObject *target, const QString &name, QQmlAbstractBinding *binding)
412 if (isStateActive()) {
413 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
415 while (revertListIterator.hasNext()) {
416 QQuickSimpleAction &simpleAction = revertListIterator.next();
417 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
418 if (simpleAction.binding())
419 simpleAction.binding()->destroy();
421 simpleAction.setBinding(binding);
430 bool QQuickState::removeEntryFromRevertList(QObject *target, const QString &name)
434 if (isStateActive()) {
435 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
437 while (revertListIterator.hasNext()) {
438 QQuickSimpleAction &simpleAction = revertListIterator.next();
439 if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
440 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
442 QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
443 oldBinding->destroy();
446 simpleAction.property().write(simpleAction.value());
447 if (simpleAction.binding())
448 QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
450 revertListIterator.remove();
459 void QQuickState::addEntryToRevertList(const QQuickAction &action)
463 QQuickSimpleAction simpleAction(action);
465 d->revertList.append(simpleAction);
468 void QQuickState::removeAllEntriesFromRevertList(QObject *target)
472 if (isStateActive()) {
473 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
475 while (revertListIterator.hasNext()) {
476 QQuickSimpleAction &simpleAction = revertListIterator.next();
477 if (simpleAction.property().object() == target) {
478 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
480 QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
481 oldBinding->destroy();
484 simpleAction.property().write(simpleAction.value());
485 if (simpleAction.binding())
486 QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
488 revertListIterator.remove();
494 void QQuickState::addEntriesToRevertList(const QList<QQuickAction> &actionList)
497 if (isStateActive()) {
498 QList<QQuickSimpleAction> simpleActionList;
500 QListIterator<QQuickAction> actionListIterator(actionList);
501 while(actionListIterator.hasNext()) {
502 const QQuickAction &action = actionListIterator.next();
503 QQuickSimpleAction simpleAction(action);
504 action.property.write(action.toValue);
505 if (!action.toBinding.isNull()) {
506 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
508 QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
509 QQmlPropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QQmlPropertyPrivate::DontRemoveBinding);
512 simpleActionList.append(simpleAction);
515 d->revertList.append(simpleActionList);
519 QVariant QQuickState::valueInRevertList(QObject *target, const QString &name) const
521 Q_D(const QQuickState);
523 if (isStateActive()) {
524 QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
526 while (revertListIterator.hasNext()) {
527 const QQuickSimpleAction &simpleAction = revertListIterator.next();
528 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
529 return simpleAction.value();
536 QQmlAbstractBinding *QQuickState::bindingInRevertList(QObject *target, const QString &name) const
538 Q_D(const QQuickState);
540 if (isStateActive()) {
541 QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
543 while (revertListIterator.hasNext()) {
544 const QQuickSimpleAction &simpleAction = revertListIterator.next();
545 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
546 return simpleAction.binding();
553 bool QQuickState::isStateActive() const
555 return stateGroup() && stateGroup()->state() == name();
558 void QQuickState::apply(QQuickTransition *trans, QQuickState *revert)
562 qmlExecuteDeferred(this);
567 d->revertList.clear();
568 d->reverting.clear();
571 QQuickStatePrivate *revertPrivate =
572 static_cast<QQuickStatePrivate*>(revert->d_func());
573 d->revertList = revertPrivate->revertList;
574 revertPrivate->revertList.clear();
577 // List of actions caused by this state
578 QQuickStateOperation::ActionList applyList = d->generateActionList();
580 // List of actions that need to be reverted to roll back (just) this state
581 QQuickStatePrivate::SimpleActionList additionalReverts;
582 // First add the reverse of all the applyList actions
583 for (int ii = 0; ii < applyList.count(); ++ii) {
584 QQuickAction &action = applyList[ii];
587 if (!action.event->isReversable())
590 for (int jj = 0; jj < d->revertList.count(); ++jj) {
591 QQuickActionEvent *event = d->revertList.at(jj).event();
592 if (event && event->type() == action.event->type()) {
593 if (action.event->override(event)) {
596 if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) {
597 action.event->copyOriginals(d->revertList.at(jj).event());
599 QQuickSimpleAction r(action);
600 additionalReverts << r;
601 d->revertList.removeAt(jj);
603 } else if (action.event->isRewindable()) //###why needed?
604 action.event->saveCurrentValues();
611 action.event->saveOriginals();
612 // Only need to revert the applyList action if the previous
613 // state doesn't have a higher priority revert already
614 QQuickSimpleAction r(action);
615 additionalReverts << r;
619 action.fromBinding = QQmlPropertyPrivate::binding(action.property);
621 for (int jj = 0; jj < d->revertList.count(); ++jj) {
622 if (d->revertList.at(jj).property() == action.property) {
624 if (d->revertList.at(jj).binding() != action.fromBinding) {
625 action.deleteFromBinding();
632 if (!action.restore) {
633 action.deleteFromBinding();;
635 // Only need to revert the applyList action if the previous
636 // state doesn't have a higher priority revert already
637 QQuickSimpleAction r(action);
638 additionalReverts << r;
644 // Any reverts from a previous state that aren't carried forth
645 // into this state need to be translated into apply actions
646 for (int ii = 0; ii < d->revertList.count(); ++ii) {
648 if (d->revertList.at(ii).event()) {
649 QQuickActionEvent *event = d->revertList.at(ii).event();
650 if (!event->isReversable())
652 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
653 const QQuickAction &action = applyList.at(jj);
654 if (action.event && action.event->type() == event->type()) {
655 if (action.event->override(event))
660 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
661 const QQuickAction &action = applyList.at(jj);
662 if (action.property == d->revertList.at(ii).property())
667 QVariant cur = d->revertList.at(ii).property().read();
668 QQmlAbstractBinding *delBinding =
669 QQmlPropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
671 delBinding->destroy();
674 a.property = d->revertList.at(ii).property();
676 a.toValue = d->revertList.at(ii).value();
677 a.toBinding = QQmlAbstractBinding::getPointer(d->revertList.at(ii).binding());
678 a.specifiedObject = d->revertList.at(ii).specifiedObject();
679 a.specifiedProperty = d->revertList.at(ii).specifiedProperty();
680 a.event = d->revertList.at(ii).event();
681 a.reverseEvent = d->revertList.at(ii).reverseEvent();
682 if (a.event && a.event->isRewindable())
683 a.event->saveCurrentValues();
685 // Store these special reverts in the reverting list
687 d->reverting << a.event;
689 d->reverting << a.property;
692 // All the local reverts now become part of the ongoing revertList
693 d->revertList << additionalReverts;
695 #ifndef QT_NO_DEBUG_STREAM
696 // Output for debugging
697 if (stateChangeDebug()) {
698 foreach(const QQuickAction &action, applyList) {
700 qWarning() << " QQuickAction event:" << action.event->type();
702 qWarning() << " QQuickAction:" << action.property.object()
703 << action.property.name() << "From:" << action.fromValue
704 << "To:" << action.toValue;
709 d->transitionManager.transition(applyList, trans);
712 QQuickStateOperation::ActionList QQuickStateOperation::actions()
717 QQuickState *QQuickStateOperation::state() const
719 Q_D(const QQuickStateOperation);
723 void QQuickStateOperation::setState(QQuickState *state)
725 Q_D(QQuickStateOperation);