diff options
Diffstat (limited to 'src/quicktemplates2/qquickaction.cpp')
-rw-r--r-- | src/quicktemplates2/qquickaction.cpp | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/src/quicktemplates2/qquickaction.cpp b/src/quicktemplates2/qquickaction.cpp new file mode 100644 index 00000000..fb6d7c3e --- /dev/null +++ b/src/quicktemplates2/qquickaction.cpp @@ -0,0 +1,553 @@ +/**************************************************************************** +** +** 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 "qquickaction_p.h" +#include "qquickaction_p_p.h" +#include "qquickactiongroup_p.h" +#include "qquickshortcutcontext_p_p.h" + +#include <QtGui/qevent.h> +#include <QtGui/private/qshortcutmap_p.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtQuick/private/qquickitem_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Action + \inherits QtObject + \instantiates QQuickAction + \inqmlmodule QtQuick.Controls + \since 5.10 + \ingroup utilities + \brief Abstract user interface action. + + Action represents an abstract user interface action that can have shortcuts + and can be assigned to menu items and toolbar buttons. + + Actions may contain \l text, an \l icon, and a \l shortcut. Actions are normally + \l triggered by the user via menu items, toolbar buttons, or keyboard shortcuts. + A \l checkable Action toggles its \l checked state when triggered. + + \snippet qtquickcontrols2-action.qml action + + Action is commonly used to implement application commands that can be invoked + via menu items, toolbar buttons, and keyboard shortcuts. Since the user expects + the commands to be performed in the same way, regardless of the user interface + used, it is useful to represent the commands as shareable actions. + + Action can be also used to separate the logic and the visual presentation. For + example, when declaring buttons and menu items in \c .ui.qml files, actions can + be declared elsewhere and assigned from the outside. + + \snippet qtquickcontrols2-action.qml toolbutton + + When an action is paired with buttons and menu items, the \c enabled, \c checkable, + and \c checked states are synced automatically. For example, in a word processor, + if the user clicks a "Bold" toolbar button, the "Bold" menu item will automatically + be checked. Buttons and menu items get their \c text and \c icon from the action by + default. An action-specific \c text or \c icon can be overridden for a specific + control by specifying \c text or \c icon directly on the control. + + \snippet qtquickcontrols2-action.qml menuitem + + Since Action presents a user interface action, it is intended to be assigned to + a \l MenuItem, \l ToolButton, or any other control that inherits \l AbstractButton. + For keyboard shortcuts, the simpler \l Shortcut type is more appropriate. + + \sa MenuItem, ToolButton, Shortcut +*/ + +/*! + \qmlsignal QtQuick.Controls::Action::toggled(QtObject source) + + This signal is emitted when the action is toggled. The \a source argument + identifies the object that toggled the action. + + For example, if the action is assigned to a menu item and a toolbar button, the + action is toggled when the control is toggled, the shortcut is activated, or + when \l toggle() is called directly. +*/ + +/*! + \qmlsignal QtQuick.Controls::Action::triggered(QtObject source) + + This signal is emitted when the action is triggered. The \a source argument + identifies the object that triggered the action. + + For example, if the action is assigned to a menu item and a toolbar button, the + action is triggered when the control is clicked, the shortcut is activated, or + when \l trigger() is called directly. +*/ + +static QKeySequence variantToKeySequence(const QVariant &var) +{ + if (var.type() == QVariant::Int) + return QKeySequence(static_cast<QKeySequence::StandardKey>(var.toInt())); + return QKeySequence::fromString(var.toString()); +} + +QQuickActionPrivate::ShortcutEntry::ShortcutEntry(QObject *target) + : m_shortcutId(0), + m_target(target) +{ +} + +QQuickActionPrivate::ShortcutEntry::~ShortcutEntry() +{ + ungrab(); +} + +QObject *QQuickActionPrivate::ShortcutEntry::target() const +{ + return m_target; +} + +int QQuickActionPrivate::ShortcutEntry::shortcutId() const +{ + return m_shortcutId; +} + +void QQuickActionPrivate::ShortcutEntry::grab(const QKeySequence &shortcut, bool enabled) +{ + if (shortcut.isEmpty()) + return; + + Qt::ShortcutContext context = Qt::WindowShortcut; // TODO + m_shortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(m_target, shortcut, context, QQuickShortcutContext::matcher); + + if (!enabled) + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(false, m_shortcutId, m_target); +} + +void QQuickActionPrivate::ShortcutEntry::ungrab() +{ + if (!m_shortcutId) + return; + + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_shortcutId, m_target); + m_shortcutId = 0; +} + +void QQuickActionPrivate::ShortcutEntry::setEnabled(bool enabled) +{ + if (!m_shortcutId) + return; + + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, m_shortcutId, m_target); +} + +QQuickActionPrivate::QQuickActionPrivate() + : explicitEnabled(false), + enabled(true), + checked(false), + checkable(false), + defaultShortcutEntry(nullptr), + group(nullptr) +{ +} + +QVariant QQuickActionPrivate::shortcut() const +{ + return vshortcut; +} + +void QQuickActionPrivate::setShortcut(const QVariant &var) +{ + Q_Q(QQuickAction); + if (vshortcut == var) + return; + + defaultShortcutEntry->ungrab(); + for (QQuickActionPrivate::ShortcutEntry *entry : qAsConst(shortcutEntries)) + entry->ungrab(); + + vshortcut = var.toString(); + keySequence = variantToKeySequence(var); + + defaultShortcutEntry->grab(keySequence, enabled); + for (QQuickActionPrivate::ShortcutEntry *entry : qAsConst(shortcutEntries)) + entry->grab(keySequence, enabled); + + emit q->shortcutChanged(keySequence); +} + +void QQuickActionPrivate::setEnabled(bool enable) +{ + Q_Q(QQuickAction); + if (enabled == enable) + return; + + enabled = enable; + + defaultShortcutEntry->setEnabled(enable); + for (QQuickActionPrivate::ShortcutEntry *entry : qAsConst(shortcutEntries)) + entry->setEnabled(enable); + + emit q->enabledChanged(enable); +} + +bool QQuickActionPrivate::watchItem(QQuickItem *item) +{ + Q_Q(QQuickAction); + if (!item) + return false; + + item->installEventFilter(q); + QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed); + return true; +} + +bool QQuickActionPrivate::unwatchItem(QQuickItem *item) +{ + Q_Q(QQuickAction); + if (!item) + return false; + + item->removeEventFilter(q); + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed); + return true; +} + +void QQuickActionPrivate::registerItem(QQuickItem *item) +{ + if (!watchItem(item)) + return; + + QQuickActionPrivate::ShortcutEntry *entry = new QQuickActionPrivate::ShortcutEntry(item); + if (item->isVisible()) + entry->grab(keySequence, enabled); + shortcutEntries += entry; + + updateDefaultShortcutEntry(); +} + +void QQuickActionPrivate::unregisterItem(QQuickItem *item) +{ + QQuickActionPrivate::ShortcutEntry *entry = findShortcutEntry(item); + if (!entry || !unwatchItem(item)) + return; + + shortcutEntries.removeOne(entry); + delete entry; + + updateDefaultShortcutEntry(); +} + +void QQuickActionPrivate::itemVisibilityChanged(QQuickItem *item) +{ + QQuickActionPrivate::ShortcutEntry *entry = findShortcutEntry(item); + if (!entry) + return; + + if (item->isVisible()) + entry->grab(keySequence, enabled); + else + entry->ungrab(); + + updateDefaultShortcutEntry(); +} + +void QQuickActionPrivate::itemDestroyed(QQuickItem *item) +{ + unregisterItem(item); +} + +bool QQuickActionPrivate::handleShortcutEvent(QObject *object, QShortcutEvent *event) +{ + Q_Q(QQuickAction); + if (event->key() != keySequence) + return false; + + QQuickActionPrivate::ShortcutEntry *entry = findShortcutEntry(object); + if (!entry || event->shortcutId() != entry->shortcutId()) + return false; + + q->trigger(entry->target()); + return true; +} + +QQuickActionPrivate::ShortcutEntry *QQuickActionPrivate::findShortcutEntry(QObject *target) const +{ + Q_Q(const QQuickAction); + if (target == q) + return defaultShortcutEntry; + for (QQuickActionPrivate::ShortcutEntry *entry : shortcutEntries) { + if (entry->target() == target) + return entry; + } + return nullptr; +} + +void QQuickActionPrivate::updateDefaultShortcutEntry() +{ + bool hasActiveShortcutEntries = false; + for (QQuickActionPrivate::ShortcutEntry *entry : qAsConst(shortcutEntries)) { + if (entry->shortcutId()) { + hasActiveShortcutEntries = true; + break; + } + } + + if (hasActiveShortcutEntries) + defaultShortcutEntry->ungrab(); + else if (!defaultShortcutEntry->shortcutId()) + defaultShortcutEntry->grab(keySequence, enabled); +} + +QQuickAction::QQuickAction(QObject *parent) + : QObject(*(new QQuickActionPrivate), parent) +{ + Q_D(QQuickAction); + d->defaultShortcutEntry = new QQuickActionPrivate::ShortcutEntry(this); +} + +QQuickAction::~QQuickAction() +{ + Q_D(QQuickAction); + if (d->group) + d->group->removeAction(this); + + for (QQuickActionPrivate::ShortcutEntry *entry : qAsConst(d->shortcutEntries)) + d->unwatchItem(qobject_cast<QQuickItem *>(entry->target())); + + qDeleteAll(d->shortcutEntries); + delete d->defaultShortcutEntry; +} + +/*! + \qmlproperty string QtQuick.Controls::Action::text + + This property holds a textual description of the action. +*/ +QString QQuickAction::text() const +{ + Q_D(const QQuickAction); + return d->text; +} + +void QQuickAction::setText(const QString &text) +{ + Q_D(QQuickAction); + if (d->text == text) + return; + + d->text = text; + emit textChanged(text); +} + +/*! + \qmlpropertygroup QtQuick.Controls::Action::icon + \qmlproperty string QtQuick.Controls::Action::icon.name + \qmlproperty url QtQuick.Controls::Action::icon.source + \qmlproperty int QtQuick.Controls::Action::icon.width + \qmlproperty int QtQuick.Controls::Action::icon.height + \qmlproperty color QtQuick.Controls::Action::icon.color + + \include qquickicon.qdocinc grouped-properties +*/ +QQuickIcon QQuickAction::icon() const +{ + Q_D(const QQuickAction); + return d->icon; +} + +void QQuickAction::setIcon(const QQuickIcon &icon) +{ + Q_D(QQuickAction); + if (d->icon == icon) + return; + + d->icon = icon; + emit iconChanged(icon); +} + +/*! + \qmlproperty bool QtQuick.Controls::Action::enabled + + This property holds whether the action is enabled. The default value is \c true. +*/ +bool QQuickAction::isEnabled() const +{ + Q_D(const QQuickAction); + return d->enabled && (!d->group || d->group->isEnabled()); +} + +void QQuickAction::setEnabled(bool enabled) +{ + Q_D(QQuickAction); + d->explicitEnabled = true; + d->setEnabled(enabled); +} + +void QQuickAction::resetEnabled() +{ + Q_D(QQuickAction); + if (!d->explicitEnabled) + return; + + d->explicitEnabled = false; + d->setEnabled(true); +} + +/*! + \qmlproperty bool QtQuick.Controls::Action::checked + + This property holds whether the action is checked. + + \sa checkable +*/ +bool QQuickAction::isChecked() const +{ + Q_D(const QQuickAction); + return d->checked; +} + +void QQuickAction::setChecked(bool checked) +{ + Q_D(QQuickAction); + if (d->checked == checked) + return; + + d->checked = checked; + emit checkedChanged(checked); +} + +/*! + \qmlproperty bool QtQuick.Controls::Action::checkable + + This property holds whether the action is checkable. The default value is \c false. + + A checkable action toggles between checked (on) and unchecked (off) when triggered. + + \sa checked +*/ +bool QQuickAction::isCheckable() const +{ + Q_D(const QQuickAction); + return d->checkable; +} + +void QQuickAction::setCheckable(bool checkable) +{ + Q_D(QQuickAction); + if (d->checkable == checkable) + return; + + d->checkable = checkable; + emit checkableChanged(checkable); +} + +/*! + \qmlproperty keysequence QtQuick.Controls::Action::shortcut + + This property holds the action's shortcut. The key sequence can be set + to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, + or it can be described with a string containing a sequence of up to four + key presses that are needed to trigger the shortcut. + + \code + Action { + sequence: "Ctrl+E,Ctrl+W" + onTriggered: edit.wrapMode = TextEdit.Wrap + } + \endcode +*/ +QKeySequence QQuickAction::shortcut() const +{ + Q_D(const QQuickAction); + return d->keySequence; +} + +void QQuickAction::setShortcut(const QKeySequence &shortcut) +{ + Q_D(QQuickAction); + d->setShortcut(shortcut.toString()); +} + +/*! + \qmlmethod void QtQuick.Controls::Action::toggle(QtObject source = null) + + Toggles the action and emits \l toggled() if enabled, with an optional \a source object defined. +*/ +void QQuickAction::toggle(QObject *source) +{ + Q_D(QQuickAction); + if (!d->enabled) + return; + + if (d->checkable) + setChecked(!d->checked); + + emit toggled(source); +} + +/*! + \qmlmethod void QtQuick.Controls::Action::trigger(QtObject source = null) + + Triggers the action and emits \l triggered() if enabled, with an optional \a source object defined. +*/ +void QQuickAction::trigger(QObject *source) +{ + Q_D(QQuickAction); + if (!d->enabled) + return; + + if (d->checkable) + toggle(source); + + emit triggered(source); +} + +bool QQuickAction::event(QEvent *event) +{ + Q_D(QQuickAction); + if (event->type() != QEvent::Shortcut) + return QObject::event(event); + return d->handleShortcutEvent(this, static_cast<QShortcutEvent *>(event)); +} + +bool QQuickAction::eventFilter(QObject *object, QEvent *event) +{ + Q_D(QQuickAction); + if (event->type() != QEvent::Shortcut) + return false; + return d->handleShortcutEvent(object, static_cast<QShortcutEvent *>(event)); +} + +QT_END_NAMESPACE + +#include "moc_qquickaction_p.cpp" |