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

source code of qtquickcontrols2/src/quicktemplates2/qquickactiongroup.cpp