1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Controls module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickexclusivegroup_p.h"
41
42#include <qvariant.h>
43#include <qdebug.h>
44#include "qquickaction_p.h"
45
46#define CHECKED_PROPERTY "checked"
47
48QT_BEGIN_NAMESPACE
49
50static const char *checkableSignals[] = {
51 CHECKED_PROPERTY"Changed()",
52 "toggled(bool)",
53 "toggled()",
54 0
55};
56
57static bool isChecked(const QObject *o)
58{
59 if (!o) return false;
60 QVariant checkedVariant = o->property(CHECKED_PROPERTY);
61 return checkedVariant.isValid() && checkedVariant.toBool();
62}
63
64/*!
65 \qmltype ExclusiveGroup
66 \instantiates QQuickExclusiveGroup1
67 \ingroup controls
68 \inqmlmodule QtQuick.Controls
69 \brief ExclusiveGroup provides a way to declare several checkable controls as mutually exclusive.
70
71 ExclusiveGroup can contain several \l Action items, and those will automatically get their
72 \l Action::exclusiveGroup property assigned.
73
74 \code
75 ExclusiveGroup {
76 id: radioInputGroup
77
78 Action {
79 id: dabRadioInput
80 text: "DAB"
81 checkable: true
82 }
83
84 Action {
85 id: fmRadioInput
86 text: "FM"
87 checkable: true
88 }
89
90 Action {
91 id: amRadioInput
92 text: "AM"
93 checkable: true
94 }
95 }
96 \endcode
97
98 Several controls already support ExclusiveGroup, e.g. \l Action,
99 \l MenuItem, \l {QtQuick.Controls::}{Button}, and \l RadioButton.
100
101 As ExclusiveGroup only supports \l Action as child items, we need to manually
102 assign the \c exclusiveGroup property for other objects.
103
104 \code
105 GroupBox {
106 id: group2
107 title: qsTr("Tab Position")
108 Layout.fillWidth: true
109 RowLayout {
110 ExclusiveGroup { id: tabPositionGroup }
111 RadioButton {
112 id: topButton
113 text: qsTr("Top")
114 checked: true
115 exclusiveGroup: tabPositionGroup
116 Layout.minimumWidth: 100
117 }
118 RadioButton {
119 id: bottomButton
120 text: qsTr("Bottom")
121 exclusiveGroup: tabPositionGroup
122 Layout.minimumWidth: 100
123 }
124 }
125 }
126 \endcode
127
128 \section1 Adding support to ExclusiveGroup
129
130 It is possible to add support for ExclusiveGroup for an object or control. It should have a \c checked
131 property, and either a \c checkedChanged, \c toggled(), or \c toggled(bool) signal. It also needs
132 to be bound with \l ExclusiveGroup::bindCheckable() when its ExclusiveGroup typed property is set.
133
134 \code
135 Item {
136 id: myItem
137
138 property bool checked: false
139 property ExclusiveGroup exclusiveGroup: null
140
141 onExclusiveGroupChanged: {
142 if (exclusiveGroup)
143 exclusiveGroup.bindCheckable(myItem)
144 }
145 }
146 \endcode
147
148 The example above shows the minimum code necessary to add ExclusiveGroup support to any item.
149*/
150
151/*!
152 \qmlproperty object ExclusiveGroup::current
153
154 The currently selected object. Defaults to the first checked object bound to the ExclusiveGroup.
155 If there is none, then it defaults to \c null.
156*/
157
158/*!
159 \qmlmethod void ExclusiveGroup::bindCheckable(object)
160
161 Registers \a object to the exclusive group.
162
163 You should only need to call this function when creating a component you want to be compatible with ExclusiveGroup.
164
165 \sa ExclusiveGroup::unbindCheckable()
166*/
167
168/*!
169 \qmlmethod void ExclusiveGroup::unbindCheckable(object)
170
171 Unregisters \a object from the exclusive group.
172
173 You should only need to call this function when creating a component you want to be compatible with ExclusiveGroup.
174
175 \sa ExclusiveGroup::bindCheckable()
176*/
177
178QQuickExclusiveGroup1::QQuickExclusiveGroup1(QObject *parent)
179 : QObject(parent), m_current(0)
180{
181 int index = metaObject()->indexOfMethod(method: "updateCurrent()");
182 m_updateCurrentMethod = metaObject()->method(index);
183}
184
185QQmlListProperty<QQuickAction1> QQuickExclusiveGroup1::actions()
186{
187 return QQmlListProperty<QQuickAction1>(this, 0, &QQuickExclusiveGroup1::append_actions, 0, 0, 0);
188}
189
190void QQuickExclusiveGroup1::append_actions(QQmlListProperty<QQuickAction1> *list, QQuickAction1 *action)
191{
192 if (QQuickExclusiveGroup1 *eg = qobject_cast<QQuickExclusiveGroup1 *>(object: list->object))
193 action->setExclusiveGroup(eg);
194}
195
196void QQuickExclusiveGroup1::setCurrent(QObject * o)
197{
198 if (m_current == o)
199 return;
200
201 if (m_current)
202 m_current->setProperty(CHECKED_PROPERTY, value: QVariant(false));
203 m_current = o;
204 if (m_current)
205 m_current->setProperty(CHECKED_PROPERTY, value: QVariant(true));
206 emit currentChanged();
207}
208
209void QQuickExclusiveGroup1::updateCurrent()
210{
211 QObject *checkable = sender();
212 if (isChecked(o: checkable))
213 setCurrent(checkable);
214}
215
216void QQuickExclusiveGroup1::bindCheckable(QObject *o)
217{
218 for (const char **signalName = checkableSignals; *signalName; signalName++) {
219 int signalIndex = o->metaObject()->indexOfSignal(signal: *signalName);
220 if (signalIndex != -1) {
221 QMetaMethod signalMethod = o->metaObject()->method(index: signalIndex);
222 connect(sender: o, signal: signalMethod, receiver: this, method: m_updateCurrentMethod, type: Qt::UniqueConnection);
223 connect(sender: o, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(unbindCheckable(QObject*)), Qt::UniqueConnection);
224 if (!m_current && isChecked(o))
225 setCurrent(o);
226 return;
227 }
228 }
229
230 qWarning() << "QQuickExclusiveGroup1::bindCheckable(): Cannot bind to" << o;
231}
232
233void QQuickExclusiveGroup1::unbindCheckable(QObject *o)
234{
235 if (m_current == o)
236 setCurrent(0);
237 for (const char **signalName = checkableSignals; *signalName; signalName++) {
238 int signalIndex = o->metaObject()->indexOfSignal(signal: *signalName);
239 if (signalIndex != -1) {
240 QMetaMethod signalMethod = o->metaObject()->method(index: signalIndex);
241 if (disconnect(sender: o, signal: signalMethod, receiver: this, member: m_updateCurrentMethod)) {
242 disconnect(sender: o, SIGNAL(destroyed(QObject*)), receiver: this, SLOT(unbindCheckable(QObject*)));
243 break;
244 }
245 }
246 }
247}
248
249QT_END_NAMESPACE
250

source code of qtquickcontrols/src/controls/qquickexclusivegroup.cpp