1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qabstracttransition.h" |
5 | #include "qabstracttransition_p.h" |
6 | #include "qabstractstate.h" |
7 | #include "qhistorystate.h" |
8 | #include "qstate.h" |
9 | #include "qstatemachine.h" |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | /*! |
14 | \class QAbstractTransition |
15 | \inmodule QtStateMachine |
16 | |
17 | \brief The QAbstractTransition class is the base class of transitions between QAbstractState objects. |
18 | |
19 | \since 4.6 |
20 | \ingroup statemachine |
21 | |
22 | The QAbstractTransition class is the abstract base class of transitions |
23 | between states (QAbstractState objects) of a |
24 | QStateMachine. QAbstractTransition is part of \l{Qt State Machine Overview} |
25 | {Qt State Machine Framework}. |
26 | |
27 | The sourceState() function returns the source of the transition. The |
28 | targetStates() function returns the targets of the transition. The machine() |
29 | function returns the state machine that the transition is part of. |
30 | |
31 | The triggered() signal is emitted when the transition has been triggered. |
32 | |
33 | Transitions can cause animations to be played. Use the addAnimation() |
34 | function to add an animation to the transition. |
35 | |
36 | \section1 Subclassing |
37 | |
38 | The eventTest() function is called by the state machine to determine whether |
39 | an event should trigger the transition. In your reimplementation you |
40 | typically check the event type and cast the event object to the proper type, |
41 | and check that one or more properties of the event meet your criteria. |
42 | |
43 | The onTransition() function is called when the transition is triggered; |
44 | reimplement this function to perform custom processing for the transition. |
45 | */ |
46 | |
47 | /*! |
48 | \property QAbstractTransition::sourceState |
49 | |
50 | \brief the source state (parent) of this transition |
51 | */ |
52 | |
53 | /*! |
54 | \property QAbstractTransition::targetState |
55 | |
56 | \brief the target state of this transition |
57 | |
58 | If a transition has no target state, the transition may still be |
59 | triggered, but this will not cause the state machine's configuration to |
60 | change (i.e. the current state will not be exited and re-entered). |
61 | */ |
62 | |
63 | /*! |
64 | \property QAbstractTransition::targetStates |
65 | |
66 | \brief the target states of this transition |
67 | |
68 | If multiple states are specified, all must be descendants of the same |
69 | parallel group state. |
70 | */ |
71 | |
72 | /*! |
73 | \property QAbstractTransition::transitionType |
74 | |
75 | \brief indicates whether this transition is an internal transition, or an external transition. |
76 | |
77 | Internal and external transitions behave the same, except for the case of a transition whose |
78 | source state is a compound state and whose target(s) is a descendant of the source. In such a |
79 | case, an internal transition will not exit and re-enter its source state, while an external one |
80 | will. |
81 | |
82 | By default, the type is an external transition. |
83 | */ |
84 | |
85 | /*! |
86 | \enum QAbstractTransition::TransitionType |
87 | |
88 | This enum specifies the kind of transition. By default, the type is an external transition. |
89 | |
90 | \value ExternalTransition Any state that is the source state of a transition (which is not a |
91 | target-less transition) is left, and re-entered when necessary. |
92 | \value InternalTransition If the target state of a transition is a sub-state of a compound state, |
93 | and that compound state is the source state, an internal transition will |
94 | not leave the source state. |
95 | |
96 | \sa QAbstractTransition::transitionType |
97 | */ |
98 | |
99 | QStateMachine *QAbstractTransitionPrivate::machine() const |
100 | { |
101 | if (QState *source = sourceState()) |
102 | return source->machine(); |
103 | Q_Q(const QAbstractTransition); |
104 | if (QHistoryState *parent = qobject_cast<QHistoryState *>(object: q->parent())) |
105 | return parent->machine(); |
106 | return nullptr; |
107 | } |
108 | |
109 | bool QAbstractTransitionPrivate::callEventTest(QEvent *e) |
110 | { |
111 | Q_Q(QAbstractTransition); |
112 | return q->eventTest(event: e); |
113 | } |
114 | |
115 | void QAbstractTransitionPrivate::callOnTransition(QEvent *e) |
116 | { |
117 | Q_Q(QAbstractTransition); |
118 | q->onTransition(event: e); |
119 | } |
120 | |
121 | QState *QAbstractTransitionPrivate::sourceState() const |
122 | { |
123 | return qobject_cast<QState*>(object: parent); |
124 | } |
125 | |
126 | void QAbstractTransitionPrivate::emitTriggered() |
127 | { |
128 | Q_Q(QAbstractTransition); |
129 | emit q->triggered(QAbstractTransition::QPrivateSignal()); |
130 | } |
131 | |
132 | /*! |
133 | Constructs a new QAbstractTransition object with the given \a sourceState. |
134 | */ |
135 | QAbstractTransition::QAbstractTransition(QState *sourceState) |
136 | : QObject(*new QAbstractTransitionPrivate, sourceState) |
137 | { |
138 | } |
139 | |
140 | /*! |
141 | \internal |
142 | */ |
143 | QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, |
144 | QState *parent) |
145 | : QObject(dd, parent) |
146 | { |
147 | } |
148 | |
149 | /*! |
150 | Destroys this transition. |
151 | */ |
152 | QAbstractTransition::~QAbstractTransition() |
153 | { |
154 | } |
155 | |
156 | /*! |
157 | Returns the source state of this transition, or \nullptr if this |
158 | transition has no source state. |
159 | */ |
160 | QState *QAbstractTransition::sourceState() const |
161 | { |
162 | Q_D(const QAbstractTransition); |
163 | return d->sourceState(); |
164 | } |
165 | |
166 | /*! |
167 | Returns the target state of this transition, or \nullptr if the |
168 | transition has no target. |
169 | */ |
170 | QAbstractState *QAbstractTransition::targetState() const |
171 | { |
172 | Q_D(const QAbstractTransition); |
173 | if (d->targetStates.isEmpty()) |
174 | return nullptr; |
175 | return d->targetStates.first().data(); |
176 | } |
177 | |
178 | /*! |
179 | Sets the \a target state of this transition. |
180 | */ |
181 | void QAbstractTransition::setTargetState(QAbstractState* target) |
182 | { |
183 | Q_D(QAbstractTransition); |
184 | if ((d->targetStates.size() == 1 && target == d->targetStates.at(i: 0).data()) || |
185 | (d->targetStates.isEmpty() && target == nullptr)) { |
186 | return; |
187 | } |
188 | if (!target) |
189 | d->targetStates.clear(); |
190 | else |
191 | setTargetStates(QList<QAbstractState*>() << target); |
192 | emit targetStateChanged(QPrivateSignal()); |
193 | } |
194 | |
195 | /*! |
196 | Returns the target states of this transition, or an empty list if this |
197 | transition has no target states. |
198 | */ |
199 | QList<QAbstractState*> QAbstractTransition::targetStates() const |
200 | { |
201 | Q_D(const QAbstractTransition); |
202 | QList<QAbstractState*> result; |
203 | for (int i = 0; i < d->targetStates.size(); ++i) { |
204 | QAbstractState *target = d->targetStates.at(i).data(); |
205 | if (target) |
206 | result.append(t: target); |
207 | } |
208 | return result; |
209 | } |
210 | |
211 | /*! |
212 | Sets the target states of this transition to be the given \a targets. |
213 | */ |
214 | void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets) |
215 | { |
216 | Q_D(QAbstractTransition); |
217 | |
218 | // Verify if any of the new target states is a null-pointer: |
219 | for (int i = 0; i < targets.size(); ++i) { |
220 | if (targets.at(i) == nullptr) { |
221 | qWarning(msg: "QAbstractTransition::setTargetStates: target state(s) cannot be null" ); |
222 | return; |
223 | } |
224 | } |
225 | |
226 | // First clean out any target states that got destroyed, but for which we still have a QPointer |
227 | // around. |
228 | for (int i = 0; i < d->targetStates.size(); ) { |
229 | if (d->targetStates.at(i).isNull()) { |
230 | d->targetStates.remove(i); |
231 | } else { |
232 | ++i; |
233 | } |
234 | } |
235 | |
236 | // Easy check: if both lists are empty, we're done. |
237 | if (targets.isEmpty() && d->targetStates.isEmpty()) |
238 | return; |
239 | |
240 | bool sameList = true; |
241 | |
242 | if (targets.size() != d->targetStates.size()) { |
243 | // If the sizes of the lists are different, we don't need to be smart: they're different. So |
244 | // we can just set the new list as the targetStates. |
245 | sameList = false; |
246 | } else { |
247 | QList<QPointer<QAbstractState>> copy(d->targetStates); |
248 | for (int i = 0; i < targets.size(); ++i) { |
249 | sameList &= copy.removeOne(t: targets.at(i)); |
250 | if (!sameList) |
251 | break; // ok, we now know the lists are not the same, so stop the loop. |
252 | } |
253 | |
254 | sameList &= copy.isEmpty(); |
255 | } |
256 | |
257 | if (sameList) |
258 | return; |
259 | |
260 | d->targetStates.resize(size: targets.size()); |
261 | for (int i = 0; i < targets.size(); ++i) { |
262 | d->targetStates[i] = targets.at(i); |
263 | } |
264 | |
265 | emit targetStatesChanged(QPrivateSignal()); |
266 | } |
267 | |
268 | /*! |
269 | Returns the type of the transition. |
270 | */ |
271 | QAbstractTransition::TransitionType QAbstractTransition::transitionType() const |
272 | { |
273 | Q_D(const QAbstractTransition); |
274 | return d->transitionType; |
275 | } |
276 | |
277 | /*! |
278 | Sets the type of the transition to \a type. |
279 | */ |
280 | void QAbstractTransition::setTransitionType(TransitionType type) |
281 | { |
282 | Q_D(QAbstractTransition); |
283 | d->transitionType = type; |
284 | } |
285 | |
286 | QBindable<QAbstractTransition::TransitionType> QAbstractTransition::bindableTransitionType() |
287 | { |
288 | Q_D(QAbstractTransition); |
289 | return &d->transitionType; |
290 | } |
291 | |
292 | /*! |
293 | Returns the state machine that this transition is part of, or |
294 | \nullptr if the transition is not part of a state machine. |
295 | */ |
296 | QStateMachine *QAbstractTransition::machine() const |
297 | { |
298 | Q_D(const QAbstractTransition); |
299 | return d->machine(); |
300 | } |
301 | |
302 | #if QT_CONFIG(animation) |
303 | |
304 | /*! |
305 | Adds the given \a animation to this transition. |
306 | The transition does not take ownership of the animation. |
307 | |
308 | \sa removeAnimation(), animations() |
309 | */ |
310 | void QAbstractTransition::addAnimation(QAbstractAnimation *animation) |
311 | { |
312 | Q_D(QAbstractTransition); |
313 | if (!animation) { |
314 | qWarning(msg: "QAbstractTransition::addAnimation: cannot add null animation" ); |
315 | return; |
316 | } |
317 | d->animations.append(t: animation); |
318 | } |
319 | |
320 | /*! |
321 | Removes the given \a animation from this transition. |
322 | |
323 | \sa addAnimation() |
324 | */ |
325 | void QAbstractTransition::removeAnimation(QAbstractAnimation *animation) |
326 | { |
327 | Q_D(QAbstractTransition); |
328 | if (!animation) { |
329 | qWarning(msg: "QAbstractTransition::removeAnimation: cannot remove null animation" ); |
330 | return; |
331 | } |
332 | d->animations.removeOne(t: animation); |
333 | } |
334 | |
335 | /*! |
336 | Returns the list of animations associated with this transition, or an empty |
337 | list if it has no animations. |
338 | |
339 | \sa addAnimation() |
340 | */ |
341 | QList<QAbstractAnimation*> QAbstractTransition::animations() const |
342 | { |
343 | Q_D(const QAbstractTransition); |
344 | return d->animations; |
345 | } |
346 | |
347 | #endif |
348 | |
349 | /*! |
350 | \fn QAbstractTransition::eventTest(QEvent *event) |
351 | |
352 | This function is called to determine whether the given \a event should cause |
353 | this transition to trigger. Reimplement this function and return true if the |
354 | event should trigger the transition, otherwise return false. |
355 | */ |
356 | |
357 | /*! |
358 | \fn QAbstractTransition::onTransition(QEvent *event) |
359 | |
360 | This function is called when the transition is triggered. The given \a event |
361 | is what caused the transition to trigger. Reimplement this function to |
362 | perform custom processing when the transition is triggered. |
363 | */ |
364 | |
365 | /*! |
366 | \fn QAbstractTransition::triggered() |
367 | |
368 | This signal is emitted when the transition has been triggered (after |
369 | onTransition() has been called). |
370 | */ |
371 | |
372 | /*! |
373 | \fn QAbstractTransition::targetStateChanged() |
374 | \since 5.4 |
375 | |
376 | This signal is emitted when the targetState property is changed. |
377 | |
378 | \sa QAbstractTransition::targetState |
379 | */ |
380 | |
381 | /*! |
382 | \fn QAbstractTransition::targetStatesChanged() |
383 | \since 5.4 |
384 | |
385 | This signal is emitted when the targetStates property is changed. |
386 | |
387 | \sa QAbstractTransition::targetStates |
388 | */ |
389 | |
390 | /*! |
391 | \reimp |
392 | */ |
393 | bool QAbstractTransition::event(QEvent *e) |
394 | { |
395 | return QObject::event(event: e); |
396 | } |
397 | |
398 | QT_END_NAMESPACE |
399 | |
400 | #include "moc_qabstracttransition.cpp" |
401 | |