diff options
author | Mitch Curtis <mitch.curtis@qt.io> | 2022-03-10 16:47:34 +0800 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-03-16 10:38:09 +0000 |
commit | 5ca8ac68e26781e63824700edb60f095a15bd6f4 (patch) | |
tree | aef533bd847eddd4d584b909d4e48c747ae16b73 | |
parent | c843ed0520ed94ea92c0673f1398dccc31220168 (diff) |
Fix sub-menu shortcuts not being triggered
When QQuickShortcutContext::matcher is checking who should get a
shortcut, it starts with the object that registered the shortcut and
climbs up its parent hierarchy until it finds a window. Sub-menus,
unlike top-level menus, will not have an associated window until their
parent menu is opened. So, check if the popup is a sub-menu so that
actions within it can grab shortcuts.
Change-Id: I07172b1038af1e9fffd9983f7bf0a155b11bb1c3
Fixes: QTBUG-101034
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
(cherry picked from commit 51ad5091929c7f2c4bab94fab9fa1c20996a6efa)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/quicktemplates2/qquickshortcutcontext.cpp | 14 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/qquickmenu/data/actionShortcuts.qml | 97 | ||||
-rw-r--r-- | tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp | 56 |
3 files changed, 167 insertions, 0 deletions
diff --git a/src/quicktemplates2/qquickshortcutcontext.cpp b/src/quicktemplates2/qquickshortcutcontext.cpp index 0c583169bb..70dc912fa8 100644 --- a/src/quicktemplates2/qquickshortcutcontext.cpp +++ b/src/quicktemplates2/qquickshortcutcontext.cpp @@ -40,6 +40,8 @@ #include "qquickshortcutcontext_p_p.h" #include "qquickoverlay_p_p.h" #include "qquicktooltip_p.h" +#include "qquickmenu_p.h" +#include "qquickmenu_p_p.h" #include "qquickpopup_p.h" #include <QtGui/qguiapplication.h> @@ -80,6 +82,18 @@ bool QQuickShortcutContext::matcher(QObject *obj, Qt::ShortcutContext context) } else if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(obj)) { obj = popup->window(); item = popup->popupItem(); + + if (!obj) { + // The popup has no associated window (yet). However, sub-menus, + // unlike top-level menus, will not have an associated window + // until their parent menu is opened. So, check if this is a sub-menu + // so that actions within it can grab shortcuts. + if (auto *menu = qobject_cast<QQuickMenu *>(popup)) { + auto parentMenu = QQuickMenuPrivate::get(menu)->parentMenu; + while (!obj && parentMenu) + obj = parentMenu->window(); + } + } break; } obj = obj->parent(); diff --git a/tests/auto/quickcontrols2/qquickmenu/data/actionShortcuts.qml b/tests/auto/quickcontrols2/qquickmenu/data/actionShortcuts.qml new file mode 100644 index 0000000000..87ad41c8c5 --- /dev/null +++ b/tests/auto/quickcontrols2/qquickmenu/data/actionShortcuts.qml @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls + +ApplicationWindow { + width: 400 + height: 400 + + property alias menu: menu + property alias subMenu: subMenu + property alias buttonMenu: buttonMenu + + Menu { + id: menu + objectName: "menu" + + Action { + objectName: text + text: "action1" + shortcut: "A" + } + + Menu { + id: subMenu + objectName: "subMenu" + + Action { + objectName: text + text: "subAction1" + shortcut: "B" + } + } + } + + Button { + text: "Menu button" + + Menu { + id: buttonMenu + + Action { + objectName: text + text: "buttonMenuAction1" + shortcut: "C" + } + } + } +} diff --git a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp index bd17b77fd8..6ae8ae38f7 100644 --- a/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp +++ b/tests/auto/quickcontrols2/qquickmenu/tst_qquickmenu.cpp @@ -29,6 +29,9 @@ #include <QtTest/qtest.h> #include <QtTest/qsignalspy.h> #include <QtGui/qcursor.h> +#if QT_CONFIG(shortcut) +#include <QtGui/qkeysequence.h> +#endif #include <QtGui/qstylehints.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> @@ -73,6 +76,9 @@ private slots: void order(); void popup(); void actions(); +#if QT_CONFIG(shortcut) + void actionShortcuts(); +#endif void removeTakeItem(); void subMenuMouse_data(); void subMenuMouse(); @@ -999,6 +1005,56 @@ void tst_QQuickMenu::actions() QVERIFY(menuItem1.isNull()); } +#if QT_CONFIG(shortcut) +void tst_QQuickMenu::actionShortcuts() +{ + QQuickControlsApplicationHelper helper(this, QLatin1String("actionShortcuts.qml")); + QVERIFY2(helper.ready, helper.failureMessage()); + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window)); + + // Try the menu's shortcut. + QQuickMenu *menu = window->property("menu").value<QQuickMenu *>(); + QVERIFY(menu); + QPointer<QQuickAction> action1 = menu->actionAt(0); + QVERIFY(action1); + QCOMPARE(action1->shortcut(), QKeySequence(Qt::Key_A)); + + QSignalSpy action1TriggeredSpy(action1, SIGNAL(triggered())); + QVERIFY(action1TriggeredSpy.isValid()); + + QTest::keyClick(window, Qt::Key_A); + QCOMPARE(action1TriggeredSpy.count(), 1); + + // Try the sub-menu. + QQuickMenu *subMenu = window->property("subMenu").value<QQuickMenu *>(); + QVERIFY(subMenu); + QPointer<QQuickAction> subMenuAction1 = subMenu->actionAt(0); + QVERIFY(subMenuAction1); + QCOMPARE(subMenuAction1->shortcut(), QKeySequence(Qt::Key_B)); + + QSignalSpy subMenuAction1TriggeredSpy(subMenuAction1, SIGNAL(triggered())); + QVERIFY(subMenuAction1TriggeredSpy.isValid()); + + QTest::keyClick(window, Qt::Key_B); + QCOMPARE(subMenuAction1TriggeredSpy.count(), 1); + + // Try the button menu. + QQuickMenu *buttonMenu = window->property("buttonMenu").value<QQuickMenu *>(); + QVERIFY(buttonMenu); + QPointer<QQuickAction> buttonMenuAction1 = buttonMenu->actionAt(0); + QVERIFY(buttonMenuAction1); + QCOMPARE(buttonMenuAction1->shortcut(), QKeySequence(Qt::Key_C)); + + QSignalSpy buttonMenuAction1TriggeredSpy(buttonMenuAction1, SIGNAL(triggered())); + QVERIFY(buttonMenuAction1TriggeredSpy.isValid()); + + QTest::keyClick(window, Qt::Key_C); + QCOMPARE(buttonMenuAction1TriggeredSpy.count(), 1); +} +#endif + void tst_QQuickMenu::removeTakeItem() { QQuickControlsApplicationHelper helper(this, QLatin1String("removeTakeItem.qml")); |