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 "qhistorystate.h" |
5 | #include "qhistorystate_p.h" |
6 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | /*! |
10 | \class QHistoryState |
11 | \inmodule QtStateMachine |
12 | |
13 | \brief The QHistoryState class provides a means of returning to a previously active substate. |
14 | |
15 | \since 4.6 |
16 | \ingroup statemachine |
17 | |
18 | A history state is a pseudo-state that represents the child state that the |
19 | parent state was in the last time the parent state was exited. A transition |
20 | with a history state as its target is in fact a transition to one or more |
21 | other child states of the parent state. QHistoryState is part of \l{Qt State Machine Overview} |
22 | {Qt State Machine Framework}. |
23 | |
24 | Use the setDefaultState() function to set the state that should be entered |
25 | if the parent state has never been entered. Example: |
26 | |
27 | \code |
28 | QStateMachine machine; |
29 | |
30 | QState *s1 = new QState(); |
31 | QState *s11 = new QState(s1); |
32 | QState *s12 = new QState(s1); |
33 | |
34 | QHistoryState *s1h = new QHistoryState(s1); |
35 | s1h->setDefaultState(s11); |
36 | |
37 | machine.addState(s1); |
38 | |
39 | QState *s2 = new QState(); |
40 | machine.addState(s2); |
41 | |
42 | QPushButton *button = new QPushButton(); |
43 | // Clicking the button will cause the state machine to enter the child state |
44 | // that s1 was in the last time s1 was exited, or the history state's default |
45 | // state if s1 has never been entered. |
46 | s1->addTransition(button, SIGNAL(clicked()), s1h); |
47 | \endcode |
48 | |
49 | If more than one default state has to be entered, or if the transition to the default state(s) |
50 | has to be acted upon, the defaultTransition should be set instead. Note that the eventTest() |
51 | method of that transition will never be called: the selection and execution of the transition is |
52 | done automatically when entering the history state. |
53 | |
54 | By default a history state is shallow, meaning that it won't remember nested |
55 | states. This can be configured through the historyType property. |
56 | */ |
57 | |
58 | /*! |
59 | \property QHistoryState::defaultTransition |
60 | |
61 | \brief the default transition of this history state |
62 | */ |
63 | |
64 | /*! |
65 | \property QHistoryState::defaultState |
66 | |
67 | \brief the default state of this history state |
68 | */ |
69 | |
70 | /*! |
71 | \property QHistoryState::historyType |
72 | |
73 | \brief the type of history that this history state records |
74 | |
75 | The default value of this property is QHistoryState::ShallowHistory. |
76 | */ |
77 | |
78 | /*! |
79 | \enum QHistoryState::HistoryType |
80 | |
81 | This enum specifies the type of history that a QHistoryState records. |
82 | |
83 | \value ShallowHistory Only the immediate child states of the parent state |
84 | are recorded. In this case a transition with the history state as its |
85 | target will end up in the immediate child state that the parent was in the |
86 | last time it was exited. This is the default. |
87 | |
88 | \value DeepHistory Nested states are recorded. In this case a transition |
89 | with the history state as its target will end up in the most deeply nested |
90 | descendant state the parent was in the last time it was exited. |
91 | */ |
92 | |
93 | namespace { |
94 | class DefaultStateTransition: public QAbstractTransition |
95 | { |
96 | Q_OBJECT |
97 | |
98 | public: |
99 | DefaultStateTransition(QHistoryState *source, QAbstractState *target); |
100 | |
101 | protected: |
102 | // It doesn't matter whether this transition matches any event or not. It is always associated |
103 | // with a QHistoryState, and as soon as the state-machine detects that it enters a history |
104 | // state, it will handle this transition as a special case. The history state itself is never |
105 | // entered either: either the stored configuration will be used, or the target(s) of this |
106 | // transition are used. |
107 | bool eventTest(QEvent *event) override { Q_UNUSED(event); return false; } |
108 | void onTransition(QEvent *event) override { Q_UNUSED(event); } |
109 | }; |
110 | } |
111 | |
112 | QHistoryStatePrivate::QHistoryStatePrivate() |
113 | : QAbstractStatePrivate(HistoryState) |
114 | , defaultTransition(nullptr) |
115 | , historyType(QHistoryState::ShallowHistory) |
116 | { |
117 | } |
118 | |
119 | DefaultStateTransition::DefaultStateTransition(QHistoryState *source, QAbstractState *target) |
120 | : QAbstractTransition() |
121 | { |
122 | setParent(source); |
123 | setTargetState(target); |
124 | } |
125 | |
126 | /*! |
127 | Constructs a new shallow history state with the given \a parent state. |
128 | */ |
129 | QHistoryState::QHistoryState(QState *parent) |
130 | : QAbstractState(*new QHistoryStatePrivate, parent) |
131 | { |
132 | } |
133 | /*! |
134 | Constructs a new history state of the given \a type, with the given \a |
135 | parent state. |
136 | */ |
137 | QHistoryState::QHistoryState(HistoryType type, QState *parent) |
138 | : QAbstractState(*new QHistoryStatePrivate, parent) |
139 | { |
140 | Q_D(QHistoryState); |
141 | d->historyType = type; |
142 | } |
143 | |
144 | /*! |
145 | Destroys this history state. |
146 | */ |
147 | QHistoryState::~QHistoryState() |
148 | { |
149 | } |
150 | |
151 | /*! |
152 | Returns this history state's default transition. The default transition is |
153 | taken when the history state has never been entered before. The target states |
154 | of the default transition therefore make up the default state. |
155 | |
156 | \since 5.6 |
157 | */ |
158 | QAbstractTransition *QHistoryState::defaultTransition() const |
159 | { |
160 | Q_D(const QHistoryState); |
161 | return d->defaultTransition; |
162 | } |
163 | |
164 | /*! |
165 | Sets this history state's default transition to be the given \a transition. |
166 | This will set the source state of the \a transition to the history state. |
167 | |
168 | Note that the eventTest method of the \a transition will never be called. |
169 | |
170 | \since 5.6 |
171 | */ |
172 | void QHistoryState::setDefaultTransition(QAbstractTransition *transition) |
173 | { |
174 | Q_D(QHistoryState); |
175 | if (d->defaultTransition.value() == transition) { |
176 | d->defaultTransition.removeBindingUnlessInWrapper(); |
177 | return; |
178 | } |
179 | d->defaultTransition = transition; |
180 | if (transition) |
181 | transition->setParent(this); |
182 | d->defaultTransition.notify(); |
183 | emit defaultTransitionChanged(QHistoryState::QPrivateSignal()); |
184 | } |
185 | |
186 | QBindable<QAbstractTransition*> QHistoryState::bindableDefaultTransition() |
187 | { |
188 | Q_D(QHistoryState); |
189 | return &d->defaultTransition; |
190 | } |
191 | |
192 | /*! |
193 | Returns this history state's default state. The default state indicates the |
194 | state to transition to if the parent state has never been entered before. |
195 | */ |
196 | QAbstractState *QHistoryState::defaultState() const |
197 | { |
198 | Q_D(const QHistoryState); |
199 | return d->defaultTransition.value() ? d->defaultTransition->targetState() : nullptr; |
200 | } |
201 | |
202 | static inline bool isSoleEntry(const QList<QAbstractState*> &states, const QAbstractState * state) |
203 | { |
204 | return states.size() == 1 && states.first() == state; |
205 | } |
206 | |
207 | /*! |
208 | Sets this history state's default state to be the given \a state. |
209 | \a state must be a sibling of this history state. |
210 | |
211 | Note that this function does not set \a state as the initial state |
212 | of its parent. |
213 | */ |
214 | void QHistoryState::setDefaultState(QAbstractState *state) |
215 | { |
216 | Q_D(QHistoryState); |
217 | if (state && state->parentState() != parentState()) { |
218 | qWarning(msg: "QHistoryState::setDefaultState: state %p does not belong " |
219 | "to this history state's group (%p)" , state, parentState()); |
220 | return; |
221 | } |
222 | if (!d->defaultTransition.value() |
223 | || !isSoleEntry(states: d->defaultTransition->targetStates(), state)) { |
224 | if (!d->defaultTransition.value() |
225 | || !qobject_cast<DefaultStateTransition*>(object: d->defaultTransition)) { |
226 | d->defaultTransition.setValue(new DefaultStateTransition(this, state)); |
227 | } else { |
228 | d->defaultTransition->setTargetState(state); |
229 | } |
230 | emit defaultStateChanged(QHistoryState::QPrivateSignal()); |
231 | } |
232 | } |
233 | |
234 | /*! |
235 | Returns the type of history that this history state records. |
236 | */ |
237 | QHistoryState::HistoryType QHistoryState::historyType() const |
238 | { |
239 | Q_D(const QHistoryState); |
240 | return d->historyType; |
241 | } |
242 | |
243 | /*! |
244 | Sets the \a type of history that this history state records. |
245 | */ |
246 | void QHistoryState::setHistoryType(HistoryType type) |
247 | { |
248 | Q_D(QHistoryState); |
249 | d->historyType = type; |
250 | } |
251 | |
252 | QBindable<QHistoryState::HistoryType> QHistoryState::bindableHistoryType() |
253 | { |
254 | Q_D(QHistoryState); |
255 | return &d->historyType; |
256 | } |
257 | |
258 | /*! |
259 | \reimp |
260 | */ |
261 | void QHistoryState::onEntry(QEvent *event) |
262 | { |
263 | Q_UNUSED(event); |
264 | } |
265 | |
266 | /*! |
267 | \reimp |
268 | */ |
269 | void QHistoryState::onExit(QEvent *event) |
270 | { |
271 | Q_UNUSED(event); |
272 | } |
273 | |
274 | /*! |
275 | \reimp |
276 | */ |
277 | bool QHistoryState::event(QEvent *e) |
278 | { |
279 | return QAbstractState::event(e); |
280 | } |
281 | |
282 | /*! |
283 | \fn QHistoryState::defaultStateChanged() |
284 | \since 5.4 |
285 | |
286 | This signal is emitted when the defaultState property is changed. |
287 | |
288 | \sa QHistoryState::defaultState |
289 | */ |
290 | |
291 | /*! |
292 | \fn QHistoryState::historyTypeChanged() |
293 | \since 5.4 |
294 | |
295 | This signal is emitted when the historyType property is changed. |
296 | |
297 | \sa QHistoryState::historyType |
298 | */ |
299 | |
300 | /*! |
301 | \fn QHistoryState::defaultTransitionChanged() |
302 | \since 5.6 |
303 | |
304 | This signal is emitted when the defaultTransition property is changed. |
305 | |
306 | \sa QHistoryState::defaultTransition |
307 | */ |
308 | |
309 | QT_END_NAMESPACE |
310 | |
311 | #include "moc_qhistorystate.cpp" |
312 | #include "qhistorystate.moc" |
313 | |