| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtQuick 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 "qquickshortcut_p.h" |
| |
| #include <QtQuick/qquickitem.h> |
| #include <QtQuick/qquickwindow.h> |
| #include <QtQuick/qquickrendercontrol.h> |
| #include <QtQuick/private/qtquickglobal_p.h> |
| #include <QtGui/private/qguiapplication_p.h> |
| |
| /*! |
| \qmltype Shortcut |
| \instantiates QQuickShortcut |
| \inqmlmodule QtQuick |
| \since 5.5 |
| \ingroup qtquick-input |
| \brief Provides keyboard shortcuts. |
| |
| The Shortcut type provides a way of handling keyboard shortcuts. The shortcut 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 \l{Shortcut::activated}{activate} the shortcut. |
| |
| \qml |
| Item { |
| id: view |
| |
| property int currentIndex |
| |
| Shortcut { |
| sequence: StandardKey.NextChild |
| onActivated: view.currentIndex++ |
| } |
| } |
| \endqml |
| |
| It is also possible to set multiple shortcut \l sequences, so that the shortcut |
| can be \l activated via several different sequences of key presses. |
| |
| \sa Keys, {Keys::}{shortcutOverride()} |
| */ |
| |
| /*! \qmlsignal QtQuick::Shortcut::activated() |
| |
| This signal is emitted when the shortcut is activated. |
| |
| The corresponding handler is \c onActivated. |
| */ |
| |
| /*! \qmlsignal QtQuick::Shortcut::activatedAmbiguously() |
| |
| This signal is emitted when the shortcut is activated ambigously, |
| meaning that it matches the start of more than one shortcut. |
| |
| The corresponding handler is \c onActivatedAmbiguously. |
| */ |
| |
| static bool qQuickShortcutContextMatcher(QObject *obj, Qt::ShortcutContext context) |
| { |
| switch (context) { |
| case Qt::ApplicationShortcut: |
| return true; |
| case Qt::WindowShortcut: |
| while (obj && !obj->isWindowType()) { |
| obj = obj->parent(); |
| if (QQuickItem *item = qobject_cast<QQuickItem *>(obj)) |
| obj = item->window(); |
| } |
| if (QWindow *renderWindow = QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow *>(obj))) |
| obj = renderWindow; |
| return obj && obj == QGuiApplication::focusWindow(); |
| default: |
| return false; |
| } |
| } |
| |
| typedef bool (*ContextMatcher)(QObject *, Qt::ShortcutContext); |
| |
| Q_GLOBAL_STATIC_WITH_ARGS(ContextMatcher, ctxMatcher, (qQuickShortcutContextMatcher)) |
| |
| Q_QUICK_PRIVATE_EXPORT ContextMatcher qt_quick_shortcut_context_matcher() |
| { |
| return *ctxMatcher(); |
| } |
| |
| Q_QUICK_PRIVATE_EXPORT void qt_quick_set_shortcut_context_matcher(ContextMatcher matcher) |
| { |
| if (!ctxMatcher.isDestroyed()) |
| *ctxMatcher() = matcher; |
| } |
| |
| QT_BEGIN_NAMESPACE |
| |
| static QKeySequence valueToKeySequence(const QVariant &value) |
| { |
| if (value.type() == QVariant::Int) |
| return QKeySequence(static_cast<QKeySequence::StandardKey>(value.toInt())); |
| return QKeySequence::fromString(value.toString()); |
| } |
| |
| QQuickShortcut::QQuickShortcut(QObject *parent) : QObject(parent), |
| m_enabled(true), m_completed(false), m_autorepeat(true), m_context(Qt::WindowShortcut) |
| { |
| } |
| |
| QQuickShortcut::~QQuickShortcut() |
| { |
| ungrabShortcut(m_shortcut); |
| for (Shortcut &shortcut : m_shortcuts) |
| ungrabShortcut(shortcut); |
| } |
| |
| /*! |
| \qmlproperty keysequence QtQuick::Shortcut::sequence |
| |
| This property holds the shortcut's key sequence. 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 \l{Shortcut::activated}{activate} the shortcut. |
| |
| The default value is an empty key sequence. |
| |
| \qml |
| Shortcut { |
| sequence: "Ctrl+E,Ctrl+W" |
| onActivated: edit.wrapMode = TextEdit.Wrap |
| } |
| \endqml |
| |
| \sa sequences |
| */ |
| QVariant QQuickShortcut::sequence() const |
| { |
| return m_shortcut.userValue; |
| } |
| |
| void QQuickShortcut::setSequence(const QVariant &value) |
| { |
| if (value == m_shortcut.userValue) |
| return; |
| |
| QKeySequence keySequence = valueToKeySequence(value); |
| |
| ungrabShortcut(m_shortcut); |
| m_shortcut.userValue = value; |
| m_shortcut.keySequence = keySequence; |
| grabShortcut(m_shortcut, m_context); |
| emit sequenceChanged(); |
| } |
| |
| /*! |
| \qmlproperty list<keysequence> QtQuick::Shortcut::sequences |
| \since 5.9 |
| |
| This property holds multiple key sequences for the shortcut. The key sequences |
| can be set to one of the \l{QKeySequence::StandardKey}{standard keyboard shortcuts}, |
| or they can be described with strings containing sequences of up to four key |
| presses that are needed to \l{Shortcut::activated}{activate} the shortcut. |
| |
| \qml |
| Shortcut { |
| sequences: [StandardKey.Cut, "Ctrl+X", "Shift+Del"] |
| onActivated: edit.cut() |
| } |
| \endqml |
| */ |
| QVariantList QQuickShortcut::sequences() const |
| { |
| QVariantList values; |
| for (const Shortcut &shortcut : m_shortcuts) |
| values += shortcut.userValue; |
| return values; |
| } |
| |
| void QQuickShortcut::setSequences(const QVariantList &values) |
| { |
| QVector<Shortcut> remainder = m_shortcuts.mid(values.count()); |
| m_shortcuts.resize(values.count()); |
| |
| bool changed = !remainder.isEmpty(); |
| for (int i = 0; i < values.count(); ++i) { |
| const QVariant &value = values.at(i); |
| Shortcut& shortcut = m_shortcuts[i]; |
| if (value == shortcut.userValue) |
| continue; |
| |
| QKeySequence keySequence = valueToKeySequence(value); |
| |
| ungrabShortcut(shortcut); |
| shortcut.userValue = value; |
| shortcut.keySequence = keySequence; |
| grabShortcut(shortcut, m_context); |
| |
| changed = true; |
| } |
| |
| if (changed) |
| emit sequencesChanged(); |
| } |
| |
| /*! |
| \qmlproperty string QtQuick::Shortcut::nativeText |
| \since 5.6 |
| |
| This property provides the shortcut's key sequence as a platform specific |
| string. This means that it will be shown translated, and on \macos it will |
| resemble a key sequence from the menu bar. It is best to display this text |
| to the user (for example, on a tooltip). |
| |
| \sa sequence, portableText |
| */ |
| QString QQuickShortcut::nativeText() const |
| { |
| return m_shortcut.keySequence.toString(QKeySequence::NativeText); |
| } |
| |
| /*! |
| \qmlproperty string QtQuick::Shortcut::portableText |
| \since 5.6 |
| |
| This property provides the shortcut's key sequence as a string in a |
| "portable" format, suitable for reading and writing to a file. In many |
| cases, it will look similar to the native text on Windows and X11. |
| |
| \sa sequence, nativeText |
| */ |
| QString QQuickShortcut::portableText() const |
| { |
| return m_shortcut.keySequence.toString(QKeySequence::PortableText); |
| } |
| |
| /*! |
| \qmlproperty bool QtQuick::Shortcut::enabled |
| |
| This property holds whether the shortcut is enabled. |
| |
| The default value is \c true. |
| */ |
| bool QQuickShortcut::isEnabled() const |
| { |
| return m_enabled; |
| } |
| |
| void QQuickShortcut::setEnabled(bool enabled) |
| { |
| if (enabled == m_enabled) |
| return; |
| |
| setEnabled(m_shortcut, enabled); |
| for (Shortcut &shortcut : m_shortcuts) |
| setEnabled(shortcut, enabled); |
| |
| m_enabled = enabled; |
| emit enabledChanged(); |
| } |
| |
| /*! |
| \qmlproperty bool QtQuick::Shortcut::autoRepeat |
| |
| This property holds whether the shortcut can auto repeat. |
| |
| The default value is \c true. |
| */ |
| bool QQuickShortcut::autoRepeat() const |
| { |
| return m_autorepeat; |
| } |
| |
| void QQuickShortcut::setAutoRepeat(bool repeat) |
| { |
| if (repeat == m_autorepeat) |
| return; |
| |
| setAutoRepeat(m_shortcut, repeat); |
| for (Shortcut &shortcut : m_shortcuts) |
| setAutoRepeat(shortcut, repeat); |
| |
| m_autorepeat = repeat; |
| emit autoRepeatChanged(); |
| } |
| |
| /*! |
| \qmlproperty enumeration QtQuick::Shortcut::context |
| |
| This property holds the \l{Qt::ShortcutContext}{shortcut context}. |
| |
| Supported values are: |
| \list |
| \li \c Qt.WindowShortcut (default) - The shortcut is active when its parent item is in an active top-level window. |
| \li \c Qt.ApplicationShortcut - The shortcut is active when one of the application's windows are active. |
| \endlist |
| |
| \qml |
| Shortcut { |
| sequence: StandardKey.Quit |
| context: Qt.ApplicationShortcut |
| onActivated: Qt.quit() |
| } |
| \endqml |
| */ |
| Qt::ShortcutContext QQuickShortcut::context() const |
| { |
| return m_context; |
| } |
| |
| void QQuickShortcut::setContext(Qt::ShortcutContext context) |
| { |
| if (context == m_context) |
| return; |
| |
| ungrabShortcut(m_shortcut); |
| m_context = context; |
| grabShortcut(m_shortcut, context); |
| emit contextChanged(); |
| } |
| |
| void QQuickShortcut::classBegin() |
| { |
| } |
| |
| void QQuickShortcut::componentComplete() |
| { |
| m_completed = true; |
| grabShortcut(m_shortcut, m_context); |
| for (Shortcut &shortcut : m_shortcuts) |
| grabShortcut(shortcut, m_context); |
| } |
| |
| bool QQuickShortcut::event(QEvent *event) |
| { |
| if (m_enabled && event->type() == QEvent::Shortcut) { |
| QShortcutEvent *se = static_cast<QShortcutEvent *>(event); |
| bool match = m_shortcut.matches(se); |
| int i = 0; |
| while (!match && i < m_shortcuts.count()) |
| match |= m_shortcuts.at(i++).matches(se); |
| if (match) { |
| if (se->isAmbiguous()) |
| emit activatedAmbiguously(); |
| else |
| emit activated(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool QQuickShortcut::Shortcut::matches(QShortcutEvent *event) const |
| { |
| return event->shortcutId() == id && event->key() == keySequence; |
| } |
| |
| void QQuickShortcut::setEnabled(QQuickShortcut::Shortcut &shortcut, bool enabled) |
| { |
| if (shortcut.id) |
| QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, shortcut.id, this); |
| } |
| |
| void QQuickShortcut::setAutoRepeat(QQuickShortcut::Shortcut &shortcut, bool repeat) |
| { |
| if (shortcut.id) |
| QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(repeat, shortcut.id, this); |
| } |
| |
| void QQuickShortcut::grabShortcut(Shortcut &shortcut, Qt::ShortcutContext context) |
| { |
| if (m_completed && !shortcut.keySequence.isEmpty()) { |
| QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance(); |
| shortcut.id = pApp->shortcutMap.addShortcut(this, shortcut.keySequence, context, *ctxMatcher()); |
| if (!m_enabled) |
| pApp->shortcutMap.setShortcutEnabled(false, shortcut.id, this); |
| if (!m_autorepeat) |
| pApp->shortcutMap.setShortcutAutoRepeat(false, shortcut.id, this); |
| } |
| } |
| |
| void QQuickShortcut::ungrabShortcut(Shortcut &shortcut) |
| { |
| if (shortcut.id) { |
| QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(shortcut.id, this); |
| shortcut.id = 0; |
| } |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qquickshortcut_p.cpp" |