1 | // Copyright (C) 2020 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 "qquickactiongroup_p.h" |
5 | |
6 | #include <QtCore/private/qobject_p.h> |
7 | #include <QtCore/qmetaobject.h> |
8 | #include <QtCore/qvariant.h> |
9 | #include <QtQml/qqmlinfo.h> |
10 | |
11 | #include "qquickaction_p.h" |
12 | #include "qquickaction_p_p.h" |
13 | |
14 | #include <QtCore/qpointer.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | /*! |
19 | \qmltype ActionGroup |
20 | \inherits QtObject |
21 | //! \nativetype QQuickActionGroup |
22 | \inqmlmodule QtQuick.Controls |
23 | \since 5.10 |
24 | \ingroup utilities |
25 | \brief Groups actions together. |
26 | |
27 | ActionGroup is a non-visual group of actions. A mutually \l exclusive |
28 | action group is used with actions where only one of the options can be |
29 | selected at a time. |
30 | |
31 | The most straight-forward way to use ActionGroup is to declare actions |
32 | as children of the group. |
33 | |
34 | \code |
35 | ActionGroup { |
36 | id: alignmentGroup |
37 | |
38 | Action { |
39 | checked: true |
40 | checkable: true |
41 | text: qsTr("Left") |
42 | } |
43 | |
44 | Action { |
45 | checkable: true |
46 | text: qsTr("Center") |
47 | } |
48 | |
49 | Action { |
50 | checkable: true |
51 | text: qsTr("Right") |
52 | } |
53 | } |
54 | \endcode |
55 | |
56 | Alternatively, the \l group attached property allows declaring the actions |
57 | elsewhere and assigning them to a specific group. |
58 | |
59 | \code |
60 | ActionGroup { id: alignmentGroup } |
61 | |
62 | Action { |
63 | checked: true |
64 | checkable: true |
65 | text: qsTr("Left") |
66 | ActionGroup.group: alignmentGroup |
67 | } |
68 | |
69 | Action { |
70 | checkable: true |
71 | text: qsTr("Center") |
72 | ActionGroup.group: alignmentGroup |
73 | } |
74 | |
75 | Action { |
76 | checkable: true |
77 | text: qsTr("Right") |
78 | ActionGroup.group: alignmentGroup |
79 | } |
80 | \endcode |
81 | |
82 | More advanced use cases can be handled using the \c addAction() and |
83 | \c removeAction() methods. |
84 | |
85 | \sa Action, ButtonGroup |
86 | */ |
87 | |
88 | /*! |
89 | \qmlsignal QtQuick.Controls::ActionGroup::triggered(Action action) |
90 | |
91 | This signal is emitted when an \a action in the group has been triggered. |
92 | |
93 | This signal is convenient for implementing a common signal handler for |
94 | all actions in the same group. |
95 | |
96 | \code |
97 | ActionGroup { |
98 | onTriggered: console.log("triggered:", action.text) |
99 | |
100 | Action { text: "First" } |
101 | Action { text: "Second" } |
102 | Action { text: "Third" } |
103 | } |
104 | \endcode |
105 | |
106 | \sa Action::triggered() |
107 | */ |
108 | |
109 | class QQuickActionGroupPrivate : public QObjectPrivate |
110 | { |
111 | public: |
112 | Q_DECLARE_PUBLIC(QQuickActionGroup) |
113 | |
114 | void clear(); |
115 | void actionTriggered(); |
116 | void _q_updateCurrent(); |
117 | |
118 | static bool changeEnabled(QQuickAction *action, bool enabled); |
119 | |
120 | static void actions_append(QQmlListProperty<QQuickAction> *prop, QQuickAction *obj); |
121 | static qsizetype actions_count(QQmlListProperty<QQuickAction> *prop); |
122 | static QQuickAction *actions_at(QQmlListProperty<QQuickAction> *prop, qsizetype index); |
123 | static void actions_clear(QQmlListProperty<QQuickAction> *prop); |
124 | |
125 | bool enabled = true; |
126 | bool exclusive = true; |
127 | QPointer<QQuickAction> checkedAction; |
128 | QList<QQuickAction*> actions; |
129 | }; |
130 | |
131 | void QQuickActionGroupPrivate::clear() |
132 | { |
133 | for (QQuickAction *action : std::as_const(t&: actions)) { |
134 | QQuickActionPrivate::get(action)->group = nullptr; |
135 | QObjectPrivate::disconnect(sender: action, signal: &QQuickAction::triggered, receiverPrivate: this, slot: &QQuickActionGroupPrivate::actionTriggered); |
136 | QObjectPrivate::disconnect(sender: action, signal: &QQuickAction::checkedChanged, receiverPrivate: this, slot: &QQuickActionGroupPrivate::_q_updateCurrent); |
137 | } |
138 | actions.clear(); |
139 | } |
140 | |
141 | void QQuickActionGroupPrivate::actionTriggered() |
142 | { |
143 | Q_Q(QQuickActionGroup); |
144 | QQuickAction *action = qobject_cast<QQuickAction*>(object: q->sender()); |
145 | if (action) |
146 | emit q->triggered(action); |
147 | } |
148 | |
149 | void QQuickActionGroupPrivate::_q_updateCurrent() |
150 | { |
151 | Q_Q(QQuickActionGroup); |
152 | if (!exclusive) |
153 | return; |
154 | QQuickAction *action = qobject_cast<QQuickAction*>(object: q->sender()); |
155 | if (action && action->isChecked()) |
156 | q->setCheckedAction(action); |
157 | else if (!actions.contains(t: checkedAction)) |
158 | q->setCheckedAction(nullptr); |
159 | } |
160 | |
161 | bool QQuickActionGroupPrivate::changeEnabled(QQuickAction *action, bool enabled) |
162 | { |
163 | return action->isEnabled() != enabled && (!enabled || !QQuickActionPrivate::get(action)->explicitEnabled); |
164 | } |
165 | |
166 | void QQuickActionGroupPrivate::actions_append(QQmlListProperty<QQuickAction> *prop, QQuickAction *obj) |
167 | { |
168 | QQuickActionGroup *q = static_cast<QQuickActionGroup *>(prop->object); |
169 | q->addAction(action: obj); |
170 | } |
171 | |
172 | qsizetype QQuickActionGroupPrivate::actions_count(QQmlListProperty<QQuickAction> *prop) |
173 | { |
174 | QQuickActionGroupPrivate *p = static_cast<QQuickActionGroupPrivate *>(prop->data); |
175 | return p->actions.size(); |
176 | } |
177 | |
178 | QQuickAction *QQuickActionGroupPrivate::actions_at(QQmlListProperty<QQuickAction> *prop, qsizetype index) |
179 | { |
180 | QQuickActionGroupPrivate *p = static_cast<QQuickActionGroupPrivate *>(prop->data); |
181 | return p->actions.value(i: index); |
182 | } |
183 | |
184 | void QQuickActionGroupPrivate::actions_clear(QQmlListProperty<QQuickAction> *prop) |
185 | { |
186 | QQuickActionGroupPrivate *p = static_cast<QQuickActionGroupPrivate *>(prop->data); |
187 | if (!p->actions.isEmpty()) { |
188 | p->clear(); |
189 | QQuickActionGroup *q = static_cast<QQuickActionGroup *>(prop->object); |
190 | // QTBUG-52358: don't clear the checked action immediately |
191 | QMetaObject::invokeMethod(obj: q, member: "_q_updateCurrent", c: Qt::QueuedConnection); |
192 | emit q->actionsChanged(); |
193 | } |
194 | } |
195 | |
196 | QQuickActionGroup::QQuickActionGroup(QObject *parent) |
197 | : QObject(*(new QQuickActionGroupPrivate), parent) |
198 | { |
199 | } |
200 | |
201 | QQuickActionGroup::~QQuickActionGroup() |
202 | { |
203 | Q_D(QQuickActionGroup); |
204 | d->clear(); |
205 | } |
206 | |
207 | QQuickActionGroupAttached *QQuickActionGroup::qmlAttachedProperties(QObject *object) |
208 | { |
209 | return new QQuickActionGroupAttached(object); |
210 | } |
211 | |
212 | /*! |
213 | \qmlproperty Action QtQuick.Controls::ActionGroup::checkedAction |
214 | |
215 | This property holds the currently selected action in an exclusive group, |
216 | or \c null if there is none or the group is non-exclusive. |
217 | |
218 | By default, it is the first checked action added to an exclusive action group. |
219 | |
220 | \sa exclusive |
221 | */ |
222 | QQuickAction *QQuickActionGroup::checkedAction() const |
223 | { |
224 | Q_D(const QQuickActionGroup); |
225 | return d->checkedAction; |
226 | } |
227 | |
228 | void QQuickActionGroup::setCheckedAction(QQuickAction *checkedAction) |
229 | { |
230 | Q_D(QQuickActionGroup); |
231 | if (d->checkedAction == checkedAction) |
232 | return; |
233 | |
234 | if (d->checkedAction) |
235 | d->checkedAction->setChecked(false); |
236 | d->checkedAction = checkedAction; |
237 | if (checkedAction) |
238 | checkedAction->setChecked(true); |
239 | emit checkedActionChanged(); |
240 | } |
241 | |
242 | /*! |
243 | \qmlproperty list<Action> QtQuick.Controls::ActionGroup::actions |
244 | \qmldefault |
245 | |
246 | This property holds the list of actions in the group. |
247 | |
248 | \sa group |
249 | */ |
250 | QQmlListProperty<QQuickAction> QQuickActionGroup::actions() |
251 | { |
252 | Q_D(QQuickActionGroup); |
253 | return QQmlListProperty<QQuickAction>(this, d, |
254 | QQuickActionGroupPrivate::actions_append, |
255 | QQuickActionGroupPrivate::actions_count, |
256 | QQuickActionGroupPrivate::actions_at, |
257 | QQuickActionGroupPrivate::actions_clear); |
258 | } |
259 | |
260 | /*! |
261 | \qmlproperty bool QtQuick.Controls::ActionGroup::exclusive |
262 | |
263 | This property holds whether the action group is exclusive. The default value is \c true. |
264 | |
265 | If this property is \c true, then only one action in the group can be checked at any given time. |
266 | The user can trigger any action to check it, and that action will replace the existing one as |
267 | the checked action in the group. |
268 | |
269 | In an exclusive group, the user cannot uncheck the currently checked action by triggering it; |
270 | instead, another action in the group must be triggered to set the new checked action for that |
271 | group. |
272 | |
273 | In a non-exclusive group, checking and unchecking actions does not affect the other actions in |
274 | the group. Furthermore, the value of the \l checkedAction property is \c null. |
275 | */ |
276 | bool QQuickActionGroup::isExclusive() const |
277 | { |
278 | Q_D(const QQuickActionGroup); |
279 | return d->exclusive; |
280 | } |
281 | |
282 | void QQuickActionGroup::setExclusive(bool exclusive) |
283 | { |
284 | Q_D(QQuickActionGroup); |
285 | if (d->exclusive == exclusive) |
286 | return; |
287 | |
288 | d->exclusive = exclusive; |
289 | emit exclusiveChanged(); |
290 | } |
291 | |
292 | /*! |
293 | \qmlproperty bool QtQuick.Controls::ActionGroup::enabled |
294 | |
295 | This property holds whether the action group is enabled. The default value is \c true. |
296 | |
297 | If this property is \c false, then all actions in the group are disabled. If this property |
298 | is \c true, all actions in the group are enabled, unless explicitly disabled. |
299 | */ |
300 | bool QQuickActionGroup::isEnabled() const |
301 | { |
302 | Q_D(const QQuickActionGroup); |
303 | return d->enabled; |
304 | } |
305 | |
306 | void QQuickActionGroup::setEnabled(bool enabled) |
307 | { |
308 | Q_D(QQuickActionGroup); |
309 | if (d->enabled == enabled) |
310 | return; |
311 | |
312 | for (QQuickAction *action : std::as_const(t&: d->actions)) { |
313 | if (d->changeEnabled(action, enabled)) |
314 | emit action->enabledChanged(enabled); |
315 | } |
316 | |
317 | d->enabled = enabled; |
318 | emit enabledChanged(); |
319 | } |
320 | |
321 | /*! |
322 | \qmlmethod void QtQuick.Controls::ActionGroup::addAction(Action action) |
323 | |
324 | Adds an \a action to the action group. |
325 | |
326 | \note Manually adding objects to a action group is typically unnecessary. |
327 | The \l actions property and the \l group attached property provide a |
328 | convenient and declarative syntax. |
329 | |
330 | \sa actions, group |
331 | */ |
332 | void QQuickActionGroup::addAction(QQuickAction *action) |
333 | { |
334 | Q_D(QQuickActionGroup); |
335 | if (!action || d->actions.contains(t: action)) |
336 | return; |
337 | |
338 | const bool enabledChange = d->changeEnabled(action, enabled: d->enabled); |
339 | |
340 | QQuickActionPrivate::get(action)->group = this; |
341 | QObjectPrivate::connect(sender: action, signal: &QQuickAction::triggered, receiverPrivate: d, slot: &QQuickActionGroupPrivate::actionTriggered); |
342 | QObjectPrivate::connect(sender: action, signal: &QQuickAction::checkedChanged, receiverPrivate: d, slot: &QQuickActionGroupPrivate::_q_updateCurrent); |
343 | |
344 | if (d->exclusive && action->isChecked()) |
345 | setCheckedAction(action); |
346 | if (enabledChange) |
347 | emit action->enabledChanged(enabled: action->isEnabled()); |
348 | |
349 | d->actions.append(t: action); |
350 | emit actionsChanged(); |
351 | } |
352 | |
353 | /*! |
354 | \qmlmethod void QtQuick.Controls::ActionGroup::removeAction(Action action) |
355 | |
356 | Removes an \a action from the action group. |
357 | |
358 | \note Manually removing objects from a action group is typically unnecessary. |
359 | The \l actions property and the \l group attached property provide a |
360 | convenient and declarative syntax. |
361 | |
362 | \sa actions, group |
363 | */ |
364 | void QQuickActionGroup::removeAction(QQuickAction *action) |
365 | { |
366 | Q_D(QQuickActionGroup); |
367 | if (!action || !d->actions.contains(t: action)) |
368 | return; |
369 | |
370 | const bool enabledChange = d->changeEnabled(action, enabled: d->enabled); |
371 | |
372 | QQuickActionPrivate::get(action)->group = nullptr; |
373 | QObjectPrivate::disconnect(sender: action, signal: &QQuickAction::triggered, receiverPrivate: d, slot: &QQuickActionGroupPrivate::actionTriggered); |
374 | QObjectPrivate::disconnect(sender: action, signal: &QQuickAction::checkedChanged, receiverPrivate: d, slot: &QQuickActionGroupPrivate::_q_updateCurrent); |
375 | |
376 | if (d->checkedAction == action) |
377 | setCheckedAction(nullptr); |
378 | if (enabledChange) |
379 | emit action->enabledChanged(enabled: action->isEnabled()); |
380 | |
381 | d->actions.removeOne(t: action); |
382 | emit actionsChanged(); |
383 | } |
384 | |
385 | class QQuickActionGroupAttachedPrivate : public QObjectPrivate |
386 | { |
387 | public: |
388 | QQuickActionGroup *group = nullptr; |
389 | }; |
390 | |
391 | QQuickActionGroupAttached::QQuickActionGroupAttached(QObject *parent) |
392 | : QObject(*(new QQuickActionGroupAttachedPrivate), parent) |
393 | { |
394 | } |
395 | |
396 | /*! |
397 | \qmlattachedproperty ActionGroup QtQuick.Controls::ActionGroup::group |
398 | |
399 | This property attaches an action to an action group. |
400 | |
401 | \code |
402 | ActionGroup { id: group } |
403 | |
404 | Action { |
405 | checked: true |
406 | text: qsTr("Option A") |
407 | ActionGroup.group: group |
408 | } |
409 | |
410 | Action { |
411 | text: qsTr("Option B") |
412 | ActionGroup.group: group |
413 | } |
414 | \endcode |
415 | |
416 | \sa actions |
417 | */ |
418 | QQuickActionGroup *QQuickActionGroupAttached::group() const |
419 | { |
420 | Q_D(const QQuickActionGroupAttached); |
421 | return d->group; |
422 | } |
423 | |
424 | void QQuickActionGroupAttached::setGroup(QQuickActionGroup *group) |
425 | { |
426 | Q_D(QQuickActionGroupAttached); |
427 | if (d->group == group) |
428 | return; |
429 | |
430 | if (d->group) |
431 | d->group->removeAction(action: qobject_cast<QQuickAction*>(object: parent())); |
432 | d->group = group; |
433 | if (group) |
434 | group->addAction(action: qobject_cast<QQuickAction*>(object: parent())); |
435 | emit groupChanged(); |
436 | } |
437 | |
438 | QT_END_NAMESPACE |
439 | |
440 | #include "moc_qquickactiongroup_p.cpp" |
441 |
Definitions
- QQuickActionGroupPrivate
- clear
- actionTriggered
- _q_updateCurrent
- changeEnabled
- actions_append
- actions_count
- actions_at
- actions_clear
- QQuickActionGroup
- ~QQuickActionGroup
- qmlAttachedProperties
- checkedAction
- setCheckedAction
- actions
- isExclusive
- setExclusive
- isEnabled
- setEnabled
- addAction
- removeAction
- QQuickActionGroupAttachedPrivate
- QQuickActionGroupAttached
- group
Start learning QML with our Intro Training
Find out more