1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativestategroup_p.h"
44 #include "qdeclarativetransition_p.h"
46 #include <private/qdeclarativebinding_p.h>
47 #include <private/qdeclarativeglobal_p.h>
49 #include <QtCore/qstringbuilder.h>
50 #include <QtCore/qstringlist.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 static void append_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, QDeclarativeTransition *state);
77 static int count_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list);
78 static QDeclarativeTransition *at_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, int index);
79 static void clear_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list);
81 QList<QDeclarativeState *> states;
82 QList<QDeclarativeTransition *> transitions;
84 bool componentComplete;
89 QDeclarativeTransition *findTransition(const QString &from, const QString &to);
90 void setCurrentStateInternal(const QString &state, bool = false);
91 bool updateAutoState();
95 \qmlclass StateGroup QDeclarativeStateGroup
96 \inqmlmodule QtQuick 2
97 \ingroup qml-state-elements
98 \brief The StateGroup element provides state support for non-Item elements.
100 Item (and all derived elements) provides built in support for states and transitions
101 via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to
102 use this support in other (non-Item-derived) elements.
112 transitions: Transition {
117 onSomethingHappened: myStateGroup.state = "state1";
121 \sa {qmlstate}{States} {QML Animation and Transitions}{Transitions}, {QtDeclarative}
124 QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent)
125 : QObject(*(new QDeclarativeStateGroupPrivate), parent)
129 QDeclarativeStateGroup::~QDeclarativeStateGroup()
131 Q_D(const QDeclarativeStateGroup);
132 for (int i = 0; i < d->states.count(); ++i)
133 d->states.at(i)->setStateGroup(0);
136 QList<QDeclarativeState *> QDeclarativeStateGroup::states() const
138 Q_D(const QDeclarativeStateGroup);
143 \qmlproperty list<State> QtQuick2::StateGroup::states
144 This property holds a list of states defined by the state group.
150 // State definition...
160 \sa {qmlstate}{States}
162 QDeclarativeListProperty<QDeclarativeState> QDeclarativeStateGroup::statesProperty()
164 Q_D(QDeclarativeStateGroup);
165 return QDeclarativeListProperty<QDeclarativeState>(this, &d->states, &QDeclarativeStateGroupPrivate::append_state,
166 &QDeclarativeStateGroupPrivate::count_state,
167 &QDeclarativeStateGroupPrivate::at_state,
168 &QDeclarativeStateGroupPrivate::clear_states);
171 void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state)
173 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
175 _this->d_func()->states.append(state);
176 state->setStateGroup(_this);
181 int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarativeState> *list)
183 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
184 return _this->d_func()->states.count();
187 QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index)
189 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
190 return _this->d_func()->states.at(index);
193 void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarativeState> *list)
195 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
196 _this->d_func()->setCurrentStateInternal(QString(), true);
197 for (int i = 0; i < _this->d_func()->states.count(); ++i) {
198 _this->d_func()->states.at(i)->setStateGroup(0);
200 _this->d_func()->states.clear();
204 \qmlproperty list<Transition> QtQuick2::StateGroup::transitions
205 This property holds a list of transitions defined by the state group.
221 \sa {QML Animation and Transitions}{Transitions}
223 QDeclarativeListProperty<QDeclarativeTransition> QDeclarativeStateGroup::transitionsProperty()
225 Q_D(QDeclarativeStateGroup);
226 return QDeclarativeListProperty<QDeclarativeTransition>(this, &d->transitions, &QDeclarativeStateGroupPrivate::append_transition,
227 &QDeclarativeStateGroupPrivate::count_transitions,
228 &QDeclarativeStateGroupPrivate::at_transition,
229 &QDeclarativeStateGroupPrivate::clear_transitions);
232 void QDeclarativeStateGroupPrivate::append_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, QDeclarativeTransition *trans)
234 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
236 _this->d_func()->transitions.append(trans);
239 int QDeclarativeStateGroupPrivate::count_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list)
241 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
242 return _this->d_func()->transitions.count();
245 QDeclarativeTransition *QDeclarativeStateGroupPrivate::at_transition(QDeclarativeListProperty<QDeclarativeTransition> *list, int index)
247 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
248 return _this->d_func()->transitions.at(index);
251 void QDeclarativeStateGroupPrivate::clear_transitions(QDeclarativeListProperty<QDeclarativeTransition> *list)
253 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object);
254 _this->d_func()->transitions.clear();
258 \qmlproperty string QtQuick2::StateGroup::state
260 This property holds the name of the current state of the state group.
262 This property is often used in scripts to change between states. For
267 if (button.state == 'On')
268 button.state = 'Off';
274 If the state group is in its base state (i.e. no explicit state has been
275 set), \c state will be a blank string. Likewise, you can return a
276 state group to its base state by setting its current state to \c ''.
278 \sa {qmlstates}{States}
280 QString QDeclarativeStateGroup::state() const
282 Q_D(const QDeclarativeStateGroup);
283 return d->currentState;
286 void QDeclarativeStateGroup::setState(const QString &state)
288 Q_D(QDeclarativeStateGroup);
289 if (d->currentState == state)
292 d->setCurrentStateInternal(state);
295 void QDeclarativeStateGroup::classBegin()
297 Q_D(QDeclarativeStateGroup);
298 d->componentComplete = false;
301 void QDeclarativeStateGroup::componentComplete()
303 Q_D(QDeclarativeStateGroup);
304 d->componentComplete = true;
306 for (int ii = 0; ii < d->states.count(); ++ii) {
307 QDeclarativeState *state = d->states.at(ii);
308 if (!state->isNamed())
309 state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount));
312 if (d->updateAutoState()) {
314 } else if (!d->currentState.isEmpty()) {
315 QString cs = d->currentState;
316 d->currentState.clear();
317 d->setCurrentStateInternal(cs, true);
322 Returns true if the state was changed, otherwise false.
324 bool QDeclarativeStateGroup::updateAutoState()
326 Q_D(QDeclarativeStateGroup);
327 return d->updateAutoState();
330 bool QDeclarativeStateGroupPrivate::updateAutoState()
332 Q_Q(QDeclarativeStateGroup);
333 if (!componentComplete)
337 for (int ii = 0; ii < states.count(); ++ii) {
338 QDeclarativeState *state = states.at(ii);
339 if (state->isWhenKnown()) {
340 if (state->isNamed()) {
341 if (state->when() && state->when()->evaluate().toBool()) {
342 if (stateChangeDebug())
343 qWarning() << "Setting auto state due to:"
344 << state->when()->expression();
345 if (currentState != state->name()) {
346 q->setState(state->name());
351 } else if (state->name() == currentState) {
358 bool rv = !currentState.isEmpty();
359 q->setState(QString());
366 QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to)
368 QDeclarativeTransition *highest = 0;
370 bool reversed = false;
373 for (int ii = 0; !done && ii < transitions.count(); ++ii) {
374 QDeclarativeTransition *t = transitions.at(ii);
377 for (int ii = 0; ii < 2; ++ii)
379 if (ii && (!t->reversible() ||
380 (t->fromState() == QLatin1String("*") &&
381 t->toState() == QLatin1String("*"))))
383 QStringList fromState;
386 fromState = t->fromState().split(QLatin1Char(','));
387 for (int jj = 0; jj < fromState.count(); ++jj)
388 fromState[jj] = fromState.at(jj).trimmed();
389 toState = t->toState().split(QLatin1Char(','));
390 for (int jj = 0; jj < toState.count(); ++jj)
391 toState[jj] = toState.at(jj).trimmed();
393 qSwap(fromState, toState);
395 if (fromState.contains(from))
397 else if (fromState.contains(QLatin1String("*")))
402 if (toState.contains(to))
404 else if (toState.contains(QLatin1String("*")))
418 } else if (tScore > score) {
426 highest->setReversed(reversed);
431 void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state,
434 Q_Q(QDeclarativeStateGroup);
435 if (!componentComplete) {
436 currentState = state;
441 qmlInfo(q) << "Can't apply a state change as part of a state definition.";
445 applyingState = true;
447 QDeclarativeTransition *transition = ignoreTrans ? 0 : findTransition(currentState, state);
448 if (stateChangeDebug()) {
449 qWarning() << this << "Changing state. From" << currentState << ". To" << state;
451 qWarning() << " using transition" << transition->fromState()
452 << transition->toState();
455 QDeclarativeState *oldState = 0;
456 if (!currentState.isEmpty()) {
457 for (int ii = 0; ii < states.count(); ++ii) {
458 if (states.at(ii)->name() == currentState) {
459 oldState = states.at(ii);
465 currentState = state;
466 emit q->stateChanged(currentState);
468 QDeclarativeState *newState = 0;
469 for (int ii = 0; ii < states.count(); ++ii) {
470 if (states.at(ii)->name() == currentState) {
471 newState = states.at(ii);
476 if (oldState == 0 || newState == 0) {
478 nullState = new QDeclarativeState;
479 QDeclarative_setParent_noEvent(nullState, q);
480 nullState->setStateGroup(q);
482 if (!oldState) oldState = nullState;
483 if (!newState) newState = nullState;
486 newState->apply(transition, oldState);
487 applyingState = false; //### consider removing this (don't allow state changes in transition)
490 QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const
492 Q_D(const QDeclarativeStateGroup);
493 for (int i = 0; i < d->states.count(); ++i) {
494 QDeclarativeState *state = d->states.at(i);
495 if (state->name() == name)
502 void QDeclarativeStateGroup::removeState(QDeclarativeState *state)
504 Q_D(QDeclarativeStateGroup);
505 d->states.removeOne(state);
508 void QDeclarativeStateGroup::stateAboutToComplete()
510 Q_D(QDeclarativeStateGroup);
511 d->applyingState = false;