diff options
-rw-r--r-- | src/quick/util/qquickshortcut.cpp | 283 | ||||
-rw-r--r-- | src/quick/util/qquickshortcut_p.h | 109 | ||||
-rw-r--r-- | src/quick/util/qquickutilmodule.cpp | 3 | ||||
-rw-r--r-- | src/quick/util/util.pri | 6 | ||||
-rw-r--r-- | tests/auto/quick/qquickshortcut/data/shortcuts.qml | 61 | ||||
-rw-r--r-- | tests/auto/quick/qquickshortcut/qquickshortcut.pro | 11 | ||||
-rw-r--r-- | tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp | 354 | ||||
-rw-r--r-- | tests/auto/quick/quick.pro | 1 | ||||
-rw-r--r-- | tests/manual/shorcuts/shortcuts.qml | 147 |
9 files changed, 973 insertions, 2 deletions
diff --git a/src/quick/util/qquickshortcut.cpp b/src/quick/util/qquickshortcut.cpp new file mode 100644 index 0000000000..cd7d265ed0 --- /dev/null +++ b/src/quick/util/qquickshortcut.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickshortcut_p.h" + +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickwindow.h> +#include <QtGui/private/qguiapplication_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \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 + + \sa Keys +*/ + +/*! \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. +*/ + +QQuickShortcut::QQuickShortcut(QObject *parent) : QObject(parent), m_id(0), + m_enabled(true), m_completed(false), m_autorepeat(true), m_context(Qt::WindowShortcut) +{ +} + +QQuickShortcut::~QQuickShortcut() +{ + ungrabShortcut(); +} + +/*! + \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 +*/ +QVariant QQuickShortcut::sequence() const +{ + return m_sequence; +} + +void QQuickShortcut::setSequence(const QVariant &sequence) +{ + if (sequence == m_sequence) + return; + + QKeySequence shortcut; + if (sequence.type() == QVariant::Int) + shortcut = QKeySequence(static_cast<QKeySequence::StandardKey>(sequence.toInt())); + else + shortcut = QKeySequence::fromString(sequence.toString()); + + grabShortcut(shortcut, m_context); + + m_sequence = sequence; + m_shortcut = shortcut; + emit sequenceChanged(); +} + +/*! + \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; + + if (m_id) + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(enabled, m_id, this); + + 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; + + if (m_id) + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutAutoRepeat(repeat, m_id, this); + + 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; + + grabShortcut(m_shortcut, context); + + m_context = context; + emit contextChanged(); +} + +void QQuickShortcut::classBegin() +{ +} + +void QQuickShortcut::componentComplete() +{ + m_completed = true; + grabShortcut(m_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){ + if (se->isAmbiguous()) + emit activatedAmbiguously(); + else + emit activated(); + return true; + } + } + return false; +} + +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(); + } + return obj && obj == QGuiApplication::focusWindow(); + default: + return false; + } +} + +void QQuickShortcut::grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context) +{ + ungrabShortcut(); + + if (m_completed && !sequence.isEmpty()) { + QGuiApplicationPrivate *pApp = QGuiApplicationPrivate::instance(); + m_id = pApp->shortcutMap.addShortcut(this, sequence, context, qQuickShortcutContextMatcher); + if (!m_enabled) + pApp->shortcutMap.setShortcutEnabled(false, m_id, this); + if (!m_autorepeat) + pApp->shortcutMap.setShortcutAutoRepeat(false, m_id, this); + } +} + +void QQuickShortcut::ungrabShortcut() +{ + if (m_id) { + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_id, this); + m_id = 0; + } +} + +QT_END_NAMESPACE diff --git a/src/quick/util/qquickshortcut_p.h b/src/quick/util/qquickshortcut_p.h new file mode 100644 index 0000000000..d6ca02e00b --- /dev/null +++ b/src/quick/util/qquickshortcut_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSHORTCUT_P_H +#define QQUICKSHORTCUT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> +#include <QtGui/qkeysequence.h> +#include <QtQml/qqmlparserstatus.h> + +QT_BEGIN_NAMESPACE + +class QQuickShortcut : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QVariant sequence READ sequence WRITE setSequence NOTIFY sequenceChanged FINAL) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged FINAL) + Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat NOTIFY autoRepeatChanged FINAL) + Q_PROPERTY(Qt::ShortcutContext context READ context WRITE setContext NOTIFY contextChanged FINAL) + +public: + explicit QQuickShortcut(QObject *parent = Q_NULLPTR); + ~QQuickShortcut(); + + QVariant sequence() const; + void setSequence(const QVariant &sequence); + + bool isEnabled() const; + void setEnabled(bool enabled); + + bool autoRepeat() const; + void setAutoRepeat(bool repeat); + + Qt::ShortcutContext context() const; + void setContext(Qt::ShortcutContext context); + +Q_SIGNALS: + void sequenceChanged(); + void enabledChanged(); + void autoRepeatChanged(); + void contextChanged(); + + void activated(); + void activatedAmbiguously(); + +protected: + void classBegin() Q_DECL_OVERRIDE; + void componentComplete() Q_DECL_OVERRIDE; + bool event(QEvent *event) Q_DECL_OVERRIDE; + + void grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context); + void ungrabShortcut(); + +private: + int m_id; + bool m_enabled; + bool m_completed; + bool m_autorepeat; + QKeySequence m_shortcut; + Qt::ShortcutContext m_context; + QVariant m_sequence; +}; + +QT_END_NAMESPACE + +#endif // QQUICKSHORTCUT_P_H diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp index 0b42c21e8b..3c4ba9d0d8 100644 --- a/src/quick/util/qquickutilmodule.cpp +++ b/src/quick/util/qquickutilmodule.cpp @@ -48,6 +48,7 @@ #include "qquicktextmetrics_p.h" #include "qquicktransition_p.h" #include "qquickanimator_p.h" +#include "qquickshortcut_p.h" #include <qqmlinfo.h> #include <private/qqmltypenotavailable_p.h> #include <private/qquickanimationcontroller_p.h> @@ -103,4 +104,6 @@ void QQuickUtilModule::defineModule() qmlRegisterType<QQuickFontMetrics>("QtQuick", 2, 4, "FontMetrics"); qmlRegisterType<QQuickTextMetrics>("QtQuick", 2, 4, "TextMetrics"); + + qmlRegisterType<QQuickShortcut>("QtQuick", 2, 5, "Shortcut"); } diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 76cf1996bc..0e0df4e751 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -28,7 +28,8 @@ SOURCES += \ $$PWD/qquickanimatorcontroller.cpp \ $$PWD/qquickprofiler.cpp \ $$PWD/qquickfontmetrics.cpp \ - $$PWD/qquicktextmetrics.cpp + $$PWD/qquicktextmetrics.cpp \ + $$PWD/qquickshortcut.cpp HEADERS += \ $$PWD/qquickapplication_p.h\ @@ -64,4 +65,5 @@ HEADERS += \ $$PWD/qquickanimatorcontroller_p.h \ $$PWD/qquickprofiler_p.h \ $$PWD/qquickfontmetrics_p.h \ - $$PWD/qquicktextmetrics_p.h + $$PWD/qquicktextmetrics_p.h \ + $$PWD/qquickshortcut_p.h diff --git a/tests/auto/quick/qquickshortcut/data/shortcuts.qml b/tests/auto/quick/qquickshortcut/data/shortcuts.qml new file mode 100644 index 0000000000..88692c7d03 --- /dev/null +++ b/tests/auto/quick/qquickshortcut/data/shortcuts.qml @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Window 2.2 + +Window { + id: window + + width: 300 + height: 300 + + property string activatedShortcut + property string ambiguousShortcut + + property alias shortcuts: repeater.model + + Repeater { + id: repeater + Item { + Shortcut { + sequence: modelData.sequence + enabled: modelData.enabled + autoRepeat: modelData.autoRepeat + context: modelData.context + onActivated: activatedShortcut = sequence + onActivatedAmbiguously: ambiguousShortcut = sequence + } + } + } +} diff --git a/tests/auto/quick/qquickshortcut/qquickshortcut.pro b/tests/auto/quick/qquickshortcut/qquickshortcut.pro new file mode 100644 index 0000000000..917a7605e6 --- /dev/null +++ b/tests/auto/quick/qquickshortcut/qquickshortcut.pro @@ -0,0 +1,11 @@ +CONFIG += testcase +TARGET = tst_qquickshortcut + +SOURCES += tst_qquickshortcut.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +QT += core gui qml quick testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp new file mode 100644 index 0000000000..5adbd9ea16 --- /dev/null +++ b/tests/auto/quick/qquickshortcut/tst_qquickshortcut.cpp @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/qtest.h> +#include <QtQuick/qquickwindow.h> +#include <QtQml/qqmlapplicationengine.h> + +#include "../../shared/util.h" + +class tst_QQuickShortcut : public QQmlDataTest +{ + Q_OBJECT + +private slots: + void shortcuts_data(); + void shortcuts(); + void sequence_data(); + void sequence(); + void context_data(); + void context(); +}; + +Q_DECLARE_METATYPE(Qt::Key) +Q_DECLARE_METATYPE(Qt::KeyboardModifiers) + +static const bool EnabledShortcut = true; +static const bool DisabledShortcut = false; + +static QVariant shortcutMap(const QVariant &sequence, Qt::ShortcutContext context, bool enabled = true, bool autoRepeat = true) +{ + QVariantMap s; + s["sequence"] = sequence; + s["enabled"] = enabled; + s["context"] = context; + s["autoRepeat"] = autoRepeat; + return s; +} + +static QVariant shortcutMap(const QVariant &key, bool enabled = true, bool autoRepeat = true) +{ + return shortcutMap(key, Qt::WindowShortcut, enabled, autoRepeat); +} + +void tst_QQuickShortcut::shortcuts_data() +{ + QTest::addColumn<QVariantList>("shortcuts"); + QTest::addColumn<Qt::Key>("key"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<QString>("activatedShortcut"); + QTest::addColumn<QString>("ambiguousShortcut"); + + QVariantList shortcuts; + shortcuts << shortcutMap("M") + << shortcutMap("Alt+M") + << shortcutMap("Ctrl+M") + << shortcutMap("Shift+M") + << shortcutMap("Ctrl+Alt+M") + << shortcutMap("+") + << shortcutMap("F1") + << shortcutMap("Shift+F1"); + + QTest::newRow("M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "M" << ""; + QTest::newRow("Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "Alt+M" << ""; + QTest::newRow("Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "Ctrl+M" << ""; + QTest::newRow("Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+M" << ""; + QTest::newRow("Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "Ctrl+Alt+M" << ""; + QTest::newRow("+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "+" << ""; + QTest::newRow("F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "F1" << ""; + QTest::newRow("Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+F1" << ""; + + // ambiguous + shortcuts << shortcutMap("M") + << shortcutMap("Alt+M") + << shortcutMap("Ctrl+M") + << shortcutMap("Shift+M") + << shortcutMap("Ctrl+Alt+M") + << shortcutMap("+") + << shortcutMap("F1") + << shortcutMap("Shift+F1"); + + QTest::newRow("?M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "M"; + QTest::newRow("?Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "" << "Alt+M"; + QTest::newRow("?Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "" << "Ctrl+M"; + QTest::newRow("?Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "Shift+M"; + QTest::newRow("?Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "" << "Ctrl+Alt+M"; + QTest::newRow("?+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "+"; + QTest::newRow("?F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "" << "F1"; + QTest::newRow("?Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << "Shift+F1"; + + // disabled + shortcuts.clear(); + shortcuts << shortcutMap("M", DisabledShortcut) + << shortcutMap("Alt+M", DisabledShortcut) + << shortcutMap("Ctrl+M", DisabledShortcut) + << shortcutMap("Shift+M", DisabledShortcut) + << shortcutMap("Ctrl+Alt+M", DisabledShortcut) + << shortcutMap("+", DisabledShortcut) + << shortcutMap("F1", DisabledShortcut) + << shortcutMap("Shift+F1", DisabledShortcut); + + QTest::newRow("!M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "" << ""; + QTest::newRow("!Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "" << ""; + QTest::newRow("!Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "" << ""; + QTest::newRow("!Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << ""; + QTest::newRow("!Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "" << ""; + QTest::newRow("!+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "" << ""; + QTest::newRow("!F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "" << ""; + QTest::newRow("!Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "" << ""; + + // unambigous because others disabled + shortcuts << shortcutMap("M") + << shortcutMap("Alt+M") + << shortcutMap("Ctrl+M") + << shortcutMap("Shift+M") + << shortcutMap("Ctrl+Alt+M") + << shortcutMap("+") + << shortcutMap("F1") + << shortcutMap("Shift+F1"); + + QTest::newRow("/M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::NoModifier) << "M" << ""; + QTest::newRow("/Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::AltModifier) << "Alt+M" << ""; + QTest::newRow("/Ctrl+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier) << "Ctrl+M" << ""; + QTest::newRow("/Shift+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+M" << ""; + QTest::newRow("/Ctrl+Alt+M") << shortcuts << Qt::Key_M << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::AltModifier) << "Ctrl+Alt+M" << ""; + QTest::newRow("/+") << shortcuts << Qt::Key_Plus << Qt::KeyboardModifiers(Qt::NoModifier) << "+" << ""; + QTest::newRow("/F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::NoModifier) << "F1" << ""; + QTest::newRow("/Shift+F1") << shortcuts << Qt::Key_F1 << Qt::KeyboardModifiers(Qt::ShiftModifier) << "Shift+F1" << ""; +} + +void tst_QQuickShortcut::shortcuts() +{ + QFETCH(QVariantList, shortcuts); + QFETCH(Qt::Key, key); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(QString, activatedShortcut); + QFETCH(QString, ambiguousShortcut); + + QQmlApplicationEngine engine; + engine.load(testFileUrl("shortcuts.qml")); + + QQuickWindow *window = qobject_cast<QQuickWindow *>(engine.rootObjects().value(0)); + QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + + window->setProperty("shortcuts", shortcuts); + + QTest::keyPress(window, key, modifiers); + QCOMPARE(window->property("activatedShortcut").toString(), activatedShortcut); + QCOMPARE(window->property("ambiguousShortcut").toString(), ambiguousShortcut); +} + +void tst_QQuickShortcut::sequence_data() +{ + QTest::addColumn<QVariantList>("shortcuts"); + QTest::addColumn<Qt::Key>("key1"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers1"); + QTest::addColumn<Qt::Key>("key2"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers2"); + QTest::addColumn<Qt::Key>("key3"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers3"); + QTest::addColumn<Qt::Key>("key4"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers4"); + QTest::addColumn<QString>("activatedShortcut"); + QTest::addColumn<QString>("ambiguousShortcut"); + + QVariantList shortcuts; + shortcuts << shortcutMap("Escape,W") + << shortcutMap("Ctrl+X,Ctrl+C") + << shortcutMap("Shift+Ctrl+4,Space") + << shortcutMap("Alt+T,Ctrl+R,Shift+J,H"); + + QTest::newRow("Escape,W") << shortcuts << Qt::Key_Escape << Qt::KeyboardModifiers(Qt::NoModifier) + << Qt::Key_W << Qt::KeyboardModifiers(Qt::NoModifier) + << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier) + << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier) + << "Escape,W" << ""; + + QTest::newRow("Ctrl+X,Ctrl+C") << shortcuts << Qt::Key_X << Qt::KeyboardModifiers(Qt::ControlModifier) + << Qt::Key_C << Qt::KeyboardModifiers(Qt::ControlModifier) + << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier) + << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier) + << "Ctrl+X,Ctrl+C" << ""; + + QTest::newRow("Shift+Ctrl+4,Space") << shortcuts << Qt::Key_4 << Qt::KeyboardModifiers(Qt::ControlModifier|Qt::ShiftModifier) + << Qt::Key_Space << Qt::KeyboardModifiers(Qt::NoModifier) + << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier) + << Qt::Key(0) << Qt::KeyboardModifiers(Qt::NoModifier) + << "Shift+Ctrl+4,Space" << ""; + + QTest::newRow("Alt+T,Ctrl+R,Shift+J,H") << shortcuts << Qt::Key_T << Qt::KeyboardModifiers(Qt::AltModifier) + << Qt::Key_R << Qt::KeyboardModifiers(Qt::ControlModifier) + << Qt::Key_J << Qt::KeyboardModifiers(Qt::ShiftModifier) + << Qt::Key_H << Qt::KeyboardModifiers(Qt::NoModifier) + << "Alt+T,Ctrl+R,Shift+J,H" << ""; +} + +void tst_QQuickShortcut::sequence() +{ + QFETCH(QVariantList, shortcuts); + QFETCH(Qt::Key, key1); + QFETCH(Qt::KeyboardModifiers, modifiers1); + QFETCH(Qt::Key, key2); + QFETCH(Qt::KeyboardModifiers, modifiers2); + QFETCH(Qt::Key, key3); + QFETCH(Qt::KeyboardModifiers, modifiers3); + QFETCH(Qt::Key, key4); + QFETCH(Qt::KeyboardModifiers, modifiers4); + QFETCH(QString, activatedShortcut); + QFETCH(QString, ambiguousShortcut); + + QQmlApplicationEngine engine; + engine.load(testFileUrl("shortcuts.qml")); + + QQuickWindow *window = qobject_cast<QQuickWindow *>(engine.rootObjects().value(0)); + QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + + window->setProperty("shortcuts", shortcuts); + + if (key1 != 0) + QTest::keyPress(window, key1, modifiers1); + if (key2 != 0) + QTest::keyPress(window, key2, modifiers2); + if (key3 != 0) + QTest::keyPress(window, key3, modifiers3); + if (key4 != 0) + QTest::keyPress(window, key4, modifiers4); + + QCOMPARE(window->property("activatedShortcut").toString(), activatedShortcut); + QCOMPARE(window->property("ambiguousShortcut").toString(), ambiguousShortcut); +} + +void tst_QQuickShortcut::context_data() +{ + QTest::addColumn<Qt::Key>("key"); + QTest::addColumn<QVariantList>("activeWindowShortcuts"); + QTest::addColumn<QVariantList>("inactiveWindowShortcuts"); + QTest::addColumn<QString>("activeWindowActivatedShortcut"); + QTest::addColumn<QString>("inactiveWindowActivatedShortcut"); + QTest::addColumn<QString>("ambiguousShortcut"); + + // APP: F1,(F2),F3,(F4) / WND: F7,(F8),F9,(F10) + QVariantList activeWindowShortcuts; + activeWindowShortcuts << shortcutMap("F1", Qt::ApplicationShortcut, EnabledShortcut) + << shortcutMap("F2", Qt::ApplicationShortcut, DisabledShortcut) + << shortcutMap("F3", Qt::ApplicationShortcut, EnabledShortcut) + << shortcutMap("F4", Qt::ApplicationShortcut, DisabledShortcut) + << shortcutMap("F7", Qt::WindowShortcut, EnabledShortcut) + << shortcutMap("F8", Qt::WindowShortcut, DisabledShortcut) + << shortcutMap("F9", Qt::WindowShortcut, EnabledShortcut) + << shortcutMap("F10", Qt::WindowShortcut, DisabledShortcut); + + // APP: F3,(F4),F5,(F6) / WND: F9,(F10),F11(F12) + QVariantList inactiveWindowShortcuts; + inactiveWindowShortcuts << shortcutMap("F3", Qt::ApplicationShortcut, EnabledShortcut) + << shortcutMap("F4", Qt::ApplicationShortcut, DisabledShortcut) + << shortcutMap("F5", Qt::ApplicationShortcut, EnabledShortcut) + << shortcutMap("F6", Qt::ApplicationShortcut, DisabledShortcut) + << shortcutMap("F9", Qt::WindowShortcut, EnabledShortcut) + << shortcutMap("F10", Qt::WindowShortcut, DisabledShortcut) + << shortcutMap("F11", Qt::WindowShortcut, EnabledShortcut) + << shortcutMap("F12", Qt::WindowShortcut, DisabledShortcut); + + // APP + QTest::newRow("F1") << Qt::Key_F1 << activeWindowShortcuts << inactiveWindowShortcuts << "F1" << "" << ""; + QTest::newRow("F2") << Qt::Key_F2 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << ""; + QTest::newRow("F3") << Qt::Key_F3 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << "F3"; + QTest::newRow("F4") << Qt::Key_F4 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << ""; + QTest::newRow("F5") << Qt::Key_F5 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "F5" << ""; + QTest::newRow("F6") << Qt::Key_F6 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << ""; + + // WND + QTest::newRow("F7") << Qt::Key_F7 << activeWindowShortcuts << inactiveWindowShortcuts << "F7" << "" << ""; + QTest::newRow("F8") << Qt::Key_F8 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << ""; + QTest::newRow("F9") << Qt::Key_F9 << activeWindowShortcuts << inactiveWindowShortcuts << "F9" << "" << ""; + QTest::newRow("F10") << Qt::Key_F10 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << ""; + QTest::newRow("F11") << Qt::Key_F11 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << ""; + QTest::newRow("F12") << Qt::Key_F12 << activeWindowShortcuts << inactiveWindowShortcuts << "" << "" << ""; +} + +void tst_QQuickShortcut::context() +{ + QFETCH(Qt::Key, key); + QFETCH(QVariantList, activeWindowShortcuts); + QFETCH(QVariantList, inactiveWindowShortcuts); + QFETCH(QString, activeWindowActivatedShortcut); + QFETCH(QString, inactiveWindowActivatedShortcut); + QFETCH(QString, ambiguousShortcut); + + QQmlApplicationEngine engine; + + engine.load(testFileUrl("shortcuts.qml")); + QQuickWindow *inactiveWindow = qobject_cast<QQuickWindow *>(engine.rootObjects().value(0)); + QVERIFY(inactiveWindow); + inactiveWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(inactiveWindow)); + inactiveWindow->setProperty("shortcuts", inactiveWindowShortcuts); + + engine.load(testFileUrl("shortcuts.qml")); + QQuickWindow *activeWindow = qobject_cast<QQuickWindow *>(engine.rootObjects().value(1)); + QVERIFY(activeWindow); + activeWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(activeWindow)); + activeWindow->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(activeWindow)); + activeWindow->setProperty("shortcuts", activeWindowShortcuts); + + QTest::keyPress(activeWindow, key); + + QCOMPARE(activeWindow->property("activatedShortcut").toString(), activeWindowActivatedShortcut); + QCOMPARE(inactiveWindow->property("activatedShortcut").toString(), inactiveWindowActivatedShortcut); + QVERIFY(activeWindow->property("ambiguousShortcut").toString() == ambiguousShortcut + || inactiveWindow->property("ambiguousShortcut").toString() == ambiguousShortcut); +} + +QTEST_MAIN(tst_QQuickShortcut) + +#include "tst_qquickshortcut.moc" diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index e400556b67..c2b7a4cc8d 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -64,6 +64,7 @@ QUICKTESTS = \ qquickrectangle \ qquickrepeater \ qquickshadereffect \ + qquickshortcut \ qquickspritesequence \ qquicktext \ qquicktextdocument \ diff --git a/tests/manual/shorcuts/shortcuts.qml b/tests/manual/shorcuts/shortcuts.qml new file mode 100644 index 0000000000..43562a6062 --- /dev/null +++ b/tests/manual/shorcuts/shortcuts.qml @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.3 + +ApplicationWindow { + id: window + + width: 520 + height: 340 + visible: true + title: "Shortcuts - main" + + menuBar: MenuBar { + Menu { + title: "File" + MenuItem { + text: "New..." + shortcut: StandardKey.New + onTriggered: shortcutWindow.createObject(window) + } + MenuItem { + text: "Quit" + shortcut: StandardKey.Quit + onTriggered: Qt.quit() + } + } + } + + Loader { + anchors.margins: 20 + anchors.fill: parent + sourceComponent: shortcutColumn + } + + Component { + id: shortcutWindow + + ApplicationWindow { + width: 520 + height: 300 + visible: true + title: "Shortcuts - child" + + Loader { + anchors.margins: 20 + anchors.fill: parent + sourceComponent: shortcutColumn + } + + onClosing: destroy(1) + } + } + + Component { + id: shortcutColumn + + Column { + spacing: 20 + + Repeater { + model: ["Esc", "Ctrl+C", "Alt+6", "Shift+F12", "Ctrl+X,Ctrl+C"] + + RowLayout { + spacing: 20 + width: parent.width + + CheckBox { + id: checkbox + text: modelData + checked: index % 2 == 0 + Layout.fillWidth: true + } + + ComboBox { + id: combobox + enabled: checkbox.checked + model: ["WindowShortcut", "ApplicationShortcut"] + } + + Shortcut { + id: shortcut + + property int activationCount: 0 + property int ambiguousActivationCount: 0 + + sequence: modelData + enabled: checkbox.checked + context: combobox.currentText + + onActivated: { activationCount++; activationTimer.restart() } + onActivatedAmbiguously: { ambiguousActivationCount++; ambiguousActivationTimer.restart() } + } + + Timer { id: activationTimer; interval: 500 } + Timer { id: ambiguousActivationTimer; interval: 500 } + + Column { + Text { + font.pixelSize: 10 + text: qsTr("Activated: %1").arg(shortcut.activationCount) + color: activationTimer.running ? "red" : checkbox.checked ? "black" : "gray" + } + Text { + font.pixelSize: 10 + text: qsTr("Ambiguously: %1").arg(shortcut.ambiguousActivationCount) + color: ambiguousActivationTimer.running ? "red" : checkbox.checked ? "black" : "gray" + } + } + } + } + } + } +} + |