| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Quick Controls module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qquickexclusivegroup_p.h" |
| |
| #include <qvariant.h> |
| #include <qdebug.h> |
| #include "qquickaction_p.h" |
| |
| #define CHECKED_PROPERTY "checked" |
| |
| QT_BEGIN_NAMESPACE |
| |
| static const char *checkableSignals[] = { |
| CHECKED_PROPERTY"Changed()", |
| "toggled(bool)", |
| "toggled()", |
| 0 |
| }; |
| |
| static bool isChecked(const QObject *o) |
| { |
| if (!o) return false; |
| QVariant checkedVariant = o->property(CHECKED_PROPERTY); |
| return checkedVariant.isValid() && checkedVariant.toBool(); |
| } |
| |
| /*! |
| \qmltype ExclusiveGroup |
| \instantiates QQuickExclusiveGroup1 |
| \ingroup controls |
| \inqmlmodule QtQuick.Controls |
| \brief ExclusiveGroup provides a way to declare several checkable controls as mutually exclusive. |
| |
| ExclusiveGroup can contain several \l Action items, and those will automatically get their |
| \l Action::exclusiveGroup property assigned. |
| |
| \code |
| ExclusiveGroup { |
| id: radioInputGroup |
| |
| Action { |
| id: dabRadioInput |
| text: "DAB" |
| checkable: true |
| } |
| |
| Action { |
| id: fmRadioInput |
| text: "FM" |
| checkable: true |
| } |
| |
| Action { |
| id: amRadioInput |
| text: "AM" |
| checkable: true |
| } |
| } |
| \endcode |
| |
| Several controls already support ExclusiveGroup, e.g. \l Action, |
| \l MenuItem, \l {QtQuick.Controls::}{Button}, and \l RadioButton. |
| |
| As ExclusiveGroup only supports \l Action as child items, we need to manually |
| assign the \c exclusiveGroup property for other objects. |
| |
| \code |
| GroupBox { |
| id: group2 |
| title: qsTr("Tab Position") |
| Layout.fillWidth: true |
| RowLayout { |
| ExclusiveGroup { id: tabPositionGroup } |
| RadioButton { |
| id: topButton |
| text: qsTr("Top") |
| checked: true |
| exclusiveGroup: tabPositionGroup |
| Layout.minimumWidth: 100 |
| } |
| RadioButton { |
| id: bottomButton |
| text: qsTr("Bottom") |
| exclusiveGroup: tabPositionGroup |
| Layout.minimumWidth: 100 |
| } |
| } |
| } |
| \endcode |
| |
| \section1 Adding support to ExclusiveGroup |
| |
| It is possible to add support for ExclusiveGroup for an object or control. It should have a \c checked |
| property, and either a \c checkedChanged, \c toggled(), or \c toggled(bool) signal. It also needs |
| to be bound with \l ExclusiveGroup::bindCheckable() when its ExclusiveGroup typed property is set. |
| |
| \code |
| Item { |
| id: myItem |
| |
| property bool checked: false |
| property ExclusiveGroup exclusiveGroup: null |
| |
| onExclusiveGroupChanged: { |
| if (exclusiveGroup) |
| exclusiveGroup.bindCheckable(myItem) |
| } |
| } |
| \endcode |
| |
| The example above shows the minimum code necessary to add ExclusiveGroup support to any item. |
| */ |
| |
| /*! |
| \qmlproperty object ExclusiveGroup::current |
| |
| The currently selected object. Defaults to the first checked object bound to the ExclusiveGroup. |
| If there is none, then it defaults to \c null. |
| */ |
| |
| /*! |
| \qmlmethod void ExclusiveGroup::bindCheckable(object) |
| |
| Registers \a object to the exclusive group. |
| |
| You should only need to call this function when creating a component you want to be compatible with ExclusiveGroup. |
| |
| \sa ExclusiveGroup::unbindCheckable() |
| */ |
| |
| /*! |
| \qmlmethod void ExclusiveGroup::unbindCheckable(object) |
| |
| Unregisters \a object from the exclusive group. |
| |
| You should only need to call this function when creating a component you want to be compatible with ExclusiveGroup. |
| |
| \sa ExclusiveGroup::bindCheckable() |
| */ |
| |
| QQuickExclusiveGroup1::QQuickExclusiveGroup1(QObject *parent) |
| : QObject(parent), m_current(0) |
| { |
| int index = metaObject()->indexOfMethod("updateCurrent()"); |
| m_updateCurrentMethod = metaObject()->method(index); |
| } |
| |
| QQmlListProperty<QQuickAction1> QQuickExclusiveGroup1::actions() |
| { |
| return QQmlListProperty<QQuickAction1>(this, 0, &QQuickExclusiveGroup1::append_actions, 0, 0, 0); |
| } |
| |
| void QQuickExclusiveGroup1::append_actions(QQmlListProperty<QQuickAction1> *list, QQuickAction1 *action) |
| { |
| if (QQuickExclusiveGroup1 *eg = qobject_cast<QQuickExclusiveGroup1 *>(list->object)) |
| action->setExclusiveGroup(eg); |
| } |
| |
| void QQuickExclusiveGroup1::setCurrent(QObject * o) |
| { |
| if (m_current == o) |
| return; |
| |
| if (m_current) |
| m_current->setProperty(CHECKED_PROPERTY, QVariant(false)); |
| m_current = o; |
| if (m_current) |
| m_current->setProperty(CHECKED_PROPERTY, QVariant(true)); |
| emit currentChanged(); |
| } |
| |
| void QQuickExclusiveGroup1::updateCurrent() |
| { |
| QObject *checkable = sender(); |
| if (isChecked(checkable)) |
| setCurrent(checkable); |
| } |
| |
| void QQuickExclusiveGroup1::bindCheckable(QObject *o) |
| { |
| for (const char **signalName = checkableSignals; *signalName; signalName++) { |
| int signalIndex = o->metaObject()->indexOfSignal(*signalName); |
| if (signalIndex != -1) { |
| QMetaMethod signalMethod = o->metaObject()->method(signalIndex); |
| connect(o, signalMethod, this, m_updateCurrentMethod, Qt::UniqueConnection); |
| connect(o, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*)), Qt::UniqueConnection); |
| if (!m_current && isChecked(o)) |
| setCurrent(o); |
| return; |
| } |
| } |
| |
| qWarning() << "QQuickExclusiveGroup1::bindCheckable(): Cannot bind to" << o; |
| } |
| |
| void QQuickExclusiveGroup1::unbindCheckable(QObject *o) |
| { |
| if (m_current == o) |
| setCurrent(0); |
| for (const char **signalName = checkableSignals; *signalName; signalName++) { |
| int signalIndex = o->metaObject()->indexOfSignal(*signalName); |
| if (signalIndex != -1) { |
| QMetaMethod signalMethod = o->metaObject()->method(signalIndex); |
| if (disconnect(o, signalMethod, this, m_updateCurrentMethod)) { |
| disconnect(o, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*))); |
| break; |
| } |
| } |
| } |
| } |
| |
| QT_END_NAMESPACE |