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 "qquicktransitionmanager_p_p.h"
44 #include "qquicktransition_p.h"
45 #include "qquickstate_p_p.h"
47 #include <private/qqmlbinding_p.h>
48 #include <private/qqmlglobal_p.h>
49 #include <private/qqmlproperty_p.h>
51 #include <QtCore/qdebug.h>
55 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
57 class QQuickTransitionManagerPrivate
60 QQuickTransitionManagerPrivate()
61 : state(0), transitionInstance(0) {}
64 typedef QList<QQuickSimpleAction> SimpleActionList;
66 QQuickTransitionInstance *transitionInstance;
67 QQuickStateOperation::ActionList bindingsList;
68 SimpleActionList completeList;
71 QQuickTransitionManager::QQuickTransitionManager()
72 : d(new QQuickTransitionManagerPrivate)
76 void QQuickTransitionManager::setState(QQuickState *s)
81 QQuickTransitionManager::~QQuickTransitionManager()
83 delete d->transitionInstance;
87 bool QQuickTransitionManager::isRunning() const
89 return d->transitionInstance && d->transitionInstance->isRunning();
92 void QQuickTransitionManager::complete()
96 for (int ii = 0; ii < d->completeList.count(); ++ii) {
97 const QQmlProperty &prop = d->completeList.at(ii).property();
98 prop.write(d->completeList.at(ii).value());
101 d->completeList.clear();
104 static_cast<QQuickStatePrivate*>(QObjectPrivate::get(d->state))->complete();
109 void QQuickTransitionManagerPrivate::applyBindings()
111 foreach(const QQuickAction &action, bindingsList) {
112 if (!action.toBinding.isNull()) {
113 QQmlPropertyPrivate::setBinding(action.property, action.toBinding.data());
114 } else if (action.event) {
115 if (action.reverseEvent)
116 action.event->reverse();
118 action.event->execute();
123 bindingsList.clear();
126 void QQuickTransitionManager::finished()
130 void QQuickTransitionManager::transition(const QList<QQuickAction> &list,
131 QQuickTransition *transition,
132 QObject *defaultTarget)
136 QQuickStateOperation::ActionList applyList = list;
137 // Determine which actions are binding changes and disable any current bindings
138 foreach(const QQuickAction &action, applyList) {
139 if (action.toBinding)
140 d->bindingsList << action;
141 if (action.fromBinding)
142 QQmlPropertyPrivate::setBinding(action.property, 0); // Disable current binding
143 if (action.event && action.event->changesBindings()) { //### assume isReversable()?
144 d->bindingsList << action;
145 action.event->clearBindings();
149 // Animated transitions need both the start and the end value for
150 // each property change. In the presence of bindings, the end values
151 // are non-trivial to calculate. As a "best effort" attempt, we first
152 // apply all the property and binding changes, then read all the actual
153 // final values, then roll back the changes and proceed as normal.
155 // This doesn't catch everything, and it might be a little fragile in
156 // some cases - but whatcha going to do?
158 // Note that we only fast forward if both a transition and bindings are
159 // present, as it is unneccessary (and potentially expensive) otherwise.
161 if (transition && !d->bindingsList.isEmpty()) {
163 // Apply all the property and binding changes
164 for (int ii = 0; ii < applyList.size(); ++ii) {
165 const QQuickAction &action = applyList.at(ii);
166 if (!action.toBinding.isNull()) {
167 QQmlPropertyPrivate::setBinding(action.property, action.toBinding.data(), QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
168 } else if (!action.event) {
169 QQmlPropertyPrivate::write(action.property, action.toValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
170 } else if (action.event->isReversable()) {
171 if (action.reverseEvent)
172 action.event->reverse(QQuickActionEvent::FastForward);
174 action.event->execute(QQuickActionEvent::FastForward);
178 // Read all the end values for binding changes
179 for (int ii = 0; ii < applyList.size(); ++ii) {
180 QQuickAction *action = &applyList[ii];
182 action->event->saveTargetValues();
185 const QQmlProperty &prop = action->property;
186 if (!action->toBinding.isNull() || !action->toValue.isValid()) {
187 action->toValue = prop.read();
191 // Revert back to the original values
192 foreach(const QQuickAction &action, applyList) {
194 if (action.event->isReversable()) {
195 action.event->clearBindings();
196 action.event->rewind();
197 action.event->clearBindings(); //### shouldn't be needed
202 if (action.toBinding)
203 QQmlPropertyPrivate::setBinding(action.property, 0); // Make sure this is disabled during the transition
205 QQmlPropertyPrivate::write(action.property, action.fromValue, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding);
210 QList<QQmlProperty> touched;
211 QQuickTransitionInstance *oldInstance = d->transitionInstance;
212 d->transitionInstance = transition->prepare(applyList, touched, this, defaultTarget);
213 d->transitionInstance->start();
214 if (oldInstance && oldInstance != d->transitionInstance)
217 // Modify the action list to remove actions handled in the transition
218 for (int ii = 0; ii < applyList.count(); ++ii) {
219 const QQuickAction &action = applyList.at(ii);
223 if (action.actionDone) {
224 applyList.removeAt(ii);
230 if (touched.contains(action.property)) {
231 if (action.toValue != action.fromValue)
233 QQuickSimpleAction(action, QQuickSimpleAction::EndState);
235 applyList.removeAt(ii);
243 // Any actions remaining have not been handled by the transition and should
244 // be applied immediately. We skip applying bindings, as they are all
245 // applied at the end in applyBindings() to avoid any nastiness mid
247 foreach(const QQuickAction &action, applyList) {
248 if (action.event && !action.event->changesBindings()) {
249 if (action.event->isReversable() && action.reverseEvent)
250 action.event->reverse();
252 action.event->execute();
253 } else if (!action.event && !action.toBinding) {
254 action.property.write(action.toValue);
257 #ifndef QT_NO_DEBUG_STREAM
258 if (stateChangeDebug()) {
259 foreach(const QQuickAction &action, applyList) {
261 qWarning() << " No transition for event:" << action.event->type();
263 qWarning() << " No transition for:" << action.property.object()
264 << action.property.name() << "From:" << action.fromValue
265 << "To:" << action.toValue;
273 void QQuickTransitionManager::cancel()
275 if (d->transitionInstance && d->transitionInstance->isRunning())
276 d->transitionInstance->stop();
278 for(int i = 0; i < d->bindingsList.count(); ++i) {
279 QQuickAction action = d->bindingsList[i];
280 if (!action.toBinding.isNull() && action.deletableToBinding) {
281 QQmlPropertyPrivate::setBinding(action.property, 0);
282 action.toBinding.data()->destroy();
283 action.toBinding.clear();
284 action.deletableToBinding = false;
285 } else if (action.event) {
286 //### what do we do here?
290 d->bindingsList.clear();
291 d->completeList.clear();