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/qdeclarativestategroup_p.h"
44 #include "private/qdeclarativetransition_p.h"
45 #include "private/qdeclarativestate_p_p.h"
47 #include <qdeclarativebinding_p.h>
48 #include <qdeclarativeglobal_p.h>
50 #include <QtCore/qstringbuilder.h>
51 #include <QtCore/qdebug.h>
53 #include <private/qobject_p.h>
54 #include <qdeclarativeinfo.h>
58 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
60 class QDeclarativeStateGroupPrivate : public QObjectPrivate
62 Q_DECLARE_PUBLIC(QDeclarativeStateGroup)
64 QDeclarativeStateGroupPrivate()
65 : nullState(0), componentComplete(true),
66 ignoreTrans(false), applyingState(false), unnamedCount(0) {}
69 QDeclarativeState *nullState;
71 static void append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state);
72 static int count_state(QDeclarativeListProperty<QDeclarativeState> *list);
73 static QDeclarativeState *at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index);
74 static void clear_states(QDeclarativeListProperty<QDeclarativeState> *list);
76 QList<QDeclarativeState *> states;
77 QList<QDeclarativeTransition *> transitions;
79 bool componentComplete;
84 QDeclarativeTransition *findTransition(const QString &from, const QString &to);
85 void setCurrentStateInternal(const QString &state, bool = false);
86 bool updateAutoState();
90 \qmlclass StateGroup QDeclarativeStateGroup
91 \ingroup qml-state-elements
93 \brief The StateGroup element provides state support for non-Item elements.
95 Item (and all derived elements) provides built in support for states and transitions
96 via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to
97 use this support in other (non-Item-derived) elements.
107 transitions: Transition {
112 onSomethingHappened: myStateGroup.state = "state1";
116 \sa {qmlstate}{States} {QML Animation and Transitions}{Transitions}, {QtDeclarative}
119 QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent)
120 : QObject(*(new QDeclarativeStateGroupPrivate), parent)
124 QDeclarativeStateGroup::~QDeclarativeStateGroup()
126 Q_D(const QDeclarativeStateGroup);
127 for (int i = 0; i < d->states.count(); ++i)
128 d->states.at(i)->setStateGroup(0);
131 QList<QDeclarativeState *> QDeclarativeStateGroup::states() const
133 Q_D(const QDeclarativeStateGroup);
138 \qmlproperty list<State> StateGroup::states
139 This property holds a list of states defined by the state group.
145 // State definition...
155 \sa {qmlstate}{States}
157 QDeclarativeListProperty<QDeclarativeState> QDeclarativeStateGroup::statesProperty()
159 Q_D(QDeclarativeStateGroup);
160 return QDeclarativeListProperty<QDeclarativeState>(this, &d->states, &QDeclarativeStateGroupPrivate::append_state,
161 &QDeclarativeStateGroupPrivate::count_state,
162 &QDeclarativeStateGroupPrivate::at_state,
163 &QDeclarativeStateGroupPrivate::clear_states);
166 void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state)
168 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
170 _this->d_func()->states.append(state);
171 state->setStateGroup(_this);
176 int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarativeState> *list)
178 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
179 return _this->d_func()->states.count();
182 QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index)
184 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
185 return _this->d_func()->states.at(index);
188 void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarativeState> *list)
190 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
191 _this->d_func()->setCurrentStateInternal(QString(), true);
192 for (int i = 0; i < _this->d_func()->states.count(); ++i) {
193 _this->d_func()->states.at(i)->setStateGroup(0);
195 _this->d_func()->states.clear();
199 \qmlproperty list<Transition> StateGroup::transitions
200 This property holds a list of transitions defined by the state group.
216 \sa {QML Animation and Transitions}{Transitions}
218 QDeclarativeListProperty<QDeclarativeTransition> QDeclarativeStateGroup::transitionsProperty()
220 Q_D(QDeclarativeStateGroup);
221 return QDeclarativeListProperty<QDeclarativeTransition>(this, d->transitions);
225 \qmlproperty string StateGroup::state
227 This property holds the name of the current state of the state group.
229 This property is often used in scripts to change between states. For
234 if (button.state == 'On')
235 button.state = 'Off';
241 If the state group is in its base state (i.e. no explicit state has been
242 set), \c state will be a blank string. Likewise, you can return a
243 state group to its base state by setting its current state to \c ''.
245 \sa {qmlstates}{States}
247 QString QDeclarativeStateGroup::state() const
249 Q_D(const QDeclarativeStateGroup);
250 return d->currentState;
253 void QDeclarativeStateGroup::setState(const QString &state)
255 Q_D(QDeclarativeStateGroup);
256 if (d->currentState == state)
259 d->setCurrentStateInternal(state);
262 void QDeclarativeStateGroup::classBegin()
264 Q_D(QDeclarativeStateGroup);
265 d->componentComplete = false;
268 void QDeclarativeStateGroup::componentComplete()
270 Q_D(QDeclarativeStateGroup);
271 d->componentComplete = true;
273 for (int ii = 0; ii < d->states.count(); ++ii) {
274 QDeclarativeState *state = d->states.at(ii);
275 if (!state->isNamed())
276 state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount));
279 if (d->updateAutoState()) {
281 } else if (!d->currentState.isEmpty()) {
282 QString cs = d->currentState;
283 d->currentState.clear();
284 d->setCurrentStateInternal(cs, true);
289 Returns true if the state was changed, otherwise false.
291 bool QDeclarativeStateGroup::updateAutoState()
293 Q_D(QDeclarativeStateGroup);
294 return d->updateAutoState();
297 bool QDeclarativeStateGroupPrivate::updateAutoState()
299 Q_Q(QDeclarativeStateGroup);
300 if (!componentComplete)
304 for (int ii = 0; ii < states.count(); ++ii) {
305 QDeclarativeState *state = states.at(ii);
306 if (state->isWhenKnown()) {
307 if (state->isNamed()) {
308 if (state->when() && state->when()->evaluate().toBool()) {
309 if (stateChangeDebug())
310 qWarning() << "Setting auto state due to:"
311 << state->when()->expression();
312 if (currentState != state->name()) {
313 q->setState(state->name());
318 } else if (state->name() == currentState) {
325 bool rv = !currentState.isEmpty();
326 q->setState(QString());
333 QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to)
335 QDeclarativeTransition *highest = 0;
337 bool reversed = false;
340 for (int ii = 0; !done && ii < transitions.count(); ++ii) {
341 QDeclarativeTransition *t = transitions.at(ii);
342 for (int ii = 0; ii < 2; ++ii)
344 if (ii && (!t->reversible() ||
345 (t->fromState() == QLatin1String("*") &&
346 t->toState() == QLatin1String("*"))))
348 QStringList fromState;
351 fromState = t->fromState().split(QLatin1Char(','));
352 toState = t->toState().split(QLatin1Char(','));
354 qSwap(fromState, toState);
356 if (fromState.contains(from))
358 else if (fromState.contains(QLatin1String("*")))
363 if (toState.contains(to))
365 else if (toState.contains(QLatin1String("*")))
379 } else if (tScore > score) {
387 highest->setReversed(reversed);
392 void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state,
395 Q_Q(QDeclarativeStateGroup);
396 if (!componentComplete) {
397 currentState = state;
402 qmlInfo(q) << "Can't apply a state change as part of a state definition.";
406 applyingState = true;
408 QDeclarativeTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state);
409 if (stateChangeDebug()) {
410 qWarning() << this << "Changing state. From" << currentState << ". To" << state;
412 qWarning() << " using transition" << transition->fromState()
413 << transition->toState();
416 QDeclarativeState *oldState = 0;
417 if (!currentState.isEmpty()) {
418 for (int ii = 0; ii < states.count(); ++ii) {
419 if (states.at(ii)->name() == currentState) {
420 oldState = states.at(ii);
426 currentState = state;
427 emit q->stateChanged(currentState);
429 QDeclarativeState *newState = 0;
430 for (int ii = 0; ii < states.count(); ++ii) {
431 if (states.at(ii)->name() == currentState) {
432 newState = states.at(ii);
437 if (oldState == 0 || newState == 0) {
438 if (!nullState) { nullState = new QDeclarativeState; QDeclarative_setParent_noEvent(nullState, q); }
439 if (!oldState) oldState = nullState;
440 if (!newState) newState = nullState;
443 newState->apply(q, transition, oldState);
444 applyingState = false;
446 static_cast<QDeclarativeStatePrivate*>(QObjectPrivate::get(newState))->complete();
449 QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const
451 Q_D(const QDeclarativeStateGroup);
452 for (int i = 0; i < d->states.count(); ++i) {
453 QDeclarativeState *state = d->states.at(i);
454 if (state->name() == name)
461 void QDeclarativeStateGroup::removeState(QDeclarativeState *state)
463 Q_D(QDeclarativeStateGroup);
464 d->states.removeOne(state);