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)
124 \instantiates QQuickState
125 \inqmlmodule QtQuick 2
126 \ingroup qtquick-states
127 \brief Defines configurations of objects and properties
129 A \e state is a set of batched changes from the default configuration.
131 All items have a default state that defines the default configuration of objects
132 and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to
133 allow items to switch between different configurations. These configurations
134 can, for example, be used to apply different sets of property values or execute
137 The following example displays a single \l Rectangle. In the default state, the rectangle
138 is colored black. In the "clicked" state, a PropertyChanges object changes the
139 rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state
140 between the default state and the "clicked" state, thus toggling the color of the
141 rectangle between black and red.
143 \snippet qml/state.qml 0
145 Notice the default state is referred to using an empty string ("").
147 States are commonly used together with \l{Animation and Transitions in Qt Quick}{Transitions} to provide
148 animations when state changes occur.
150 \note Setting the state of an object from within another state of the same object is
153 \sa {declarative/animation/states}{states example}, {Qt Quick States}{Qt Quick States},
154 {Animation and Transitions in Qt Quick}{Transitions}, QtQml
156 QQuickState::QQuickState(QObject *parent)
157 : QObject(*(new QQuickStatePrivate), parent)
160 d->transitionManager.setState(this);
163 QQuickState::~QQuickState()
167 d->group->removeState(this);
171 \qmlproperty string QtQuick2::State::name
172 This property holds the name of the state.
174 Each state should have a unique name within its item.
176 QString QQuickState::name() const
178 Q_D(const QQuickState);
182 void QQuickState::setName(const QString &n)
189 bool QQuickState::isNamed() const
191 Q_D(const QQuickState);
195 bool QQuickState::isWhenKnown() const
197 Q_D(const QQuickState);
202 \qmlproperty bool QtQuick2::State::when
203 This property holds when the state should be applied.
205 This should be set to an expression that evaluates to \c true when you want the state to
206 be applied. For example, the following \l Rectangle changes in and out of the "hidden"
207 state when the \l MouseArea is pressed:
209 \snippet qml/state-when.qml 0
211 If multiple states in a group have \c when clauses that evaluate to \c true
212 at the same time, the first matching state will be applied. For example, in
213 the following snippet \c state1 will always be selected rather than
214 \c state2 when sharedCondition becomes \c true.
218 State { name: "state1"; when: sharedCondition },
219 State { name: "state2"; when: sharedCondition }
225 QQmlBinding *QQuickState::when() const
227 Q_D(const QQuickState);
231 void QQuickState::setWhen(QQmlBinding *when)
236 d->group->updateAutoState();
240 \qmlproperty string QtQuick2::State::extend
241 This property holds the state that this state extends.
243 When a state extends another state, it inherits all the changes of that state.
245 The state being extended is treated as the base state in regards to
246 the changes specified by the extending state.
248 QString QQuickState::extends() const
250 Q_D(const QQuickState);
254 void QQuickState::setExtends(const QString &extends)
257 d->extends = extends;
261 \qmlproperty list<Change> QtQuick2::State::changes
262 This property holds the changes to apply for this state
265 By default these changes are applied against the default state. If the state
266 extends another state, then the changes are applied against the state being
269 QQmlListProperty<QQuickStateOperation> QQuickState::changes()
272 return QQmlListProperty<QQuickStateOperation>(this, &d->operations, QQuickStatePrivate::operations_append,
273 QQuickStatePrivate::operations_count, QQuickStatePrivate::operations_at,
274 QQuickStatePrivate::operations_clear);
277 int QQuickState::operationCount() const
279 Q_D(const QQuickState);
280 return d->operations.count();
283 QQuickStateOperation *QQuickState::operationAt(int index) const
285 Q_D(const QQuickState);
286 return d->operations.at(index);
289 QQuickState &QQuickState::operator<<(QQuickStateOperation *op)
292 d->operations.append(QQuickStatePrivate::OperationGuard(op, &d->operations));
296 void QQuickStatePrivate::complete()
300 for (int ii = 0; ii < reverting.count(); ++ii) {
301 for (int jj = 0; jj < revertList.count(); ++jj) {
302 const QQuickRevertAction &revert = reverting.at(ii);
303 const QQuickSimpleAction &simple = revertList.at(jj);
304 if ((revert.event && simple.event() == revert.event) ||
305 simple.property() == revert.property) {
306 revertList.removeAt(jj);
314 group->stateAboutToComplete();
318 // Generate a list of actions for this state. This includes coelescing state
319 // actions that this state "extends"
320 QQuickStateOperation::ActionList
321 QQuickStatePrivate::generateActionList() const
323 QQuickStateOperation::ActionList applyList;
327 // Prevent "extends" recursion
330 if (!extends.isEmpty()) {
331 QList<QQuickState *> states = group ? group->states() : QList<QQuickState *>();
332 for (int ii = 0; ii < states.count(); ++ii)
333 if (states.at(ii)->name() == extends) {
334 qmlExecuteDeferred(states.at(ii));
335 applyList = static_cast<QQuickStatePrivate*>(states.at(ii)->d_func())->generateActionList();
339 foreach(QQuickStateOperation *op, operations)
340 applyList << op->actions();
346 QQuickStateGroup *QQuickState::stateGroup() const
348 Q_D(const QQuickState);
352 void QQuickState::setStateGroup(QQuickStateGroup *group)
358 void QQuickState::cancel()
361 d->transitionManager.cancel();
364 void QQuickAction::deleteFromBinding()
367 QQmlPropertyPrivate::setBinding(property, 0);
368 fromBinding->destroy();
373 bool QQuickState::containsPropertyInRevertList(QObject *target, const QString &name) const
375 Q_D(const QQuickState);
377 if (isStateActive()) {
378 QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
380 while (revertListIterator.hasNext()) {
381 const QQuickSimpleAction &simpleAction = revertListIterator.next();
382 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
390 bool QQuickState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue)
394 if (isStateActive()) {
395 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
397 while (revertListIterator.hasNext()) {
398 QQuickSimpleAction &simpleAction = revertListIterator.next();
399 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
400 simpleAction.setValue(revertValue);
409 bool QQuickState::changeBindingInRevertList(QObject *target, const QString &name, QQmlAbstractBinding *binding)
413 if (isStateActive()) {
414 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
416 while (revertListIterator.hasNext()) {
417 QQuickSimpleAction &simpleAction = revertListIterator.next();
418 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
419 if (simpleAction.binding())
420 simpleAction.binding()->destroy();
422 simpleAction.setBinding(binding);
431 bool QQuickState::removeEntryFromRevertList(QObject *target, const QString &name)
435 if (isStateActive()) {
436 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
438 while (revertListIterator.hasNext()) {
439 QQuickSimpleAction &simpleAction = revertListIterator.next();
440 if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
441 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
443 QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
444 oldBinding->destroy();
447 simpleAction.property().write(simpleAction.value());
448 if (simpleAction.binding())
449 QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
451 revertListIterator.remove();
460 void QQuickState::addEntryToRevertList(const QQuickAction &action)
464 QQuickSimpleAction simpleAction(action);
466 d->revertList.append(simpleAction);
469 void QQuickState::removeAllEntriesFromRevertList(QObject *target)
473 if (isStateActive()) {
474 QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
476 while (revertListIterator.hasNext()) {
477 QQuickSimpleAction &simpleAction = revertListIterator.next();
478 if (simpleAction.property().object() == target) {
479 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
481 QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
482 oldBinding->destroy();
485 simpleAction.property().write(simpleAction.value());
486 if (simpleAction.binding())
487 QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
489 revertListIterator.remove();
495 void QQuickState::addEntriesToRevertList(const QList<QQuickAction> &actionList)
498 if (isStateActive()) {
499 QList<QQuickSimpleAction> simpleActionList;
501 QListIterator<QQuickAction> actionListIterator(actionList);
502 while(actionListIterator.hasNext()) {
503 const QQuickAction &action = actionListIterator.next();
504 QQuickSimpleAction simpleAction(action);
505 action.property.write(action.toValue);
506 if (!action.toBinding.isNull()) {
507 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
509 QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
510 QQmlPropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QQmlPropertyPrivate::DontRemoveBinding);
513 simpleActionList.append(simpleAction);
516 d->revertList.append(simpleActionList);
520 QVariant QQuickState::valueInRevertList(QObject *target, const QString &name) const
522 Q_D(const QQuickState);
524 if (isStateActive()) {
525 QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
527 while (revertListIterator.hasNext()) {
528 const QQuickSimpleAction &simpleAction = revertListIterator.next();
529 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
530 return simpleAction.value();
537 QQmlAbstractBinding *QQuickState::bindingInRevertList(QObject *target, const QString &name) const
539 Q_D(const QQuickState);
541 if (isStateActive()) {
542 QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
544 while (revertListIterator.hasNext()) {
545 const QQuickSimpleAction &simpleAction = revertListIterator.next();
546 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
547 return simpleAction.binding();
554 bool QQuickState::isStateActive() const
556 return stateGroup() && stateGroup()->state() == name();
559 void QQuickState::apply(QQuickTransition *trans, QQuickState *revert)
563 qmlExecuteDeferred(this);
568 d->revertList.clear();
569 d->reverting.clear();
572 QQuickStatePrivate *revertPrivate =
573 static_cast<QQuickStatePrivate*>(revert->d_func());
574 d->revertList = revertPrivate->revertList;
575 revertPrivate->revertList.clear();
578 // List of actions caused by this state
579 QQuickStateOperation::ActionList applyList = d->generateActionList();
581 // List of actions that need to be reverted to roll back (just) this state
582 QQuickStatePrivate::SimpleActionList additionalReverts;
583 // First add the reverse of all the applyList actions
584 for (int ii = 0; ii < applyList.count(); ++ii) {
585 QQuickAction &action = applyList[ii];
588 if (!action.event->isReversable())
591 for (int jj = 0; jj < d->revertList.count(); ++jj) {
592 QQuickActionEvent *event = d->revertList.at(jj).event();
593 if (event && event->type() == action.event->type()) {
594 if (action.event->override(event)) {
597 if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) {
598 action.event->copyOriginals(d->revertList.at(jj).event());
600 QQuickSimpleAction r(action);
601 additionalReverts << r;
602 d->revertList.removeAt(jj);
604 } else if (action.event->isRewindable()) //###why needed?
605 action.event->saveCurrentValues();
612 action.event->saveOriginals();
613 // Only need to revert the applyList action if the previous
614 // state doesn't have a higher priority revert already
615 QQuickSimpleAction r(action);
616 additionalReverts << r;
620 action.fromBinding = QQmlPropertyPrivate::binding(action.property);
622 for (int jj = 0; jj < d->revertList.count(); ++jj) {
623 if (d->revertList.at(jj).property() == action.property) {
625 if (d->revertList.at(jj).binding() != action.fromBinding) {
626 action.deleteFromBinding();
633 if (!action.restore) {
634 action.deleteFromBinding();;
636 // Only need to revert the applyList action if the previous
637 // state doesn't have a higher priority revert already
638 QQuickSimpleAction r(action);
639 additionalReverts << r;
645 // Any reverts from a previous state that aren't carried forth
646 // into this state need to be translated into apply actions
647 for (int ii = 0; ii < d->revertList.count(); ++ii) {
649 if (d->revertList.at(ii).event()) {
650 QQuickActionEvent *event = d->revertList.at(ii).event();
651 if (!event->isReversable())
653 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
654 const QQuickAction &action = applyList.at(jj);
655 if (action.event && action.event->type() == event->type()) {
656 if (action.event->override(event))
661 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
662 const QQuickAction &action = applyList.at(jj);
663 if (action.property == d->revertList.at(ii).property())
668 QVariant cur = d->revertList.at(ii).property().read();
669 QQmlAbstractBinding *delBinding =
670 QQmlPropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
672 delBinding->destroy();
675 a.property = d->revertList.at(ii).property();
677 a.toValue = d->revertList.at(ii).value();
678 a.toBinding = QQmlAbstractBinding::getPointer(d->revertList.at(ii).binding());
679 a.specifiedObject = d->revertList.at(ii).specifiedObject();
680 a.specifiedProperty = d->revertList.at(ii).specifiedProperty();
681 a.event = d->revertList.at(ii).event();
682 a.reverseEvent = d->revertList.at(ii).reverseEvent();
683 if (a.event && a.event->isRewindable())
684 a.event->saveCurrentValues();
686 // Store these special reverts in the reverting list
688 d->reverting << a.event;
690 d->reverting << a.property;
693 // All the local reverts now become part of the ongoing revertList
694 d->revertList << additionalReverts;
696 #ifndef QT_NO_DEBUG_STREAM
697 // Output for debugging
698 if (stateChangeDebug()) {
699 foreach(const QQuickAction &action, applyList) {
701 qWarning() << " QQuickAction event:" << action.event->type();
703 qWarning() << " QQuickAction:" << action.property.object()
704 << action.property.name() << "From:" << action.fromValue
705 << "To:" << action.toValue;
710 d->transitionManager.transition(applyList, trans);
713 QQuickStateOperation::ActionList QQuickStateOperation::actions()
718 QQuickState *QQuickStateOperation::state() const
720 Q_D(const QQuickStateOperation);
724 void QQuickStateOperation::setState(QQuickState *state)
726 Q_D(QQuickStateOperation);