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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativestate_p_p.h"
43 #include "private/qdeclarativestate_p.h"
45 #include "private/qdeclarativetransition_p.h"
46 #include "private/qdeclarativestategroup_p.h"
47 #include "private/qdeclarativestateoperations_p.h"
48 #include "private/qdeclarativeanimation_p.h"
49 #include "private/qdeclarativeanimation_p_p.h"
51 #include <qdeclarativebinding_p.h>
52 #include <qdeclarativeglobal_p.h>
54 #include <QtCore/qdebug.h>
58 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
60 QDeclarativeAction::QDeclarativeAction()
61 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0),
66 QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName,
67 const QVariant &value)
68 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
69 property(target, propertyName, qmlEngine(target)), toValue(value),
70 fromBinding(0), event(0),
71 specifiedObject(target), specifiedProperty(propertyName)
73 if (property.isValid())
74 fromValue = property.read();
77 QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName,
78 QDeclarativeContext *context, const QVariant &value)
79 : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
80 property(target, propertyName, context), toValue(value),
81 fromBinding(0), event(0),
82 specifiedObject(target), specifiedProperty(propertyName)
84 if (property.isValid())
85 fromValue = property.read();
89 QDeclarativeActionEvent::~QDeclarativeActionEvent()
93 QString QDeclarativeActionEvent::typeName() const
98 void QDeclarativeActionEvent::execute(Reason)
102 bool QDeclarativeActionEvent::isReversable()
107 void QDeclarativeActionEvent::reverse(Reason)
111 bool QDeclarativeActionEvent::changesBindings()
116 void QDeclarativeActionEvent::clearBindings()
120 bool QDeclarativeActionEvent::override(QDeclarativeActionEvent *other)
126 QDeclarativeStateOperation::QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent)
127 : QObject(dd, parent)
132 \qmlclass State QDeclarativeState
133 \ingroup qml-state-elements
135 \brief The State element defines configurations of objects and properties.
137 A \e state is a set of batched changes from the default configuration.
139 All items have a default state that defines the default configuration of objects
140 and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to
141 allow items to switch between different configurations. These configurations
142 can, for example, be used to apply different sets of property values or execute
145 The following example displays a single \l Rectangle. In the default state, the rectangle
146 is colored black. In the "clicked" state, a PropertyChanges element changes the
147 rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state
148 between the default state and the "clicked" state, thus toggling the color of the
149 rectangle between black and red.
151 \snippet doc/src/snippets/declarative/state.qml 0
153 Notice the default state is referred to using an empty string ("").
155 States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide
156 animations when state changes occur.
158 \note Setting the state of an object from within another state of the same object is
161 \sa {declarative/animation/states}{states example}, {qmlstates}{States},
162 {QML Animation and Transitions}{Transitions}, QtDeclarative
164 QDeclarativeState::QDeclarativeState(QObject *parent)
165 : QObject(*(new QDeclarativeStatePrivate), parent)
167 Q_D(QDeclarativeState);
168 d->transitionManager.setState(this);
171 QDeclarativeState::~QDeclarativeState()
173 Q_D(QDeclarativeState);
175 d->group->removeState(this);
179 \qmlproperty string State::name
180 This property holds the name of the state.
182 Each state should have a unique name within its item.
184 QString QDeclarativeState::name() const
186 Q_D(const QDeclarativeState);
190 void QDeclarativeState::setName(const QString &n)
192 Q_D(QDeclarativeState);
197 bool QDeclarativeState::isNamed() const
199 Q_D(const QDeclarativeState);
203 bool QDeclarativeState::isWhenKnown() const
205 Q_D(const QDeclarativeState);
210 \qmlproperty bool State::when
211 This property holds when the state should be applied.
213 This should be set to an expression that evaluates to \c true when you want the state to
214 be applied. For example, the following \l Rectangle changes in and out of the "hidden"
215 state when the \l MouseArea is pressed:
217 \snippet doc/src/snippets/declarative/state-when.qml 0
219 If multiple states in a group have \c when clauses that evaluate to \c true
220 at the same time, the first matching state will be applied. For example, in
221 the following snippet \c state1 will always be selected rather than
222 \c state2 when sharedCondition becomes \c true.
226 State { name: "state1"; when: sharedCondition },
227 State { name: "state2"; when: sharedCondition }
233 QDeclarativeBinding *QDeclarativeState::when() const
235 Q_D(const QDeclarativeState);
239 void QDeclarativeState::setWhen(QDeclarativeBinding *when)
241 Q_D(QDeclarativeState);
244 d->group->updateAutoState();
248 \qmlproperty string State::extend
249 This property holds the state that this state extends.
251 When a state extends another state, it inherits all the changes of that state.
253 The state being extended is treated as the base state in regards to
254 the changes specified by the extending state.
256 QString QDeclarativeState::extends() const
258 Q_D(const QDeclarativeState);
262 void QDeclarativeState::setExtends(const QString &extends)
264 Q_D(QDeclarativeState);
265 d->extends = extends;
269 \qmlproperty list<Change> State::changes
270 This property holds the changes to apply for this state
273 By default these changes are applied against the default state. If the state
274 extends another state, then the changes are applied against the state being
277 QDeclarativeListProperty<QDeclarativeStateOperation> QDeclarativeState::changes()
279 Q_D(QDeclarativeState);
280 return QDeclarativeListProperty<QDeclarativeStateOperation>(this, &d->operations, QDeclarativeStatePrivate::operations_append,
281 QDeclarativeStatePrivate::operations_count, QDeclarativeStatePrivate::operations_at,
282 QDeclarativeStatePrivate::operations_clear);
285 int QDeclarativeState::operationCount() const
287 Q_D(const QDeclarativeState);
288 return d->operations.count();
291 QDeclarativeStateOperation *QDeclarativeState::operationAt(int index) const
293 Q_D(const QDeclarativeState);
294 return d->operations.at(index);
297 QDeclarativeState &QDeclarativeState::operator<<(QDeclarativeStateOperation *op)
299 Q_D(QDeclarativeState);
300 d->operations.append(QDeclarativeStatePrivate::OperationGuard(op, &d->operations));
304 void QDeclarativeStatePrivate::complete()
306 Q_Q(QDeclarativeState);
308 for (int ii = 0; ii < reverting.count(); ++ii) {
309 for (int jj = 0; jj < revertList.count(); ++jj) {
310 if (revertList.at(jj).property() == reverting.at(ii)) {
311 revertList.removeAt(jj);
321 // Generate a list of actions for this state. This includes coelescing state
322 // actions that this state "extends"
323 QDeclarativeStateOperation::ActionList
324 QDeclarativeStatePrivate::generateActionList(QDeclarativeStateGroup *group) const
326 QDeclarativeStateOperation::ActionList applyList;
330 // Prevent "extends" recursion
333 if (!extends.isEmpty()) {
334 QList<QDeclarativeState *> states = group->states();
335 for (int ii = 0; ii < states.count(); ++ii)
336 if (states.at(ii)->name() == extends) {
337 qmlExecuteDeferred(states.at(ii));
338 applyList = static_cast<QDeclarativeStatePrivate*>(states.at(ii)->d_func())->generateActionList(group);
342 foreach(QDeclarativeStateOperation *op, operations)
343 applyList << op->actions();
349 QDeclarativeStateGroup *QDeclarativeState::stateGroup() const
351 Q_D(const QDeclarativeState);
355 void QDeclarativeState::setStateGroup(QDeclarativeStateGroup *group)
357 Q_D(QDeclarativeState);
361 void QDeclarativeState::cancel()
363 Q_D(QDeclarativeState);
364 d->transitionManager.cancel();
367 void QDeclarativeAction::deleteFromBinding()
370 QDeclarativePropertyPrivate::setBinding(property, 0);
371 fromBinding->destroy();
376 bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QString &name) const
378 Q_D(const QDeclarativeState);
380 if (isStateActive()) {
381 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
383 while (revertListIterator.hasNext()) {
384 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
385 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
393 bool QDeclarativeState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue)
395 Q_D(QDeclarativeState);
397 if (isStateActive()) {
398 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
400 while (revertListIterator.hasNext()) {
401 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
402 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
403 simpleAction.setValue(revertValue);
412 bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding)
414 Q_D(QDeclarativeState);
416 if (isStateActive()) {
417 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
419 while (revertListIterator.hasNext()) {
420 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
421 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
422 if (simpleAction.binding())
423 simpleAction.binding()->destroy();
425 simpleAction.setBinding(binding);
434 bool QDeclarativeState::removeEntryFromRevertList(QObject *target, const QString &name)
436 Q_D(QDeclarativeState);
438 if (isStateActive()) {
439 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
441 while (revertListIterator.hasNext()) {
442 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
443 if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
444 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
446 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
447 oldBinding->destroy();
450 simpleAction.property().write(simpleAction.value());
451 if (simpleAction.binding())
452 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
454 revertListIterator.remove();
463 void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action)
465 Q_D(QDeclarativeState);
467 QDeclarativeSimpleAction simpleAction(action);
469 d->revertList.append(simpleAction);
472 void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target)
474 Q_D(QDeclarativeState);
476 if (isStateActive()) {
477 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
479 while (revertListIterator.hasNext()) {
480 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
481 if (simpleAction.property().object() == target) {
482 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
484 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
485 oldBinding->destroy();
488 simpleAction.property().write(simpleAction.value());
489 if (simpleAction.binding())
490 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
492 revertListIterator.remove();
498 void QDeclarativeState::addEntriesToRevertList(const QList<QDeclarativeAction> &actionList)
500 Q_D(QDeclarativeState);
501 if (isStateActive()) {
502 QList<QDeclarativeSimpleAction> simpleActionList;
504 QListIterator<QDeclarativeAction> actionListIterator(actionList);
505 while(actionListIterator.hasNext()) {
506 const QDeclarativeAction &action = actionListIterator.next();
507 QDeclarativeSimpleAction simpleAction(action);
508 action.property.write(action.toValue);
509 if (!action.toBinding.isNull()) {
510 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
512 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
513 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding);
516 simpleActionList.append(simpleAction);
519 d->revertList.append(simpleActionList);
523 QVariant QDeclarativeState::valueInRevertList(QObject *target, const QString &name) const
525 Q_D(const QDeclarativeState);
527 if (isStateActive()) {
528 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
530 while (revertListIterator.hasNext()) {
531 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
532 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
533 return simpleAction.value();
540 QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QString &name) const
542 Q_D(const QDeclarativeState);
544 if (isStateActive()) {
545 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
547 while (revertListIterator.hasNext()) {
548 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
549 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
550 return simpleAction.binding();
557 bool QDeclarativeState::isStateActive() const
559 return stateGroup() && stateGroup()->state() == name();
562 void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransition *trans, QDeclarativeState *revert)
564 Q_D(QDeclarativeState);
566 qmlExecuteDeferred(this);
571 d->revertList.clear();
572 d->reverting.clear();
575 QDeclarativeStatePrivate *revertPrivate =
576 static_cast<QDeclarativeStatePrivate*>(revert->d_func());
577 d->revertList = revertPrivate->revertList;
578 revertPrivate->revertList.clear();
581 // List of actions caused by this state
582 QDeclarativeStateOperation::ActionList applyList = d->generateActionList(group);
584 // List of actions that need to be reverted to roll back (just) this state
585 QDeclarativeStatePrivate::SimpleActionList additionalReverts;
586 // First add the reverse of all the applyList actions
587 for (int ii = 0; ii < applyList.count(); ++ii) {
588 QDeclarativeAction &action = applyList[ii];
591 if (!action.event->isReversable())
594 for (int jj = 0; jj < d->revertList.count(); ++jj) {
595 QDeclarativeActionEvent *event = d->revertList.at(jj).event();
596 if (event && event->typeName() == action.event->typeName()) {
597 if (action.event->override(event)) {
600 if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) {
601 action.event->copyOriginals(d->revertList.at(jj).event());
603 QDeclarativeSimpleAction r(action);
604 additionalReverts << r;
605 d->revertList.removeAt(jj);
607 } else if (action.event->isRewindable()) //###why needed?
608 action.event->saveCurrentValues();
615 action.event->saveOriginals();
616 // Only need to revert the applyList action if the previous
617 // state doesn't have a higher priority revert already
618 QDeclarativeSimpleAction r(action);
619 additionalReverts << r;
623 action.fromBinding = QDeclarativePropertyPrivate::binding(action.property);
625 for (int jj = 0; jj < d->revertList.count(); ++jj) {
626 if (d->revertList.at(jj).property() == action.property) {
628 if (d->revertList.at(jj).binding() != action.fromBinding) {
629 action.deleteFromBinding();
636 if (!action.restore) {
637 action.deleteFromBinding();;
639 // Only need to revert the applyList action if the previous
640 // state doesn't have a higher priority revert already
641 QDeclarativeSimpleAction r(action);
642 additionalReverts << r;
648 // Any reverts from a previous state that aren't carried forth
649 // into this state need to be translated into apply actions
650 for (int ii = 0; ii < d->revertList.count(); ++ii) {
652 if (d->revertList.at(ii).event()) {
653 QDeclarativeActionEvent *event = d->revertList.at(ii).event();
654 if (!event->isReversable())
656 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
657 const QDeclarativeAction &action = applyList.at(jj);
658 if (action.event && action.event->typeName() == event->typeName()) {
659 if (action.event->override(event))
664 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
665 const QDeclarativeAction &action = applyList.at(jj);
666 if (action.property == d->revertList.at(ii).property())
671 QVariant cur = d->revertList.at(ii).property().read();
672 QDeclarativeAbstractBinding *delBinding =
673 QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
675 delBinding->destroy();
677 QDeclarativeAction a;
678 a.property = d->revertList.at(ii).property();
680 a.toValue = d->revertList.at(ii).value();
681 a.toBinding = QDeclarativeAbstractBinding::getPointer(d->revertList.at(ii).binding());
682 a.specifiedObject = d->revertList.at(ii).specifiedObject();
683 a.specifiedProperty = d->revertList.at(ii).specifiedProperty();
684 a.event = d->revertList.at(ii).event();
685 a.reverseEvent = d->revertList.at(ii).reverseEvent();
686 if (a.event && a.event->isRewindable())
687 a.event->saveCurrentValues();
689 // Store these special reverts in the reverting list
690 d->reverting << d->revertList.at(ii).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 QDeclarativeAction &action, applyList) {
701 qWarning() << " QDeclarativeAction event:" << action.event->typeName();
703 qWarning() << " QDeclarativeAction:" << action.property.object()
704 << action.property.name() << "From:" << action.fromValue
705 << "To:" << action.toValue;
710 d->transitionManager.transition(applyList, trans);
713 QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions()
718 QDeclarativeState *QDeclarativeStateOperation::state() const
720 Q_D(const QDeclarativeStateOperation);
724 void QDeclarativeStateOperation::setState(QDeclarativeState *state)
726 Q_D(QDeclarativeStateOperation);