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 void QDeclarativeActionEvent::execute(Reason)
93 bool QDeclarativeActionEvent::isReversable()
98 void QDeclarativeActionEvent::reverse(Reason)
102 bool QDeclarativeActionEvent::changesBindings()
107 void QDeclarativeActionEvent::clearBindings()
111 bool QDeclarativeActionEvent::override(QDeclarativeActionEvent *other)
117 QDeclarativeStateOperation::QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent)
118 : QObject(dd, parent)
123 \qmlclass State QDeclarativeState
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/src/snippets/declarative/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}, QtDeclarative
155 QDeclarativeState::QDeclarativeState(QObject *parent)
156 : QObject(*(new QDeclarativeStatePrivate), parent)
158 Q_D(QDeclarativeState);
159 d->transitionManager.setState(this);
162 QDeclarativeState::~QDeclarativeState()
164 Q_D(QDeclarativeState);
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 QDeclarativeState::name() const
177 Q_D(const QDeclarativeState);
181 void QDeclarativeState::setName(const QString &n)
183 Q_D(QDeclarativeState);
188 bool QDeclarativeState::isNamed() const
190 Q_D(const QDeclarativeState);
194 bool QDeclarativeState::isWhenKnown() const
196 Q_D(const QDeclarativeState);
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/src/snippets/declarative/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 QDeclarativeBinding *QDeclarativeState::when() const
226 Q_D(const QDeclarativeState);
230 void QDeclarativeState::setWhen(QDeclarativeBinding *when)
232 Q_D(QDeclarativeState);
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 QDeclarativeState::extends() const
249 Q_D(const QDeclarativeState);
253 void QDeclarativeState::setExtends(const QString &extends)
255 Q_D(QDeclarativeState);
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 QDeclarativeListProperty<QDeclarativeStateOperation> QDeclarativeState::changes()
270 Q_D(QDeclarativeState);
271 return QDeclarativeListProperty<QDeclarativeStateOperation>(this, &d->operations, QDeclarativeStatePrivate::operations_append,
272 QDeclarativeStatePrivate::operations_count, QDeclarativeStatePrivate::operations_at,
273 QDeclarativeStatePrivate::operations_clear);
276 int QDeclarativeState::operationCount() const
278 Q_D(const QDeclarativeState);
279 return d->operations.count();
282 QDeclarativeStateOperation *QDeclarativeState::operationAt(int index) const
284 Q_D(const QDeclarativeState);
285 return d->operations.at(index);
288 QDeclarativeState &QDeclarativeState::operator<<(QDeclarativeStateOperation *op)
290 Q_D(QDeclarativeState);
291 d->operations.append(QDeclarativeStatePrivate::OperationGuard(op, &d->operations));
295 void QDeclarativeStatePrivate::complete()
297 Q_Q(QDeclarativeState);
299 for (int ii = 0; ii < reverting.count(); ++ii) {
300 for (int jj = 0; jj < revertList.count(); ++jj) {
301 const QDeclarativeRevertAction &revert = reverting.at(ii);
302 const QDeclarativeSimpleAction &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 QDeclarativeStateOperation::ActionList
320 QDeclarativeStatePrivate::generateActionList() const
322 QDeclarativeStateOperation::ActionList applyList;
326 // Prevent "extends" recursion
329 if (!extends.isEmpty()) {
330 QList<QDeclarativeState *> states = group ? group->states() : QList<QDeclarativeState *>();
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<QDeclarativeStatePrivate*>(states.at(ii)->d_func())->generateActionList();
338 foreach(QDeclarativeStateOperation *op, operations)
339 applyList << op->actions();
345 QDeclarativeStateGroup *QDeclarativeState::stateGroup() const
347 Q_D(const QDeclarativeState);
351 void QDeclarativeState::setStateGroup(QDeclarativeStateGroup *group)
353 Q_D(QDeclarativeState);
357 void QDeclarativeState::cancel()
359 Q_D(QDeclarativeState);
360 d->transitionManager.cancel();
363 void QDeclarativeAction::deleteFromBinding()
366 QDeclarativePropertyPrivate::setBinding(property, 0);
367 fromBinding->destroy();
372 bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QString &name) const
374 Q_D(const QDeclarativeState);
376 if (isStateActive()) {
377 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
379 while (revertListIterator.hasNext()) {
380 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
381 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
389 bool QDeclarativeState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue)
391 Q_D(QDeclarativeState);
393 if (isStateActive()) {
394 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
396 while (revertListIterator.hasNext()) {
397 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
398 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
399 simpleAction.setValue(revertValue);
408 bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding)
410 Q_D(QDeclarativeState);
412 if (isStateActive()) {
413 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
415 while (revertListIterator.hasNext()) {
416 QDeclarativeSimpleAction &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 QDeclarativeState::removeEntryFromRevertList(QObject *target, const QString &name)
432 Q_D(QDeclarativeState);
434 if (isStateActive()) {
435 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
437 while (revertListIterator.hasNext()) {
438 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
439 if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
440 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
442 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
443 oldBinding->destroy();
446 simpleAction.property().write(simpleAction.value());
447 if (simpleAction.binding())
448 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
450 revertListIterator.remove();
459 void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action)
461 Q_D(QDeclarativeState);
463 QDeclarativeSimpleAction simpleAction(action);
465 d->revertList.append(simpleAction);
468 void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target)
470 Q_D(QDeclarativeState);
472 if (isStateActive()) {
473 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
475 while (revertListIterator.hasNext()) {
476 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
477 if (simpleAction.property().object() == target) {
478 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
480 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
481 oldBinding->destroy();
484 simpleAction.property().write(simpleAction.value());
485 if (simpleAction.binding())
486 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
488 revertListIterator.remove();
494 void QDeclarativeState::addEntriesToRevertList(const QList<QDeclarativeAction> &actionList)
496 Q_D(QDeclarativeState);
497 if (isStateActive()) {
498 QList<QDeclarativeSimpleAction> simpleActionList;
500 QListIterator<QDeclarativeAction> actionListIterator(actionList);
501 while(actionListIterator.hasNext()) {
502 const QDeclarativeAction &action = actionListIterator.next();
503 QDeclarativeSimpleAction simpleAction(action);
504 action.property.write(action.toValue);
505 if (!action.toBinding.isNull()) {
506 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
508 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
509 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding);
512 simpleActionList.append(simpleAction);
515 d->revertList.append(simpleActionList);
519 QVariant QDeclarativeState::valueInRevertList(QObject *target, const QString &name) const
521 Q_D(const QDeclarativeState);
523 if (isStateActive()) {
524 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
526 while (revertListIterator.hasNext()) {
527 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
528 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
529 return simpleAction.value();
536 QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QString &name) const
538 Q_D(const QDeclarativeState);
540 if (isStateActive()) {
541 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
543 while (revertListIterator.hasNext()) {
544 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
545 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
546 return simpleAction.binding();
553 bool QDeclarativeState::isStateActive() const
555 return stateGroup() && stateGroup()->state() == name();
558 void QDeclarativeState::apply(QDeclarativeTransition *trans, QDeclarativeState *revert)
560 Q_D(QDeclarativeState);
562 qmlExecuteDeferred(this);
567 d->revertList.clear();
568 d->reverting.clear();
571 QDeclarativeStatePrivate *revertPrivate =
572 static_cast<QDeclarativeStatePrivate*>(revert->d_func());
573 d->revertList = revertPrivate->revertList;
574 revertPrivate->revertList.clear();
577 // List of actions caused by this state
578 QDeclarativeStateOperation::ActionList applyList = d->generateActionList();
580 // List of actions that need to be reverted to roll back (just) this state
581 QDeclarativeStatePrivate::SimpleActionList additionalReverts;
582 // First add the reverse of all the applyList actions
583 for (int ii = 0; ii < applyList.count(); ++ii) {
584 QDeclarativeAction &action = applyList[ii];
587 if (!action.event->isReversable())
590 for (int jj = 0; jj < d->revertList.count(); ++jj) {
591 QDeclarativeActionEvent *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 QDeclarativeSimpleAction 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 QDeclarativeSimpleAction r(action);
615 additionalReverts << r;
619 action.fromBinding = QDeclarativePropertyPrivate::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 QDeclarativeSimpleAction 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 QDeclarativeActionEvent *event = d->revertList.at(ii).event();
650 if (!event->isReversable())
652 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
653 const QDeclarativeAction &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 QDeclarativeAction &action = applyList.at(jj);
662 if (action.property == d->revertList.at(ii).property())
667 QVariant cur = d->revertList.at(ii).property().read();
668 QDeclarativeAbstractBinding *delBinding =
669 QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
671 delBinding->destroy();
673 QDeclarativeAction a;
674 a.property = d->revertList.at(ii).property();
676 a.toValue = d->revertList.at(ii).value();
677 a.toBinding = QDeclarativeAbstractBinding::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 QDeclarativeAction &action, applyList) {
700 qWarning() << " QDeclarativeAction event:" << action.event->type();
702 qWarning() << " QDeclarativeAction:" << action.property.object()
703 << action.property.name() << "From:" << action.fromValue
704 << "To:" << action.toValue;
709 d->transitionManager.transition(applyList, trans);
712 QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions()
717 QDeclarativeState *QDeclarativeStateOperation::state() const
719 Q_D(const QDeclarativeStateOperation);
723 void QDeclarativeStateOperation::setState(QDeclarativeState *state)
725 Q_D(QDeclarativeStateOperation);