Shortcut: add support for multiple key sequences
[ChangeLog][QtQuick][Shortcut] Added support for multiple shortcut sequences. Previously it was possible to specify a single sequence that could consist of up to four key presses. Now it is possible to specify multiple sequences that can each consist of multiple key presses. Change-Id: Id12f25da2f352cc542ec776049d8e81593951d41 Reviewed-by: Robin Burchell <robin.burchell@viroteck.net> Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
e21a699dca
commit
a7fd83cd0c
|
@ -70,6 +70,9 @@
|
|||
}
|
||||
\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
|
||||
*/
|
||||
|
||||
|
@ -121,14 +124,23 @@ Q_QUICK_PRIVATE_EXPORT void qt_quick_set_shortcut_context_matcher(ContextMatcher
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QQuickShortcut::QQuickShortcut(QObject *parent) : QObject(parent), m_id(0),
|
||||
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();
|
||||
ungrabShortcut(m_shortcut);
|
||||
for (Shortcut &shortcut : m_shortcuts)
|
||||
ungrabShortcut(shortcut);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -147,30 +159,78 @@ QQuickShortcut::~QQuickShortcut()
|
|||
onActivated: edit.wrapMode = TextEdit.Wrap
|
||||
}
|
||||
\endqml
|
||||
|
||||
\sa sequences
|
||||
*/
|
||||
QVariant QQuickShortcut::sequence() const
|
||||
{
|
||||
return m_sequence;
|
||||
return m_shortcut.userValue;
|
||||
}
|
||||
|
||||
void QQuickShortcut::setSequence(const QVariant &sequence)
|
||||
void QQuickShortcut::setSequence(const QVariant &value)
|
||||
{
|
||||
if (sequence == m_sequence)
|
||||
if (value == m_shortcut.userValue)
|
||||
return;
|
||||
|
||||
QKeySequence shortcut;
|
||||
if (sequence.type() == QVariant::Int)
|
||||
shortcut = QKeySequence(static_cast<QKeySequence::StandardKey>(sequence.toInt()));
|
||||
else
|
||||
shortcut = QKeySequence::fromString(sequence.toString());
|
||||
QKeySequence keySequence = valueToKeySequence(value);
|
||||
|
||||
grabShortcut(shortcut, m_context);
|
||||
|
||||
m_sequence = sequence;
|
||||
m_shortcut = shortcut;
|
||||
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) {
|
||||
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
|
||||
|
@ -184,7 +244,7 @@ void QQuickShortcut::setSequence(const QVariant &sequence)
|
|||
*/
|
||||
QString QQuickShortcut::nativeText() const
|
||||
{
|
||||
return m_shortcut.toString(QKeySequence::NativeText);
|
||||
return m_shortcut.keySequence.toString(QKeySequence::NativeText);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -199,7 +259,7 @@ QString QQuickShortcut::nativeText() const
|
|||
*/
|
||||
QString QQuickShortcut::portableText() const
|
||||
{
|
||||
return m_shortcut.toString(QKeySequence::PortableText);
|
||||
return m_shortcut.keySequence.toString(QKeySequence::PortableText);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -219,8 +279,9 @@ void QQuickShortcut::setEnabled(bool enabled)
|
|||
if (enabled == m_enabled)
|
||||
return;
|
||||
|
||||
if (m_id)
|
||||
QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, m_id, this);
|
||||
setEnabled(m_shortcut, enabled);
|
||||
for (Shortcut &shortcut : m_shortcuts)
|
||||
setEnabled(shortcut, enabled);
|
||||
|
||||
m_enabled = enabled;
|
||||
emit enabledChanged();
|
||||
|
@ -243,8 +304,9 @@ void QQuickShortcut::setAutoRepeat(bool repeat)
|
|||
if (repeat == m_autorepeat)
|
||||
return;
|
||||
|
||||
if (m_id)
|
||||
QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(repeat, m_id, this);
|
||||
setAutoRepeat(m_shortcut, repeat);
|
||||
for (Shortcut &shortcut : m_shortcuts)
|
||||
setAutoRepeat(shortcut, repeat);
|
||||
|
||||
m_autorepeat = repeat;
|
||||
emit autoRepeatChanged();
|
||||
|
@ -279,9 +341,9 @@ void QQuickShortcut::setContext(Qt::ShortcutContext context)
|
|||
if (context == m_context)
|
||||
return;
|
||||
|
||||
grabShortcut(m_shortcut, context);
|
||||
|
||||
ungrabShortcut(m_shortcut);
|
||||
m_context = context;
|
||||
grabShortcut(m_shortcut, context);
|
||||
emit contextChanged();
|
||||
}
|
||||
|
||||
|
@ -293,13 +355,19 @@ 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);
|
||||
if (se->shortcutId() == m_id && se->key() == m_shortcut){
|
||||
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
|
||||
|
@ -310,25 +378,40 @@ bool QQuickShortcut::event(QEvent *event)
|
|||
return false;
|
||||
}
|
||||
|
||||
void QQuickShortcut::grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context)
|
||||
bool QQuickShortcut::Shortcut::matches(QShortcutEvent *event) const
|
||||
{
|
||||
ungrabShortcut();
|
||||
return event->shortcutId() == id && event->key() == keySequence;
|
||||
}
|
||||
|
||||
if (m_completed && !sequence.isEmpty()) {
|
||||
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();
|
||||
m_id = pApp->shortcutMap.addShortcut(this, sequence, context, *ctxMatcher());
|
||||
shortcut.id = pApp->shortcutMap.addShortcut(this, shortcut.keySequence, context, *ctxMatcher());
|
||||
if (!m_enabled)
|
||||
pApp->shortcutMap.setShortcutEnabled(false, m_id, this);
|
||||
pApp->shortcutMap.setShortcutEnabled(false, shortcut.id, this);
|
||||
if (!m_autorepeat)
|
||||
pApp->shortcutMap.setShortcutAutoRepeat(false, m_id, this);
|
||||
pApp->shortcutMap.setShortcutAutoRepeat(false, shortcut.id, this);
|
||||
}
|
||||
}
|
||||
|
||||
void QQuickShortcut::ungrabShortcut()
|
||||
void QQuickShortcut::ungrabShortcut(Shortcut &shortcut)
|
||||
{
|
||||
if (m_id) {
|
||||
QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_id, this);
|
||||
m_id = 0;
|
||||
if (shortcut.id) {
|
||||
QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(shortcut.id, this);
|
||||
shortcut.id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,17 +52,21 @@
|
|||
//
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qvector.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtGui/qkeysequence.h>
|
||||
#include <QtQml/qqmlparserstatus.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QShortcutEvent;
|
||||
|
||||
class QQuickShortcut : public QObject, public QQmlParserStatus
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(QQmlParserStatus)
|
||||
Q_PROPERTY(QVariant sequence READ sequence WRITE setSequence NOTIFY sequenceChanged FINAL)
|
||||
Q_PROPERTY(QVariantList sequences READ sequences WRITE setSequences NOTIFY sequencesChanged FINAL REVISION 9)
|
||||
Q_PROPERTY(QString nativeText READ nativeText NOTIFY sequenceChanged FINAL REVISION 1)
|
||||
Q_PROPERTY(QString portableText READ portableText NOTIFY sequenceChanged FINAL REVISION 1)
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged FINAL)
|
||||
|
@ -76,6 +80,9 @@ public:
|
|||
QVariant sequence() const;
|
||||
void setSequence(const QVariant &sequence);
|
||||
|
||||
QVariantList sequences() const;
|
||||
void setSequences(const QVariantList &sequences);
|
||||
|
||||
QString nativeText() const;
|
||||
QString portableText() const;
|
||||
|
||||
|
@ -90,6 +97,7 @@ public:
|
|||
|
||||
Q_SIGNALS:
|
||||
void sequenceChanged();
|
||||
Q_REVISION(9) void sequencesChanged();
|
||||
void enabledChanged();
|
||||
void autoRepeatChanged();
|
||||
void contextChanged();
|
||||
|
@ -102,17 +110,26 @@ protected:
|
|||
void componentComplete() Q_DECL_OVERRIDE;
|
||||
bool event(QEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
void grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context);
|
||||
void ungrabShortcut();
|
||||
struct Shortcut {
|
||||
bool matches(QShortcutEvent *event) const;
|
||||
int id;
|
||||
QVariant userValue;
|
||||
QKeySequence keySequence;
|
||||
};
|
||||
|
||||
void setEnabled(Shortcut &shortcut, bool enabled);
|
||||
void setAutoRepeat(Shortcut &shortcut, bool repeat);
|
||||
|
||||
void grabShortcut(Shortcut &shortcut, Qt::ShortcutContext context);
|
||||
void ungrabShortcut(Shortcut &shortcut);
|
||||
|
||||
private:
|
||||
int m_id;
|
||||
bool m_enabled;
|
||||
bool m_completed;
|
||||
bool m_autorepeat;
|
||||
QKeySequence m_shortcut;
|
||||
Qt::ShortcutContext m_context;
|
||||
QVariant m_sequence;
|
||||
Shortcut m_shortcut;
|
||||
QVector<Shortcut> m_shortcuts;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -123,4 +123,6 @@ void QQuickUtilModule::defineModule()
|
|||
qmlRegisterType<QQuickShortcut>("QtQuick", 2, 5, "Shortcut");
|
||||
|
||||
qmlRegisterType<QQuickShortcut,1>("QtQuick", 2, 6, "Shortcut");
|
||||
|
||||
qmlRegisterType<QQuickShortcut,9>("QtQuick", 2, 9, "Shortcut");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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:GPL-EXCEPT$
|
||||
** 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 General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** 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-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.9
|
||||
import QtQuick.Window 2.2
|
||||
|
||||
Window {
|
||||
id: window
|
||||
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
property bool activated: false
|
||||
property alias shortcut: shortcut
|
||||
|
||||
Shortcut {
|
||||
id: shortcut
|
||||
onActivated: window.activated = true
|
||||
}
|
||||
}
|
|
@ -45,6 +45,8 @@ private slots:
|
|||
void context();
|
||||
void matcher_data();
|
||||
void matcher();
|
||||
void multiple_data();
|
||||
void multiple();
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Qt::Key)
|
||||
|
@ -408,6 +410,49 @@ void tst_QQuickShortcut::matcher()
|
|||
qt_quick_set_shortcut_context_matcher(defaultMatcher);
|
||||
}
|
||||
|
||||
void tst_QQuickShortcut::multiple_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("sequences");
|
||||
QTest::addColumn<Qt::Key>("key");
|
||||
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||
QTest::addColumn<bool>("enabled");
|
||||
QTest::addColumn<bool>("activated");
|
||||
|
||||
// first
|
||||
QTest::newRow("Ctrl+X,(Shift+Del)") << (QStringList() << "Ctrl+X" << "Shift+Del") << Qt::Key_X << Qt::KeyboardModifiers(Qt::ControlModifier) << true << true;
|
||||
// second
|
||||
QTest::newRow("(Ctrl+X),Shift+Del") << (QStringList() << "Ctrl+X" << "Shift+Del") << Qt::Key_Delete << Qt::KeyboardModifiers(Qt::ShiftModifier) << true << true;
|
||||
// disabled
|
||||
QTest::newRow("(Ctrl+X,Shift+Del)") << (QStringList() << "Ctrl+X" << "Shift+Del") << Qt::Key_X << Qt::KeyboardModifiers(Qt::ControlModifier) << false << false;
|
||||
}
|
||||
|
||||
void tst_QQuickShortcut::multiple()
|
||||
{
|
||||
QFETCH(QStringList, sequences);
|
||||
QFETCH(Qt::Key, key);
|
||||
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||
QFETCH(bool, enabled);
|
||||
QFETCH(bool, activated);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
engine.load(testFileUrl("multiple.qml"));
|
||||
QQuickWindow *window = qobject_cast<QQuickWindow *>(engine.rootObjects().value(0));
|
||||
QVERIFY(window);
|
||||
window->show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
|
||||
QObject *shortcut = window->property("shortcut").value<QObject *>();
|
||||
QVERIFY(shortcut);
|
||||
|
||||
shortcut->setProperty("enabled", enabled);
|
||||
shortcut->setProperty("sequences", sequences);
|
||||
|
||||
QTest::keyPress(window, key, modifiers);
|
||||
|
||||
QCOMPARE(window->property("activated").toBool(), activated);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QQuickShortcut)
|
||||
|
||||
#include "tst_qquickshortcut.moc"
|
||||
|
|
Loading…
Reference in New Issue