diff options
author | Mitch Curtis <[email protected]> | 2023-11-21 11:12:31 +0800 |
---|---|---|
committer | Mitch Curtis <[email protected]> | 2024-01-16 18:09:10 +0800 |
commit | d84556e6bcc91690e5fccd1a909707698b6be56c (patch) | |
tree | 9047a4e927eed8ca643680a8d0c02cd4ec1a8cd3 /tests | |
parent | 8ba21bb39fdb92de889c3ba5f3bc10428576a5f3 (diff) |
Add beginnings of native Menu backend
This patch gets the basic functionality and initial API in place
so that work on MenuBar can begin.
Task-number: QTBUG-69558
Change-Id: I94df848f771d38cd1cabb964b695d383f66240f2
Reviewed-by: Richard Moe Gustavsen <[email protected]>
Diffstat (limited to 'tests')
6 files changed, 338 insertions, 0 deletions
diff --git a/tests/auto/quickcontrols/CMakeLists.txt b/tests/auto/quickcontrols/CMakeLists.txt index f4f2e6b2c6..6b53bdf80e 100644 --- a/tests/auto/quickcontrols/CMakeLists.txt +++ b/tests/auto/quickcontrols/CMakeLists.txt @@ -17,6 +17,12 @@ if(NOT ANDROID) # QTBUG-100258 add_subdirectory(focus) endif() add_subdirectory(font) +# For now there's no way of knowing (at built time) if the Linux we're on is +# running with the GTK+ platform theme, which is the only context +# in which native menus are supported there. So we don't include it. +if(WIN32 OR MACOS OR IOS OR ANDROID) + add_subdirectory(nativemenus) +endif() add_subdirectory(palette) add_subdirectory(platform) add_subdirectory(pointerhandlers) diff --git a/tests/auto/quickcontrols/nativemenus/CMakeLists.txt b/tests/auto/quickcontrols/nativemenus/CMakeLists.txt new file mode 100644 index 0000000000..2112c9ab74 --- /dev/null +++ b/tests/auto/quickcontrols/nativemenus/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_nativemenus LANGUAGES C CXX ASM) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_nativemenus + SOURCES + tst_nativemenus.cpp + LIBRARIES + Qt::CorePrivate + Qt::Gui + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickControls2 + Qt::QuickControls2Private + Qt::QuickControlsTestUtilsPrivate + Qt::QuickPrivate + Qt::QuickTemplates2Private + Qt::QuickTest + Qt::QuickTestUtilsPrivate + Qt::TestPrivate + TESTDATA ${test_data} +) + +qt_internal_extend_target(tst_nativemenus CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=":/data" +) + +qt_internal_extend_target(tst_nativemenus CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" +) diff --git a/tests/auto/quickcontrols/nativemenus/data/emptyMenu.qml b/tests/auto/quickcontrols/nativemenus/data/emptyMenu.qml new file mode 100644 index 0000000000..f1d4f33625 --- /dev/null +++ b/tests/auto/quickcontrols/nativemenus/data/emptyMenu.qml @@ -0,0 +1,50 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T +import QtQuick.Controls + +ApplicationWindow { + width: 400 + height: 400 + + property alias contextMenu: contextMenu + + function addAction(menu: T.Menu, text: string) { + menu.addAction(actionComponent.createObject(null, { text: text })) + } + + function insertAction(menu: T.Menu, index: int, text: string) { + menu.insertAction(index, actionComponent.createObject(null, { text: text })) + } + + function removeAction(menu: T.Menu, index: int) { + menu.removeAction(menu.actionAt(index)) + } + + function addMenu(menu: T.Menu, title: string) { + menu.addMenu(menuComponent.createObject(null, { title: title })) + } + + Component { + id: actionComponent + + Action { + objectName: text + } + } + + Component { + id: menuComponent + + Menu { + objectName: title + } + } + + Menu { + id: contextMenu + objectName: "menu" + } +} diff --git a/tests/auto/quickcontrols/nativemenus/data/staticActionsAndSubmenus.qml b/tests/auto/quickcontrols/nativemenus/data/staticActionsAndSubmenus.qml new file mode 100644 index 0000000000..54fb1497d0 --- /dev/null +++ b/tests/auto/quickcontrols/nativemenus/data/staticActionsAndSubmenus.qml @@ -0,0 +1,56 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Templates as T +import QtQuick.Controls + +ApplicationWindow { + width: 400 + height: 400 + + property alias contextMenu: contextMenu +// property alias buttonMenu: buttonMenu + + Menu { + id: contextMenu + objectName: "menu" + + Action { + objectName: text + text: "action1" + shortcut: "A" + } + + Action { + objectName: text + text: "action2" + shortcut: "B" + } + + Menu { + id: subMenu + objectName: "subMenu" + + Action { + objectName: text + text: "subAction1" + shortcut: "1" + } + } + } + +// Button { +// text: "Menu button" + +// Menu { +// id: buttonMenu + +// Action { +// objectName: text +// text: "buttonMenuAction1" +// shortcut: "Z" +// } +// } +// } +} diff --git a/tests/auto/quickcontrols/nativemenus/tst_nativemenus.cpp b/tests/auto/quickcontrols/nativemenus/tst_nativemenus.cpp new file mode 100644 index 0000000000..4e1fe9dd70 --- /dev/null +++ b/tests/auto/quickcontrols/nativemenus/tst_nativemenus.cpp @@ -0,0 +1,181 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#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 <QtGui/qpa/qplatformintegration.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcomponent.h> +#include <QtQml/qqmlcontext.h> +#include <QtQuick/qquickview.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuickTestUtils/private/qmlutils_p.h> +#include <QtQuickTestUtils/private/visualtestutils_p.h> +#include <QtQuickControlsTestUtils/private/controlstestutils_p.h> +#include <QtQuickControlsTestUtils/private/qtest_quickcontrols_p.h> + +#include <QtQuickTemplates2/private/qquickaction_p.h> +#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h> +#include <QtQuickTemplates2/private/qquickmenu_p.h> +#include <QtQuickTemplates2/private/qquickmenu_p_p.h> +#include <QtQuickTemplates2/private/qquickmenuitem_p.h> +#include <QtQuickTemplates2/private/qquickmenuseparator_p.h> + +using namespace QQuickVisualTestUtils; +using namespace QQuickControlsTestUtils; + +/* + We have a separate test project for native menus because we don't + want to run them for every style, just the platforms that have + native menu support. +*/ + +class tst_NativeMenus : public QQmlDataTest +{ + Q_OBJECT + +public: + tst_NativeMenus(); + +private slots: + void defaults(); + void staticActionsAndSubmenus(); + void dynamicActions(); + void dynamicSubmenus(); +}; + +tst_NativeMenus::tst_NativeMenus() + : QQmlDataTest(QT_QMLTEST_DATADIR) +{ + qputenv("QT_QUICK_CONTROLS_USE_NATIVE_MENUS", "1"); +} + +void tst_NativeMenus::defaults() +{ + QQuickControlsApplicationHelper helper(this, QLatin1String("emptyMenu.qml")); + QVERIFY2(helper.ready, helper.failureMessage()); + QQuickApplicationWindow *window = helper.appWindow; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>(); + QVERIFY(contextMenu); + auto *contextMenuPrivate = QQuickMenuPrivate::get(contextMenu); + QVERIFY(contextMenuPrivate->usingNativeMenu()); +} + +void tst_NativeMenus::staticActionsAndSubmenus() +{ + QQuickControlsApplicationHelper helper(this, QLatin1String("staticActionsAndSubmenus.qml")); + QVERIFY2(helper.ready, helper.failureMessage()); + QQuickApplicationWindow *window = helper.appWindow; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>(); + QVERIFY(contextMenu); + QVERIFY(contextMenu->requestNative()); + auto *contextMenuPrivate = QQuickMenuPrivate::get(contextMenu); + + // Check that the actions of the parent menu can be accessed + // and are in the appropriate places in contentData. + auto *action1 = contextMenu->actionAt(0); + QVERIFY(action1); + QCOMPARE(contextMenuPrivate->contentData.at(0), action1); + + auto *action2 = contextMenu->actionAt(1); + QVERIFY(action2); + QCOMPARE(contextMenuPrivate->contentData.at(1), action2); + + // Check that the sub-menu can be accessed and is in the + // appropriate place in contentData. + auto *subMenu = contextMenu->menuAt(2); + QVERIFY(subMenu); + + // TODO: check that sub-menus exist +} + +void tst_NativeMenus::dynamicActions() +{ + QQuickControlsApplicationHelper helper(this, QLatin1String("emptyMenu.qml")); + QVERIFY2(helper.ready, helper.failureMessage()); + QQuickApplicationWindow *window = helper.appWindow; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>(); + QVERIFY(contextMenu); + auto *contextMenuPrivate = QQuickMenuPrivate::get(contextMenu); + + // Check that items can be appended to an empty menu. + QCOMPARE(contextMenu->actionAt(0), nullptr); + QVERIFY(QMetaObject::invokeMethod(window, "addAction", + Q_ARG(QQuickMenu *, contextMenu), Q_ARG(QString, "action1"))); + { + auto action1 = contextMenu->actionAt(0); + QVERIFY(action1); + QCOMPARE(action1->text(), "action1"); + QCOMPARE(contextMenuPrivate->contentData.at(0), action1); + } + + // Check that actions can be appended after existing items in the parent menu. + QCOMPARE(contextMenu->actionAt(1), nullptr); + QVERIFY(QMetaObject::invokeMethod(window, "addAction", + Q_ARG(QQuickMenu *, contextMenu), Q_ARG(QString, "action2"))); + { + auto action2 = contextMenu->actionAt(1); + QVERIFY(action2); + QCOMPARE(action2->text(), "action2"); + QCOMPARE(contextMenuPrivate->contentData.at(1), action2); + } + + // Check that actions can be inserted before existing items in the parent menu. + QVERIFY(QMetaObject::invokeMethod(window, "insertAction", + Q_ARG(QQuickMenu *, contextMenu), Q_ARG(int, 0), Q_ARG(QString, "action0"))); + { + auto action0 = contextMenu->actionAt(0); + QVERIFY(action0); + QCOMPARE(action0->text(), "action0"); + QCOMPARE(contextMenuPrivate->contentData.at(0), action0); + } +} + +void tst_NativeMenus::dynamicSubmenus() +{ + QQuickControlsApplicationHelper helper(this, QLatin1String("emptyMenu.qml")); + QVERIFY2(helper.ready, helper.failureMessage()); + QQuickApplicationWindow *window = helper.appWindow; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>(); + QVERIFY(contextMenu); + auto *contextMenuPrivate = QQuickMenuPrivate::get(contextMenu); + + // Check that sub-menus (with no menu items, yet) can be appended to an empty parent menu. + QVERIFY(QMetaObject::invokeMethod(window, "addMenu", + Q_ARG(QQuickMenu *, contextMenu), Q_ARG(QString, "subMenu1"))); + auto subMenu1 = contextMenu->menuAt(0); + QVERIFY(subMenu1); + QCOMPARE(subMenu1->title(), "subMenu1"); + QCOMPARE(contextMenuPrivate->contentData.at(0), subMenu1); + + // + QVERIFY(QMetaObject::invokeMethod(window, "addAction", + Q_ARG(QQuickMenu *, subMenu1), Q_ARG(QString, "subMenuAction1"))); + + // TODO: insert another sub-menu action before the first one +} + +// TODO: add a test that mixes items with native items +// and ensure that all items are recreated as non-native + +QTEST_MAIN(tst_NativeMenus) + +#include "tst_nativemenus.moc" diff --git a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp index fbb4e7d5f9..d446fbecac 100644 --- a/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp +++ b/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp @@ -32,6 +32,8 @@ using namespace QQuickVisualTestUtils; using namespace QQuickControlsTestUtils; +// Native menu tests are in "nativemenus". + class tst_QQuickMenu : public QQmlDataTest { Q_OBJECT |