aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@qt.io>2016-10-22 07:31:37 +0200
committerJ-P Nurmi <jpnurmi@qt.io>2016-11-16 17:33:41 +0000
commita7fd83cd0cecb789006baecabfc6a49c49b7f48c (patch)
tree069fbf66e53c046770d0083197ba822efe884f45
parente21a699dca60b816a48956c26e1366cfd3ff04dc (diff)
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>
-rw-r--r--src/quick/util/qquickshortcut.cpp149
-rw-r--r--src/quick/util/qquickshortcut_p.h27
-rw-r--r--src/quick/util/qquickutilmodule.cpp2
-rw-r--r--tests/auto/quick/qquickshortcut/data/multiple.qml45
-rw-r--r--tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp45
5 files changed, 230 insertions, 38 deletions
diff --git a/src/quick/util/qquickshortcut.cpp b/src/quick/util/qquickshortcut.cpp
index 3e04161639..a0a58f2e02 100644
--- a/src/quick/util/qquickshortcut.cpp
+++ b/src/quick/util/qquickshortcut.cpp
@@ -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,31 +159,79 @@ 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;
+}
+
+void QQuickShortcut::setEnabled(QQuickShortcut::Shortcut &shortcut, bool enabled)
+{
+ if (shortcut.id)
+ QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, shortcut.id, this);
+}
- if (m_completed && !sequence.isEmpty()) {
+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;
}
}
diff --git a/src/quick/util/qquickshortcut_p.h b/src/quick/util/qquickshortcut_p.h
index b3f33a33c1..93430ad893 100644
--- a/src/quick/util/qquickshortcut_p.h
+++ b/src/quick/util/qquickshortcut_p.h
@@ -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
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index 7e2973a78b..cb586d1565 100644
--- a/src/quick/util/qquickutilmodule.cpp
+++ b/src/quick/util/qquickutilmodule.cpp
@@ -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");
}
diff --git a/tests/auto/quick/qquickshortcut/data/multiple.qml b/tests/auto/quick/qquickshortcut/data/multiple.qml
new file mode 100644
index 0000000000..2b58327cf0
--- /dev/null
+++ b/tests/auto/quick/qquickshortcut/data/multiple.qml
@@ -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
+ }
+}
diff --git a/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
index 2df94bb84a..75ccf26af9 100644
--- a/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
+++ b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp
@@ -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"