/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** 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 http://www.qt.io/terms-conditions. For further ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qquickactiongroup_p.h" #include #include #include #include #include "qquickaction_p.h" #include "qquickaction_p_p.h" QT_BEGIN_NAMESPACE /*! \qmltype ActionGroup \inherits QtObject //! \instantiates QQuickActionGroup \inqmlmodule QtQuick.Controls \since 5.10 \ingroup utilities \brief Groups actions together. ActionGroup is a non-visual group of actions. A mutually \l exclusive action group is used with actions where only one of the options can be selected at a time. The most straight-forward way to use ActionGroup is to declare actions as children of the group. \code ActionGroup { id: alignmentGroup Action { checked: true checkable: true text: qsTr("Left") } Action { checkable: true text: qsTr("Center") } Action { checkable: true text: qsTr("Right") } } \endcode Alternatively, the \l group attached property allows declaring the actions elsewhere and assigning them to a specific group. \code ActionGroup { id: alignmentGroup } Action { checked: true checkable: true text: qsTr("Left") ActionGroup.group: alignmentGroup } Action { checkable: true text: qsTr("Center") ActionGroup.group: alignmentGroup } Action { checkable: true text: qsTr("Right") ActionGroup.group: alignmentGroup } \endcode More advanced use cases can be handled using the \c addAction() and \c removeAction() methods. \sa Action, ButtonGroup */ /*! \qmlsignal QtQuick.Controls::ActionGroup::triggered(Action action) This signal is emitted when an \a action in the group has been triggered. This signal is convenient for implementing a common signal handler for all actions in the same group. \code ActionGroup { onTriggered: console.log("triggered:", action.text) Action { text: "First" } Action { text: "Second" } Action { text: "Third" } } \endcode \sa Action::triggered() */ class QQuickActionGroupPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQuickActionGroup) public: void clear(); void actionTriggered(); void _q_updateCurrent(); static bool changeEnabled(QQuickAction *action, bool enabled); static void actions_append(QQmlListProperty *prop, QQuickAction *obj); static int actions_count(QQmlListProperty *prop); static QQuickAction *actions_at(QQmlListProperty *prop, int index); static void actions_clear(QQmlListProperty *prop); bool enabled = true; bool exclusive = true; QPointer checkedAction; QList actions; }; void QQuickActionGroupPrivate::clear() { for (QQuickAction *action : qAsConst(actions)) { QQuickActionPrivate::get(action)->group = nullptr; QObjectPrivate::disconnect(action, &QQuickAction::triggered, this, &QQuickActionGroupPrivate::actionTriggered); QObjectPrivate::disconnect(action, &QQuickAction::checkedChanged, this, &QQuickActionGroupPrivate::_q_updateCurrent); } actions.clear(); } void QQuickActionGroupPrivate::actionTriggered() { Q_Q(QQuickActionGroup); QQuickAction *action = qobject_cast(q->sender()); if (action) emit q->triggered(action); } void QQuickActionGroupPrivate::_q_updateCurrent() { Q_Q(QQuickActionGroup); if (!exclusive) return; QQuickAction *action = qobject_cast(q->sender()); if (action && action->isChecked()) q->setCheckedAction(action); else if (!actions.contains(checkedAction)) q->setCheckedAction(nullptr); } bool QQuickActionGroupPrivate::changeEnabled(QQuickAction *action, bool enabled) { return action->isEnabled() != enabled && (!enabled || !QQuickActionPrivate::get(action)->explicitEnabled); } void QQuickActionGroupPrivate::actions_append(QQmlListProperty *prop, QQuickAction *obj) { QQuickActionGroup *q = static_cast(prop->object); q->addAction(obj); } int QQuickActionGroupPrivate::actions_count(QQmlListProperty *prop) { QQuickActionGroupPrivate *p = static_cast(prop->data); return p->actions.count(); } QQuickAction *QQuickActionGroupPrivate::actions_at(QQmlListProperty *prop, int index) { QQuickActionGroupPrivate *p = static_cast(prop->data); return p->actions.value(index); } void QQuickActionGroupPrivate::actions_clear(QQmlListProperty *prop) { QQuickActionGroupPrivate *p = static_cast(prop->data); if (!p->actions.isEmpty()) { p->clear(); QQuickActionGroup *q = static_cast(prop->object); // QTBUG-52358: don't clear the checked action immediately QMetaObject::invokeMethod(q, "_q_updateCurrent", Qt::QueuedConnection); emit q->actionsChanged(); } } QQuickActionGroup::QQuickActionGroup(QObject *parent) : QObject(*(new QQuickActionGroupPrivate), parent) { } QQuickActionGroup::~QQuickActionGroup() { Q_D(QQuickActionGroup); d->clear(); } QQuickActionGroupAttached *QQuickActionGroup::qmlAttachedProperties(QObject *object) { return new QQuickActionGroupAttached(object); } /*! \qmlproperty Action QtQuick.Controls::ActionGroup::checkedAction This property holds the currently selected action in an exclusive group, or \c null if there is none or the group is non-exclusive. By default, it is the first checked action added to an exclusive action group. \sa exclusive */ QQuickAction *QQuickActionGroup::checkedAction() const { Q_D(const QQuickActionGroup); return d->checkedAction; } void QQuickActionGroup::setCheckedAction(QQuickAction *checkedAction) { Q_D(QQuickActionGroup); if (d->checkedAction == checkedAction) return; if (d->checkedAction) d->checkedAction->setChecked(false); d->checkedAction = checkedAction; if (checkedAction) checkedAction->setChecked(true); emit checkedActionChanged(); } /*! \qmlproperty list QtQuick.Controls::ActionGroup::actions \default This property holds the list of actions in the group. \sa group */ QQmlListProperty QQuickActionGroup::actions() { Q_D(QQuickActionGroup); return QQmlListProperty(this, d, QQuickActionGroupPrivate::actions_append, QQuickActionGroupPrivate::actions_count, QQuickActionGroupPrivate::actions_at, QQuickActionGroupPrivate::actions_clear); } /*! \qmlproperty bool QtQuick.Controls::ActionGroup::exclusive This property holds whether the action group is exclusive. The default value is \c true. If this property is \c true, then only one action in the group can be checked at any given time. The user can trigger any action to check it, and that action will replace the existing one as the checked action in the group. In an exclusive group, the user cannot uncheck the currently checked action by triggering it; instead, another action in the group must be triggered to set the new checked action for that group. In a non-exclusive group, checking and unchecking actions does not affect the other actions in the group. Furthermore, the value of the \l checkedAction property is \c null. */ bool QQuickActionGroup::isExclusive() const { Q_D(const QQuickActionGroup); return d->exclusive; } void QQuickActionGroup::setExclusive(bool exclusive) { Q_D(QQuickActionGroup); if (d->exclusive == exclusive) return; d->exclusive = exclusive; emit exclusiveChanged(); } /*! \qmlproperty bool QtQuick.Controls::ActionGroup::enabled This property holds whether the action group is enabled. The default value is \c true. If this property is \c false, then all actions in the group are disabled. If this property is \c true, all actions in the group are enabled, unless explicitly disabled. */ bool QQuickActionGroup::isEnabled() const { Q_D(const QQuickActionGroup); return d->enabled; } void QQuickActionGroup::setEnabled(bool enabled) { Q_D(QQuickActionGroup); if (d->enabled == enabled) return; for (QQuickAction *action : qAsConst(d->actions)) { if (d->changeEnabled(action, enabled)) emit action->enabledChanged(enabled); } d->enabled = enabled; emit enabledChanged(); } /*! \qmlmethod void QtQuick.Controls::ActionGroup::addAction(Action action) Adds an \a action to the action group. \note Manually adding objects to a action group is typically unnecessary. The \l actions property and the \l group attached property provide a convenient and declarative syntax. \sa actions, group */ void QQuickActionGroup::addAction(QQuickAction *action) { Q_D(QQuickActionGroup); if (!action || d->actions.contains(action)) return; const bool enabledChange = d->changeEnabled(action, d->enabled); QQuickActionPrivate::get(action)->group = this; QObjectPrivate::connect(action, &QQuickAction::triggered, d, &QQuickActionGroupPrivate::actionTriggered); QObjectPrivate::connect(action, &QQuickAction::checkedChanged, d, &QQuickActionGroupPrivate::_q_updateCurrent); if (d->exclusive && action->isChecked()) setCheckedAction(action); if (enabledChange) emit action->enabledChanged(action->isEnabled()); d->actions.append(action); emit actionsChanged(); } /*! \qmlmethod void QtQuick.Controls::ActionGroup::removeAction(Action action) Removes an \a action from the action group. \note Manually removing objects from a action group is typically unnecessary. The \l actions property and the \l group attached property provide a convenient and declarative syntax. \sa actions, group */ void QQuickActionGroup::removeAction(QQuickAction *action) { Q_D(QQuickActionGroup); if (!action || !d->actions.contains(action)) return; const bool enabledChange = d->changeEnabled(action, d->enabled); QQuickActionPrivate::get(action)->group = nullptr; QObjectPrivate::disconnect(action, &QQuickAction::triggered, d, &QQuickActionGroupPrivate::actionTriggered); QObjectPrivate::disconnect(action, &QQuickAction::checkedChanged, d, &QQuickActionGroupPrivate::_q_updateCurrent); if (d->checkedAction == action) setCheckedAction(nullptr); if (enabledChange) emit action->enabledChanged(action->isEnabled()); d->actions.removeOne(action); emit actionsChanged(); } class QQuickActionGroupAttachedPrivate : public QObjectPrivate { public: QQuickActionGroup *group = nullptr; }; QQuickActionGroupAttached::QQuickActionGroupAttached(QObject *parent) : QObject(*(new QQuickActionGroupAttachedPrivate), parent) { } /*! \qmlattachedproperty ActionGroup QtQuick.Controls::ActionGroup::group This property attaches an action to an action group. \code ActionGroup { id: group } Action { checked: true text: qsTr("Option A") ActionGroup.group: group } Action { text: qsTr("Option B") ActionGroup.group: group } \endcode \sa actions */ QQuickActionGroup *QQuickActionGroupAttached::group() const { Q_D(const QQuickActionGroupAttached); return d->group; } void QQuickActionGroupAttached::setGroup(QQuickActionGroup *group) { Q_D(QQuickActionGroupAttached); if (d->group == group) return; if (d->group) d->group->removeAction(qobject_cast(parent())); d->group = group; if (group) group->addAction(qobject_cast(parent())); emit groupChanged(); } QT_END_NAMESPACE #include "moc_qquickactiongroup_p.cpp"