|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtDeclarative module of the Qt Toolkit. |
|
8 ** |
|
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 |
|
14 ** this package. |
|
15 ** |
|
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. |
|
23 ** |
|
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. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "private/qdeclarativestategroup_p.h" |
|
43 |
|
44 #include "private/qdeclarativetransition_p.h" |
|
45 #include "private/qdeclarativestate_p_p.h" |
|
46 |
|
47 #include <qdeclarativebinding_p.h> |
|
48 #include <qdeclarativeglobal_p.h> |
|
49 |
|
50 #include <QtCore/qstringbuilder.h> |
|
51 #include <QtCore/qdebug.h> |
|
52 |
|
53 #include <private/qobject_p.h> |
|
54 #include <qdeclarativeinfo.h> |
|
55 |
|
56 QT_BEGIN_NAMESPACE |
|
57 |
|
58 DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); |
|
59 |
|
60 class QDeclarativeStateGroupPrivate : public QObjectPrivate |
|
61 { |
|
62 Q_DECLARE_PUBLIC(QDeclarativeStateGroup) |
|
63 public: |
|
64 QDeclarativeStateGroupPrivate() |
|
65 : nullState(0), componentComplete(true), |
|
66 ignoreTrans(false), applyingState(false), unnamedCount(0) {} |
|
67 |
|
68 QString currentState; |
|
69 QDeclarativeState *nullState; |
|
70 |
|
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); |
|
75 |
|
76 QList<QDeclarativeState *> states; |
|
77 QList<QDeclarativeTransition *> transitions; |
|
78 |
|
79 bool componentComplete; |
|
80 bool ignoreTrans; |
|
81 bool applyingState; |
|
82 int unnamedCount; |
|
83 |
|
84 QDeclarativeTransition *findTransition(const QString &from, const QString &to); |
|
85 void setCurrentStateInternal(const QString &state, bool = false); |
|
86 bool updateAutoState(); |
|
87 }; |
|
88 |
|
89 /*! |
|
90 \qmlclass StateGroup QDeclarativeStateGroup |
|
91 \since 4.7 |
|
92 \brief The StateGroup element provides state support for non-Item elements. |
|
93 |
|
94 Item (and all dervied elements) provides built in support for states and transitions |
|
95 via its state, states and transitions properties. StateGroup provides an easy way to |
|
96 use this support in other (non-Item-derived) elements. |
|
97 |
|
98 \qml |
|
99 MyCustomObject { |
|
100 StateGroup { |
|
101 id: myStateGroup |
|
102 states: State { |
|
103 name: "state1" |
|
104 ... |
|
105 } |
|
106 transitions: Transition { |
|
107 ... |
|
108 } |
|
109 } |
|
110 |
|
111 onSomethingHappened: myStateGroup.state = "state1"; |
|
112 } |
|
113 \endqml |
|
114 |
|
115 \sa {qmlstate}{States} {state-transitions}{Transitions}, {QtDeclarative} |
|
116 */ |
|
117 |
|
118 QDeclarativeStateGroup::QDeclarativeStateGroup(QObject *parent) |
|
119 : QObject(*(new QDeclarativeStateGroupPrivate), parent) |
|
120 { |
|
121 } |
|
122 |
|
123 QDeclarativeStateGroup::~QDeclarativeStateGroup() |
|
124 { |
|
125 Q_D(const QDeclarativeStateGroup); |
|
126 for (int i = 0; i < d->states.count(); ++i) |
|
127 d->states.at(i)->setStateGroup(0); |
|
128 } |
|
129 |
|
130 QList<QDeclarativeState *> QDeclarativeStateGroup::states() const |
|
131 { |
|
132 Q_D(const QDeclarativeStateGroup); |
|
133 return d->states; |
|
134 } |
|
135 |
|
136 /*! |
|
137 \qmlproperty list<State> StateGroup::states |
|
138 This property holds a list of states defined by the state group. |
|
139 |
|
140 \qml |
|
141 StateGroup { |
|
142 states: [ |
|
143 State { ... }, |
|
144 State { ... } |
|
145 ... |
|
146 ] |
|
147 } |
|
148 \endqml |
|
149 |
|
150 \sa {qmlstate}{States} |
|
151 */ |
|
152 QDeclarativeListProperty<QDeclarativeState> QDeclarativeStateGroup::statesProperty() |
|
153 { |
|
154 Q_D(QDeclarativeStateGroup); |
|
155 return QDeclarativeListProperty<QDeclarativeState>(this, &d->states, &QDeclarativeStateGroupPrivate::append_state, |
|
156 &QDeclarativeStateGroupPrivate::count_state, |
|
157 &QDeclarativeStateGroupPrivate::at_state, |
|
158 &QDeclarativeStateGroupPrivate::clear_states); |
|
159 } |
|
160 |
|
161 void QDeclarativeStateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarativeState> *list, QDeclarativeState *state) |
|
162 { |
|
163 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); |
|
164 if (state) { |
|
165 _this->d_func()->states.append(state); |
|
166 state->setStateGroup(_this); |
|
167 } |
|
168 |
|
169 } |
|
170 |
|
171 int QDeclarativeStateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarativeState> *list) |
|
172 { |
|
173 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); |
|
174 return _this->d_func()->states.count(); |
|
175 } |
|
176 |
|
177 QDeclarativeState *QDeclarativeStateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarativeState> *list, int index) |
|
178 { |
|
179 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); |
|
180 return _this->d_func()->states.at(index); |
|
181 } |
|
182 |
|
183 void QDeclarativeStateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarativeState> *list) |
|
184 { |
|
185 QDeclarativeStateGroup *_this = static_cast<QDeclarativeStateGroup *>(list->object); |
|
186 _this->d_func()->setCurrentStateInternal(QString(), true); |
|
187 for (int i = 0; i < _this->d_func()->states.count(); ++i) { |
|
188 _this->d_func()->states.at(i)->setStateGroup(0); |
|
189 } |
|
190 _this->d_func()->states.clear(); |
|
191 } |
|
192 |
|
193 /*! |
|
194 \qmlproperty list<Transition> StateGroup::transitions |
|
195 This property holds a list of transitions defined by the state group. |
|
196 |
|
197 \qml |
|
198 StateGroup { |
|
199 transitions: [ |
|
200 Transition { ... }, |
|
201 Transition { ... } |
|
202 ... |
|
203 ] |
|
204 } |
|
205 \endqml |
|
206 |
|
207 \sa {state-transitions}{Transitions} |
|
208 */ |
|
209 QDeclarativeListProperty<QDeclarativeTransition> QDeclarativeStateGroup::transitionsProperty() |
|
210 { |
|
211 Q_D(QDeclarativeStateGroup); |
|
212 return QDeclarativeListProperty<QDeclarativeTransition>(this, d->transitions); |
|
213 } |
|
214 |
|
215 /*! |
|
216 \qmlproperty string StateGroup::state |
|
217 |
|
218 This property holds the name of the current state of the state group. |
|
219 |
|
220 This property is often used in scripts to change between states. For |
|
221 example: |
|
222 |
|
223 \qml |
|
224 function toggle() { |
|
225 if (button.state == 'On') |
|
226 button.state = 'Off'; |
|
227 else |
|
228 button.state = 'On'; |
|
229 } |
|
230 \endqml |
|
231 |
|
232 If the state group is in its base state (i.e. no explicit state has been |
|
233 set), \c state will be a blank string. Likewise, you can return a |
|
234 state group to its base state by setting its current state to \c ''. |
|
235 |
|
236 \sa {qmlstates}{States} |
|
237 */ |
|
238 QString QDeclarativeStateGroup::state() const |
|
239 { |
|
240 Q_D(const QDeclarativeStateGroup); |
|
241 return d->currentState; |
|
242 } |
|
243 |
|
244 void QDeclarativeStateGroup::setState(const QString &state) |
|
245 { |
|
246 Q_D(QDeclarativeStateGroup); |
|
247 if (d->currentState == state) |
|
248 return; |
|
249 |
|
250 d->setCurrentStateInternal(state); |
|
251 } |
|
252 |
|
253 void QDeclarativeStateGroup::classBegin() |
|
254 { |
|
255 Q_D(QDeclarativeStateGroup); |
|
256 d->componentComplete = false; |
|
257 } |
|
258 |
|
259 void QDeclarativeStateGroup::componentComplete() |
|
260 { |
|
261 Q_D(QDeclarativeStateGroup); |
|
262 d->componentComplete = true; |
|
263 |
|
264 for (int ii = 0; ii < d->states.count(); ++ii) { |
|
265 QDeclarativeState *state = d->states.at(ii); |
|
266 if (state->name().isEmpty()) |
|
267 state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount)); |
|
268 } |
|
269 |
|
270 if (d->updateAutoState()) { |
|
271 return; |
|
272 } else if (!d->currentState.isEmpty()) { |
|
273 QString cs = d->currentState; |
|
274 d->currentState = QString(); |
|
275 d->setCurrentStateInternal(cs, true); |
|
276 } |
|
277 } |
|
278 |
|
279 /*! |
|
280 Returns true if the state was changed, otherwise false. |
|
281 */ |
|
282 bool QDeclarativeStateGroup::updateAutoState() |
|
283 { |
|
284 Q_D(QDeclarativeStateGroup); |
|
285 return d->updateAutoState(); |
|
286 } |
|
287 |
|
288 bool QDeclarativeStateGroupPrivate::updateAutoState() |
|
289 { |
|
290 Q_Q(QDeclarativeStateGroup); |
|
291 if (!componentComplete) |
|
292 return false; |
|
293 |
|
294 bool revert = false; |
|
295 for (int ii = 0; ii < states.count(); ++ii) { |
|
296 QDeclarativeState *state = states.at(ii); |
|
297 if (state->isWhenKnown()) { |
|
298 if (!state->name().isEmpty()) { |
|
299 if (state->when() && state->when()->evaluate().toBool()) { |
|
300 if (stateChangeDebug()) |
|
301 qWarning() << "Setting auto state due to:" |
|
302 << state->when()->expression(); |
|
303 if (currentState != state->name()) { |
|
304 q->setState(state->name()); |
|
305 return true; |
|
306 } else { |
|
307 return false; |
|
308 } |
|
309 } else if (state->name() == currentState) { |
|
310 revert = true; |
|
311 } |
|
312 } |
|
313 } |
|
314 } |
|
315 if (revert) { |
|
316 bool rv = currentState != QString(); |
|
317 q->setState(QString()); |
|
318 return rv; |
|
319 } else { |
|
320 return false; |
|
321 } |
|
322 } |
|
323 |
|
324 QDeclarativeTransition *QDeclarativeStateGroupPrivate::findTransition(const QString &from, const QString &to) |
|
325 { |
|
326 QDeclarativeTransition *highest = 0; |
|
327 int score = 0; |
|
328 bool reversed = false; |
|
329 bool done = false; |
|
330 |
|
331 for (int ii = 0; !done && ii < transitions.count(); ++ii) { |
|
332 QDeclarativeTransition *t = transitions.at(ii); |
|
333 for (int ii = 0; ii < 2; ++ii) |
|
334 { |
|
335 if (ii && (!t->reversible() || |
|
336 (t->fromState() == QLatin1String("*") && |
|
337 t->toState() == QLatin1String("*")))) |
|
338 break; |
|
339 QStringList fromState; |
|
340 QStringList toState; |
|
341 |
|
342 fromState = t->fromState().split(QLatin1Char(',')); |
|
343 toState = t->toState().split(QLatin1Char(',')); |
|
344 if (ii == 1) |
|
345 qSwap(fromState, toState); |
|
346 int tScore = 0; |
|
347 if (fromState.contains(from)) |
|
348 tScore += 2; |
|
349 else if (fromState.contains(QLatin1String("*"))) |
|
350 tScore += 1; |
|
351 else |
|
352 continue; |
|
353 |
|
354 if (toState.contains(to)) |
|
355 tScore += 2; |
|
356 else if (toState.contains(QLatin1String("*"))) |
|
357 tScore += 1; |
|
358 else |
|
359 continue; |
|
360 |
|
361 if (ii == 1) |
|
362 reversed = true; |
|
363 else |
|
364 reversed = false; |
|
365 |
|
366 if (tScore == 4) { |
|
367 highest = t; |
|
368 done = true; |
|
369 break; |
|
370 } else if (tScore > score) { |
|
371 score = tScore; |
|
372 highest = t; |
|
373 } |
|
374 } |
|
375 } |
|
376 |
|
377 if (highest) |
|
378 highest->setReversed(reversed); |
|
379 |
|
380 return highest; |
|
381 } |
|
382 |
|
383 void QDeclarativeStateGroupPrivate::setCurrentStateInternal(const QString &state, |
|
384 bool ignoreTrans) |
|
385 { |
|
386 Q_Q(QDeclarativeStateGroup); |
|
387 if (!componentComplete) { |
|
388 currentState = state; |
|
389 return; |
|
390 } |
|
391 |
|
392 if (applyingState) { |
|
393 qmlInfo(q) << "Can't apply a state change as part of a state definition."; |
|
394 return; |
|
395 } |
|
396 |
|
397 applyingState = true; |
|
398 |
|
399 QDeclarativeTransition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state); |
|
400 if (stateChangeDebug()) { |
|
401 qWarning() << this << "Changing state. From" << currentState << ". To" << state; |
|
402 if (transition) |
|
403 qWarning() << " using transition" << transition->fromState() |
|
404 << transition->toState(); |
|
405 } |
|
406 |
|
407 QDeclarativeState *oldState = 0; |
|
408 if (!currentState.isEmpty()) { |
|
409 for (int ii = 0; ii < states.count(); ++ii) { |
|
410 if (states.at(ii)->name() == currentState) { |
|
411 oldState = states.at(ii); |
|
412 break; |
|
413 } |
|
414 } |
|
415 } |
|
416 |
|
417 currentState = state; |
|
418 emit q->stateChanged(currentState); |
|
419 |
|
420 QDeclarativeState *newState = 0; |
|
421 for (int ii = 0; ii < states.count(); ++ii) { |
|
422 if (states.at(ii)->name() == currentState) { |
|
423 newState = states.at(ii); |
|
424 break; |
|
425 } |
|
426 } |
|
427 |
|
428 if (oldState == 0 || newState == 0) { |
|
429 if (!nullState) { nullState = new QDeclarativeState; QDeclarative_setParent_noEvent(nullState, q); } |
|
430 if (!oldState) oldState = nullState; |
|
431 if (!newState) newState = nullState; |
|
432 } |
|
433 |
|
434 newState->apply(q, transition, oldState); |
|
435 applyingState = false; |
|
436 if (!transition) |
|
437 static_cast<QDeclarativeStatePrivate*>(QObjectPrivate::get(newState))->complete(); |
|
438 } |
|
439 |
|
440 QDeclarativeState *QDeclarativeStateGroup::findState(const QString &name) const |
|
441 { |
|
442 Q_D(const QDeclarativeStateGroup); |
|
443 for (int i = 0; i < d->states.count(); ++i) { |
|
444 QDeclarativeState *state = d->states.at(i); |
|
445 if (state->name() == name) |
|
446 return state; |
|
447 } |
|
448 |
|
449 return 0; |
|
450 } |
|
451 |
|
452 void QDeclarativeStateGroup::removeState(QDeclarativeState *state) |
|
453 { |
|
454 Q_D(QDeclarativeStateGroup); |
|
455 d->states.removeOne(state); |
|
456 } |
|
457 |
|
458 QT_END_NAMESPACE |
|
459 |
|
460 |