diff options
Diffstat (limited to 'src/quicktemplates2/qquickactiongroup.cpp')
-rw-r--r-- | src/quicktemplates2/qquickactiongroup.cpp | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/src/quicktemplates2/qquickactiongroup.cpp b/src/quicktemplates2/qquickactiongroup.cpp new file mode 100644 index 00000000..64895689 --- /dev/null +++ b/src/quicktemplates2/qquickactiongroup.cpp @@ -0,0 +1,480 @@ +/**************************************************************************** +** +** 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 <QtCore/private/qobject_p.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qvariant.h> +#include <QtQml/qqmlinfo.h> + +#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: + QQuickActionGroupPrivate() + : enabled(true), + exclusive(true), + checkedAction(nullptr) + { + } + + void clear(); + void actionTriggered(); + void _q_updateCurrent(); + + static bool changeEnabled(QQuickAction *action, bool enabled); + + static void actions_append(QQmlListProperty<QQuickAction> *prop, QQuickAction *obj); + static int actions_count(QQmlListProperty<QQuickAction> *prop); + static QQuickAction *actions_at(QQmlListProperty<QQuickAction> *prop, int index); + static void actions_clear(QQmlListProperty<QQuickAction> *prop); + + bool enabled; + bool exclusive; + QPointer<QQuickAction> checkedAction; + QVector<QQuickAction*> 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<QQuickAction*>(q->sender()); + if (action) + emit q->triggered(action); +} + +void QQuickActionGroupPrivate::_q_updateCurrent() +{ + Q_Q(QQuickActionGroup); + if (!exclusive) + return; + QQuickAction *action = qobject_cast<QQuickAction*>(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<QQuickAction> *prop, QQuickAction *obj) +{ + QQuickActionGroup *q = static_cast<QQuickActionGroup *>(prop->object); + q->addAction(obj); +} + +int QQuickActionGroupPrivate::actions_count(QQmlListProperty<QQuickAction> *prop) +{ + QQuickActionGroupPrivate *p = static_cast<QQuickActionGroupPrivate *>(prop->data); + return p->actions.count(); +} + +QQuickAction *QQuickActionGroupPrivate::actions_at(QQmlListProperty<QQuickAction> *prop, int index) +{ + QQuickActionGroupPrivate *p = static_cast<QQuickActionGroupPrivate *>(prop->data); + return p->actions.value(index); +} + +void QQuickActionGroupPrivate::actions_clear(QQmlListProperty<QQuickAction> *prop) +{ + QQuickActionGroupPrivate *p = static_cast<QQuickActionGroupPrivate *>(prop->data); + if (!p->actions.isEmpty()) { + p->clear(); + QQuickActionGroup *q = static_cast<QQuickActionGroup *>(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<Action> QtQuick.Controls::ActionGroup::actions + \default + + This property holds the list of actions in the group. + + \sa group +*/ +QQmlListProperty<QQuickAction> QQuickActionGroup::actions() +{ + Q_D(QQuickActionGroup); + return QQmlListProperty<QQuickAction>(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: + QQuickActionGroupAttachedPrivate() : group(nullptr) { } + + QQuickActionGroup *group; +}; + +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<QQuickAction*>(parent())); + d->group = group; + if (group) + group->addAction(qobject_cast<QQuickAction*>(parent())); + emit groupChanged(); +} + +QT_END_NAMESPACE + +#include "moc_qquickactiongroup_p.cpp" |