2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2017 The Qt Company Ltd.
|
2024-02-22 14:51:16 +00:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2019-04-29 11:03:25 +00:00
|
|
|
#include <QtTest/qtest.h>
|
|
|
|
#include <QtTest/qsignalspy.h>
|
2017-05-17 15:35:00 +00:00
|
|
|
#include <QtGui/qcursor.h>
|
2022-03-10 08:47:34 +00:00
|
|
|
#if QT_CONFIG(shortcut)
|
|
|
|
#include <QtGui/qkeysequence.h>
|
|
|
|
#endif
|
2015-11-16 15:06:07 +00:00
|
|
|
#include <QtGui/qstylehints.h>
|
2022-06-24 10:39:41 +00:00
|
|
|
#include <QtGui/qpa/qplatformintegration.h>
|
2024-02-07 12:52:20 +00:00
|
|
|
#include <QtGui/qpa/qplatformtheme.h>
|
2022-06-24 10:39:41 +00:00
|
|
|
#include <QtGui/private/qguiapplication_p.h>
|
2015-11-16 15:06:07 +00:00
|
|
|
#include <QtQml/qqmlengine.h>
|
|
|
|
#include <QtQml/qqmlcomponent.h>
|
|
|
|
#include <QtQml/qqmlcontext.h>
|
|
|
|
#include <QtQuick/qquickview.h>
|
|
|
|
#include <QtQuick/private/qquickitem_p.h>
|
2024-02-12 06:35:17 +00:00
|
|
|
#include <QtQuick/private/qquickrectangle_p.h>
|
2024-08-20 13:50:06 +00:00
|
|
|
#include <QtQuickTest/quicktest.h>
|
2021-08-06 10:27:35 +00:00
|
|
|
#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>
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2017-06-09 08:09:28 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickaction_p.h>
|
2016-04-13 13:59:53 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickapplicationwindow_p.h>
|
|
|
|
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
|
|
|
|
#include <QtQuickTemplates2/private/qquickbutton_p.h>
|
2022-09-14 11:09:49 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickicon_p.h>
|
2016-04-13 13:59:53 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickmenu_p.h>
|
2024-02-05 08:51:46 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickmenu_p_p.h>
|
2016-04-13 13:59:53 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickmenuitem_p.h>
|
2016-07-21 14:04:18 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickmenuseparator_p.h>
|
2024-02-05 08:51:46 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquicknativemenuitem_p.h>
|
2024-08-20 13:50:06 +00:00
|
|
|
#include <QtQuickTemplates2/private/qquickpopupwindow_p_p.h>
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
using namespace QQuickVisualTestUtils;
|
|
|
|
using namespace QQuickControlsTestUtils;
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2023-11-21 03:12:31 +00:00
|
|
|
// Native menu tests are in "nativemenus".
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
class tst_QQuickMenu : public QQmlDataTest
|
2015-11-16 15:06:07 +00:00
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
2021-08-06 10:27:35 +00:00
|
|
|
tst_QQuickMenu();
|
2015-11-16 15:06:07 +00:00
|
|
|
|
|
|
|
private slots:
|
2024-06-06 12:35:25 +00:00
|
|
|
void init() final;
|
2024-09-02 23:43:15 +00:00
|
|
|
void cleanup();
|
2024-03-14 03:36:06 +00:00
|
|
|
|
2015-11-16 15:06:07 +00:00
|
|
|
void defaults();
|
2017-07-12 13:58:02 +00:00
|
|
|
void count();
|
2024-08-20 13:50:06 +00:00
|
|
|
void mouse_data();
|
2015-11-16 15:06:07 +00:00
|
|
|
void mouse();
|
2017-06-29 11:15:24 +00:00
|
|
|
void pressAndHold();
|
2024-08-20 14:08:13 +00:00
|
|
|
void contextMenuKeyboard_data();
|
2015-11-16 15:06:07 +00:00
|
|
|
void contextMenuKeyboard();
|
2019-02-01 12:53:06 +00:00
|
|
|
void disabledMenuItemKeyNavigation();
|
2017-06-29 12:23:13 +00:00
|
|
|
void mnemonics();
|
2024-08-15 10:49:34 +00:00
|
|
|
#if QT_CONFIG(shortcut)
|
|
|
|
void checkableMnemonics_data();
|
|
|
|
void checkableMnemonics();
|
|
|
|
#endif
|
2015-11-16 15:06:07 +00:00
|
|
|
void menuButton();
|
2016-05-10 11:58:05 +00:00
|
|
|
void addItem();
|
2016-07-21 14:04:18 +00:00
|
|
|
void menuSeparator();
|
2016-12-28 19:24:18 +00:00
|
|
|
void repeater();
|
|
|
|
void order();
|
2022-06-28 08:05:18 +00:00
|
|
|
#if QT_CONFIG(cursor)
|
2017-05-17 15:35:00 +00:00
|
|
|
void popup();
|
2022-06-28 08:05:18 +00:00
|
|
|
#endif
|
2024-03-14 10:07:30 +00:00
|
|
|
void openParentlessMenu();
|
2017-06-01 18:09:44 +00:00
|
|
|
void actions();
|
2022-03-10 08:47:34 +00:00
|
|
|
#if QT_CONFIG(shortcut)
|
|
|
|
void actionShortcuts();
|
|
|
|
#endif
|
2017-06-02 14:18:56 +00:00
|
|
|
void removeTakeItem();
|
2017-06-01 21:36:57 +00:00
|
|
|
void subMenuMouse_data();
|
|
|
|
void subMenuMouse();
|
2019-01-28 12:14:30 +00:00
|
|
|
void subMenuDisabledMouse_data();
|
|
|
|
void subMenuDisabledMouse();
|
2017-06-01 21:36:57 +00:00
|
|
|
void subMenuKeyboard_data();
|
|
|
|
void subMenuKeyboard();
|
2019-01-28 12:14:30 +00:00
|
|
|
void subMenuDisabledKeyboard_data();
|
|
|
|
void subMenuDisabledKeyboard();
|
2017-06-01 21:36:57 +00:00
|
|
|
void subMenuPosition_data();
|
|
|
|
void subMenuPosition();
|
2022-09-14 11:09:49 +00:00
|
|
|
void subMenuWithIcon();
|
2017-06-07 17:43:58 +00:00
|
|
|
void addRemoveSubMenus();
|
2024-06-12 11:50:31 +00:00
|
|
|
void subMenuPopupType();
|
2018-06-18 09:38:40 +00:00
|
|
|
void scrollable_data();
|
|
|
|
void scrollable();
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
void disableWhenTriggered_data();
|
|
|
|
void disableWhenTriggered();
|
2019-04-12 13:21:42 +00:00
|
|
|
void menuItemWidth_data();
|
|
|
|
void menuItemWidth();
|
|
|
|
void menuItemWidthAfterMenuWidthChanged_data();
|
|
|
|
void menuItemWidthAfterMenuWidthChanged();
|
|
|
|
void menuItemWidthAfterImplicitWidthChanged_data();
|
|
|
|
void menuItemWidthAfterImplicitWidthChanged();
|
|
|
|
void menuItemWidthAfterRetranslate();
|
2020-10-20 08:34:53 +00:00
|
|
|
void giveMenuItemFocusOnButtonPress();
|
2022-06-21 03:10:00 +00:00
|
|
|
void customMenuCullItems();
|
2022-06-24 02:38:51 +00:00
|
|
|
void customMenuUseRepeaterAsTheContentItem();
|
2023-09-05 20:14:30 +00:00
|
|
|
void invalidUrlInImgTag();
|
2024-02-07 07:10:33 +00:00
|
|
|
void nativeStatic();
|
2024-02-05 08:51:46 +00:00
|
|
|
void nativeDynamicActions();
|
|
|
|
void nativeDynamicSubmenus();
|
|
|
|
void nativeMenuSeparator();
|
2024-06-27 10:18:09 +00:00
|
|
|
void AA_DontUseNativeMenuWindows();
|
2024-03-14 03:36:06 +00:00
|
|
|
void dontUseNativeMenuWindowsChanges();
|
2024-02-12 06:35:17 +00:00
|
|
|
void nativeMixedItems();
|
2024-06-03 14:03:48 +00:00
|
|
|
void effectivePosition_data();
|
|
|
|
void effectivePosition();
|
2024-05-22 20:15:53 +00:00
|
|
|
void textPadding();
|
2024-06-03 09:58:44 +00:00
|
|
|
void resetCurrentIndexUponPopup_data();
|
|
|
|
void resetCurrentIndexUponPopup();
|
2022-06-24 10:39:41 +00:00
|
|
|
|
|
|
|
private:
|
2024-02-07 12:52:20 +00:00
|
|
|
bool nativeMenuSupported = false;
|
2024-06-12 11:50:31 +00:00
|
|
|
bool popupWindowsSupported = false;
|
2024-02-07 12:52:20 +00:00
|
|
|
};
|
2024-02-05 08:51:46 +00:00
|
|
|
|
|
|
|
// This allows us to use QQuickMenuItem's more descriptive operator<< output
|
|
|
|
// for the QCOMPARE failure message. It doesn't seem possible to use toString
|
|
|
|
// overloads or template specialization when types declared in QML are involved,
|
|
|
|
// as is the case for the MenuItems created from Menu's delegate.
|
|
|
|
#define COMPARE_MENUITEMS(actualMenuItem, expectedMenuItem) \
|
|
|
|
QVERIFY2(actualMenuItem == expectedMenuItem, \
|
|
|
|
qPrintable(QString::fromLatin1("\n Actual: %1\n Expected: %2") \
|
|
|
|
.arg(QDebug::toString(actualMenuItem), QDebug::toString(expectedMenuItem))));
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
tst_QQuickMenu::tst_QQuickMenu()
|
|
|
|
: QQmlDataTest(QT_QMLTEST_DATADIR)
|
|
|
|
{
|
2024-02-07 12:52:20 +00:00
|
|
|
std::unique_ptr<QPlatformMenu> platformMenu(QGuiApplicationPrivate::platformTheme()->createPlatformMenu());
|
|
|
|
nativeMenuSupported = platformMenu != nullptr;
|
2024-08-23 15:53:26 +00:00
|
|
|
#if defined(Q_OS_WINDOWS) || defined (Q_OS_MACOS)
|
2024-06-12 11:50:31 +00:00
|
|
|
popupWindowsSupported = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::Capability::MultipleWindows);
|
2024-08-23 15:53:26 +00:00
|
|
|
#endif
|
2021-08-06 10:27:35 +00:00
|
|
|
}
|
|
|
|
|
2024-03-14 03:36:06 +00:00
|
|
|
void tst_QQuickMenu::init()
|
|
|
|
{
|
|
|
|
QQmlDataTest::init();
|
|
|
|
|
|
|
|
// By default we don't want to use native menus, as the majority of the tests
|
|
|
|
// were written before they were a thing. We instead explicitly set it where necessary.
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
|
|
|
|
}
|
|
|
|
|
2024-09-02 23:43:15 +00:00
|
|
|
void tst_QQuickMenu::cleanup()
|
|
|
|
{
|
|
|
|
// In case a test forgets to close all popup windows
|
|
|
|
if (QGuiApplicationPrivate::popupCount() > 0)
|
|
|
|
QGuiApplicationPrivate::closeAllPopups();
|
|
|
|
QTRY_COMPARE(QGuiApplicationPrivate::popupCount(), 0);
|
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::defaults()
|
2015-11-16 15:06:07 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2016-09-03 13:15:53 +00:00
|
|
|
QQuickMenu *emptyMenu = helper.appWindow->property("emptyMenu").value<QQuickMenu*>();
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(emptyMenu->isVisible(), false);
|
2022-02-25 08:43:45 +00:00
|
|
|
QVERIFY(emptyMenu->hasFocus());
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(emptyMenu->currentIndex(), -1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(emptyMenu->contentItem()->property("currentIndex"), QVariant(-1));
|
2017-07-12 13:58:02 +00:00
|
|
|
QCOMPARE(emptyMenu->count(), 0);
|
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::count()
|
2017-07-12 13:58:02 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-07-12 13:58:02 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = helper.window->property("emptyMenu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
|
|
|
QSignalSpy countSpy(menu, &QQuickMenu::countChanged);
|
|
|
|
QVERIFY(countSpy.isValid());
|
|
|
|
|
|
|
|
menu->addItem(new QQuickItem);
|
|
|
|
QCOMPARE(menu->count(), 1);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(countSpy.size(), 1);
|
2017-07-12 13:58:02 +00:00
|
|
|
|
|
|
|
menu->insertItem(0, new QQuickItem);
|
|
|
|
QCOMPARE(menu->count(), 2);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(countSpy.size(), 2);
|
2017-07-12 13:58:02 +00:00
|
|
|
|
|
|
|
menu->removeItem(menu->itemAt(1));
|
|
|
|
QCOMPARE(menu->count(), 1);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(countSpy.size(), 3);
|
2017-07-12 13:58:02 +00:00
|
|
|
|
tst_qquickmenu: fix memory leak
takeItem() unparents the item, so we need to make sure it gets deleted.
The leak was caught by valgrind:
==10039== 832 (32 direct, 800 indirect) bytes in 1 blocks are definitely lost in loss record 6,465 of 6,706
==10039== at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10039== by 0x112C1E: tst_QQuickMenu::count() (tst_qquickmenu.cpp:121)
==10039== by 0x12F313: tst_QQuickMenu::qt_static_metacall(QObject*, QMetaObject::Call, int, void**) (tst_qquickmenu.moc:156)
==10039== by 0x612B6B2: QMetaMethod::invoke(QObject*, Qt::ConnectionType, QGenericReturnArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument, QGenericArgument) const (qmetaobject.cpp:2305)
==10039== by 0x41B07CD: invoke (qmetaobject.h:123)
==10039== by 0x41B07CD: QTest::TestMethods::invokeTestOnData(int) const (qtestcase.cpp:915)
==10039== by 0x41B15BF: QTest::TestMethods::invokeTest(int, char const*, QTest::WatchDog*) const (qtestcase.cpp:1114)
==10039== by 0x41B1D07: QTest::TestMethods::invokeTests(QObject*) const (qtestcase.cpp:1456)
==10039== by 0x41B2381: QTest::qRun() (qtestcase.cpp:1896)
==10039== by 0x41B24E1: QTest::qExec(QObject*, int, char**) (qtestcase.cpp:1783)
==10039== by 0x12F468: main (tst_qquickmenu.cpp:1505)
Change-Id: I459c7897c1088c8b58152d2e0b5ceb8f3684e589
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-10-12 13:17:41 +00:00
|
|
|
QScopedPointer<QQuickItem> item(menu->takeItem(0));
|
2017-07-12 13:58:02 +00:00
|
|
|
QVERIFY(item);
|
|
|
|
QCOMPARE(menu->count(), 0);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(countSpy.size(), 4);
|
2015-11-16 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
2024-08-20 13:50:06 +00:00
|
|
|
void tst_QQuickMenu::mouse_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QQuickPopup::PopupType>("popupType");
|
|
|
|
QTest::newRow("Popup.Item") << QQuickPopup::Item;
|
|
|
|
if (popupWindowsSupported)
|
|
|
|
QTest::newRow("Popup.Window") << QQuickPopup::Window;
|
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::mouse()
|
2015-11-16 15:06:07 +00:00
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2024-08-20 13:50:06 +00:00
|
|
|
QFETCH(QQuickPopup::PopupType, popupType);
|
|
|
|
|
2017-06-05 09:48:40 +00:00
|
|
|
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|
|
|
|
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
|
|
|
|
QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2016-09-03 13:15:53 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2017-08-09 10:51:40 +00:00
|
|
|
centerOnScreen(window);
|
2017-06-10 08:18:21 +00:00
|
|
|
moveMouseAway(window);
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
tst_qquickmenu: call requestActivate before QTest::qWaitForWindowActive
On a Ubuntu 22.04.4 VM, every test after the first one that didn't call
requestActivate before QTest::qWaitForWindowActive would fail:
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
********* Start testing of tst_QQuickMenu *********
Config: Using QtTest library 6.8.0, Qt 6.8.0 (x86_64-little_endian-lp64 shared (dynamic) debug build; by GCC 11.4.0), ubuntu 22.04
PASS : tst_QQuickMenu::Basic::initTestCase()
PASS : tst_QQuickMenu::Basic::defaults()
PASS : tst_QQuickMenu::Basic::count()
PASS : tst_QQuickMenu::Basic::mouse()
PASS : tst_QQuickMenu::Basic::pressAndHold()
PASS : tst_QQuickMenu::Basic::contextMenuKeyboard()
PASS : tst_QQuickMenu::Basic::disabledMenuItemKeyNavigation()
PASS : tst_QQuickMenu::Basic::mnemonics()
PASS : tst_QQuickMenu::Basic::menuButton()
PASS : tst_QQuickMenu::Basic::addItem()
FAIL! : tst_QQuickMenu::Basic::menuSeparator() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(697)]
PASS : tst_QQuickMenu::Basic::repeater()
PASS : tst_QQuickMenu::Basic::order()
PASS : tst_QQuickMenu::Basic::popup()
PASS : tst_QQuickMenu::Basic::actions()
FAIL! : tst_QQuickMenu::Basic::actionShortcuts() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1084)]
PASS : tst_QQuickMenu::Basic::removeTakeItem()
PASS : tst_QQuickMenu::Basic::subMenuMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuMouse(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(non-cascading)
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuWithIcon()
PASS : tst_QQuickMenu::Basic::addRemoveSubMenus()
PASS : tst_QQuickMenu::Basic::scrollable(Window)
PASS : tst_QQuickMenu::Basic::scrollable(ApplicationWindow)
PASS : tst_QQuickMenu::Basic::scrollable(WithPadding)
PASS : tst_QQuickMenu::Basic::scrollable(FixedHeight)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared inside)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::menuItemWidth(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidth(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterRetranslate()
FAIL! : tst_QQuickMenu::Basic::giveMenuItemFocusOnButtonPress() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(2097)]
PASS : tst_QQuickMenu::Basic::customMenuCullItems()
PASS : tst_QQuickMenu::Basic::customMenuUseRepeaterAsTheContentItem()
PASS : tst_QQuickMenu::Basic::invalidUrlInImgTag()
PASS : tst_QQuickMenu::Basic::nativeStatic()
PASS : tst_QQuickMenu::Basic::nativeDynamicActions()
PASS : tst_QQuickMenu::Basic::nativeDynamicSubmenus()
PASS : tst_QQuickMenu::Basic::nativeMenuSeparator()
PASS : tst_QQuickMenu::Basic::dontUseNativeMenuWindowsChanges()
PASS : tst_QQuickMenu::Basic::nativeMixedItems()
PASS : tst_QQuickMenu::Basic::cleanupTestCase()
Totals: 59 passed, 11 failed, 0 skipped, 0 blacklisted, 74936ms
********* Finished testing of tst_QQuickMenu *********
Change-Id: I5ef76d2648fc7c8732aafab3a9a0a02844075c3e
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2024-03-20 07:33:47 +00:00
|
|
|
window->requestActivate();
|
2019-07-18 17:02:40 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
2015-11-16 15:06:07 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
2020-06-05 11:31:53 +00:00
|
|
|
QVERIFY(menu);
|
2024-08-20 13:50:06 +00:00
|
|
|
QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
|
|
|
|
QVERIFY(menuPrivate);
|
|
|
|
menu->setPopupType(popupType);
|
2015-12-11 14:34:55 +00:00
|
|
|
menu->open();
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2024-08-20 13:50:06 +00:00
|
|
|
QQuickItem *parentItem = nullptr;
|
|
|
|
if (!menuPrivate->usePopupWindow()) {
|
|
|
|
parentItem = window->property("overlay").value<QQuickOverlay*>();
|
|
|
|
} else {
|
|
|
|
QTRY_VERIFY(menuPrivate->popupWindow);
|
|
|
|
parentItem = menuPrivate->popupWindow->contentItem();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(menuPrivate->popupWindow));
|
|
|
|
QQuickTest::qWaitForPolish(menuPrivate->popupWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
QVERIFY(parentItem);
|
|
|
|
QVERIFY(parentItem->childItems().contains(menu->contentItem()->parentItem()));
|
|
|
|
|
2015-11-16 15:06:07 +00:00
|
|
|
QQuickItem *firstItem = menu->itemAt(0);
|
2016-01-25 14:14:27 +00:00
|
|
|
QSignalSpy clickedSpy(firstItem, SIGNAL(clicked()));
|
2015-11-16 15:06:07 +00:00
|
|
|
QSignalSpy triggeredSpy(firstItem, SIGNAL(triggered()));
|
|
|
|
QSignalSpy visibleSpy(menu, SIGNAL(visibleChanged()));
|
|
|
|
|
2024-08-20 13:50:06 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
|
2015-11-16 15:06:07 +00:00
|
|
|
// Ensure that presses cause the current index to change,
|
|
|
|
// so that the highlight acts as a way of illustrating press state.
|
2024-08-20 13:50:06 +00:00
|
|
|
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, window->mapFromGlobal(firstItem->mapToGlobal(firstItem->boundingRect().center()).toPoint()));
|
|
|
|
QTRY_COMPARE(menu->currentIndex(), 0);
|
|
|
|
QTRY_VERIFY(firstItem->hasActiveFocus());
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(0));
|
2024-08-20 13:50:06 +00:00
|
|
|
QVERIFY(menu->isOpened());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2024-08-20 13:50:06 +00:00
|
|
|
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, window->mapFromGlobal(firstItem->mapToGlobal(firstItem->boundingRect().center()).toPoint()));
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(clickedSpy.size(), 1);
|
|
|
|
QCOMPARE(triggeredSpy.size(), 1);
|
|
|
|
QTRY_COMPARE(visibleSpy.size(), 1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!menu->isVisible());
|
2024-08-20 13:50:06 +00:00
|
|
|
QVERIFY(!parentItem->childItems().contains(menu->contentItem()));
|
|
|
|
|
|
|
|
QTRY_COMPARE(menu->currentIndex(), -1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
2015-12-11 14:34:55 +00:00
|
|
|
menu->open();
|
2024-08-20 13:50:06 +00:00
|
|
|
if (auto *popupWindow = menuPrivate->popupWindow)
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(popupWindow));
|
|
|
|
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(visibleSpy.size(), 2);
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(menu->isVisible());
|
2024-08-20 13:50:06 +00:00
|
|
|
QVERIFY(parentItem->childItems().contains(menu->contentItem()->parentItem()));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
|
|
|
// Ensure that we have enough space to click outside of the menu.
|
|
|
|
QVERIFY(window->width() > menu->contentItem()->width());
|
|
|
|
QVERIFY(window->height() > menu->contentItem()->height());
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
2021-07-06 16:07:47 +00:00
|
|
|
QPoint(menu->contentItem()->x() + menu->contentItem()->width() + 1,
|
|
|
|
menu->contentItem()->y() + menu->contentItem()->height() + 1));
|
2022-10-05 05:29:16 +00:00
|
|
|
QTRY_COMPARE(visibleSpy.size(), 3);
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!menu->isVisible());
|
2024-08-20 13:50:06 +00:00
|
|
|
if (menuPrivate->usePopupWindow())
|
|
|
|
QVERIFY(!menuPrivate->popupWindow->isVisible());
|
|
|
|
else
|
|
|
|
QVERIFY(!parentItem->childItems().contains(menu->contentItem()->parentItem()));
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2015-12-11 14:34:55 +00:00
|
|
|
menu->open();
|
2024-08-20 13:50:06 +00:00
|
|
|
if (auto *popupWindow = menuPrivate->popupWindow)
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(popupWindow));
|
|
|
|
QTRY_COMPARE(visibleSpy.size(), 4);
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(menu->isVisible());
|
2024-08-20 13:50:06 +00:00
|
|
|
QVERIFY(parentItem->childItems().contains(menu->contentItem()->parentItem()));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2022-04-22 15:01:14 +00:00
|
|
|
// Hover-highlighting does not work on Android
|
|
|
|
#ifndef Q_OS_ANDROID
|
2017-06-06 13:58:36 +00:00
|
|
|
// Hover-highlight through the menu items one by one
|
|
|
|
QQuickItem *prevHoverItem = nullptr;
|
|
|
|
QQuickItem *listView = menu->contentItem();
|
2019-04-29 11:03:25 +00:00
|
|
|
for (int y = menu->topPadding(); y < listView->height(); ++y) {
|
2017-06-06 13:58:36 +00:00
|
|
|
QQuickItem *hoverItem = nullptr;
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(listView, "itemAt", Q_RETURN_ARG(QQuickItem *, hoverItem), Q_ARG(qreal, 0), Q_ARG(qreal, listView->property("contentY").toReal() + y)));
|
|
|
|
if (!hoverItem || !hoverItem->isVisible() || hoverItem == prevHoverItem)
|
|
|
|
continue;
|
2019-04-29 11:03:25 +00:00
|
|
|
QTest::mouseMove(window, QPoint(
|
2022-07-20 15:55:21 +00:00
|
|
|
menu->x() + menu->leftPadding() + hoverItem->x() + hoverItem->width() / 2,
|
|
|
|
menu->y() + menu->topPadding() + hoverItem->y() + hoverItem->height() / 2));
|
2017-06-06 13:58:36 +00:00
|
|
|
QTRY_VERIFY(hoverItem->property("highlighted").toBool());
|
|
|
|
if (prevHoverItem)
|
|
|
|
QVERIFY(!prevHoverItem->property("highlighted").toBool());
|
|
|
|
prevHoverItem = hoverItem;
|
|
|
|
}
|
2022-04-22 15:01:14 +00:00
|
|
|
#endif
|
2017-06-06 13:58:36 +00:00
|
|
|
|
2015-11-16 15:06:07 +00:00
|
|
|
// Try pressing within the menu and releasing outside of it; it should close.
|
2015-12-08 13:26:51 +00:00
|
|
|
// TODO: won't work until QQuickPopup::releasedOutside() actually gets emitted
|
2015-11-16 15:06:07 +00:00
|
|
|
// QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(firstItem->width() / 2, firstItem->height() / 2));
|
|
|
|
// QVERIFY(firstItem->hasActiveFocus());
|
|
|
|
// QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(0));
|
|
|
|
// QVERIFY(menu->isVisible());
|
|
|
|
// QCOMPARE(triggeredSpy.count(), 1);
|
|
|
|
|
|
|
|
// QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(menu->contentItem()->width() + 1, firstItem->height() / 2));
|
|
|
|
// QCOMPARE(clickedSpy.count(), 1);
|
|
|
|
// QCOMPARE(triggeredSpy.count(), 1);
|
|
|
|
// QCOMPARE(visibleSpy.count(), 5);
|
|
|
|
// QVERIFY(!menu->isVisible());
|
2020-06-05 11:31:53 +00:00
|
|
|
// QVERIFY(!overlay->childItems().contains(menu->contentItem()));
|
2015-11-16 15:06:07 +00:00
|
|
|
// QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::pressAndHold()
|
2017-06-29 11:15:24 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("pressAndHold.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-29 11:15:24 +00:00
|
|
|
|
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2017-06-29 11:15:24 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
|
|
|
QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
|
|
|
|
QTRY_VERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
|
|
|
|
QVERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(1, 1));
|
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
|
|
|
}
|
|
|
|
|
2024-08-20 14:08:13 +00:00
|
|
|
void tst_QQuickMenu::contextMenuKeyboard_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QQuickPopup::PopupType>("popupType");
|
|
|
|
QTest::newRow("Popup.Item") << QQuickPopup::Item;
|
|
|
|
QTest::newRow("Popup.Window") << QQuickPopup::Window;
|
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::contextMenuKeyboard()
|
2015-11-16 15:06:07 +00:00
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2024-08-20 14:08:13 +00:00
|
|
|
QFETCH(QQuickPopup::PopupType, popupType);
|
|
|
|
|
2015-11-16 15:06:07 +00:00
|
|
|
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
|
|
|
|
QSKIP("This platform only allows tab focus for text controls");
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2016-09-03 13:15:53 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2019-07-18 17:02:40 +00:00
|
|
|
centerOnScreen(window);
|
|
|
|
moveMouseAway(window);
|
2015-11-16 15:06:07 +00:00
|
|
|
window->show();
|
|
|
|
window->requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
QVERIFY(QGuiApplication::focusWindow() == window);
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
2024-08-20 14:08:13 +00:00
|
|
|
QVERIFY(menu);
|
|
|
|
QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
|
|
|
|
QVERIFY(menuPrivate);
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
2024-08-20 14:08:13 +00:00
|
|
|
menu->setPopupType(popupType);
|
|
|
|
|
2017-06-08 18:19:50 +00:00
|
|
|
QQuickMenuItem *firstItem = qobject_cast<QQuickMenuItem *>(menu->itemAt(0));
|
|
|
|
QVERIFY(firstItem);
|
2015-11-16 15:06:07 +00:00
|
|
|
QSignalSpy visibleSpy(menu, SIGNAL(visibleChanged()));
|
|
|
|
|
2022-02-25 08:43:45 +00:00
|
|
|
QVERIFY(menu->hasFocus());
|
2015-12-11 14:34:55 +00:00
|
|
|
menu->open();
|
2024-08-20 14:08:13 +00:00
|
|
|
|
|
|
|
QQuickItem *parentItem = nullptr;
|
|
|
|
if (!menuPrivate->usePopupWindow()) {
|
|
|
|
parentItem = window->property("overlay").value<QQuickOverlay*>();
|
|
|
|
} else {
|
|
|
|
QTRY_VERIFY(menuPrivate->popupWindow);
|
|
|
|
parentItem = menuPrivate->popupWindow->contentItem();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(menuPrivate->popupWindow));
|
|
|
|
QQuickTest::qWaitForPolish(menuPrivate->popupWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(visibleSpy.size(), 1);
|
2022-02-25 08:43:45 +00:00
|
|
|
QVERIFY(menu->hasActiveFocus());
|
2024-08-20 14:08:13 +00:00
|
|
|
|
|
|
|
QVERIFY(parentItem);
|
|
|
|
QVERIFY(parentItem->childItems().contains(menu->contentItem()->parentItem()));
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
2017-06-06 13:58:36 +00:00
|
|
|
QVERIFY(!firstItem->property("highlighted").toBool());
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Tab);
|
|
|
|
QVERIFY(firstItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(firstItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(firstItem->focusReason(), Qt::TabFocusReason);
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), 0);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(0));
|
|
|
|
|
2017-06-08 18:19:50 +00:00
|
|
|
QQuickMenuItem *secondItem = qobject_cast<QQuickMenuItem *>(menu->itemAt(1));
|
|
|
|
QVERIFY(secondItem);
|
2015-11-16 15:06:07 +00:00
|
|
|
QTest::keyClick(window, Qt::Key_Tab);
|
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!firstItem->isHighlighted());
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(secondItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(secondItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(secondItem->focusReason(), Qt::TabFocusReason);
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), 1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(1));
|
|
|
|
|
|
|
|
QSignalSpy secondTriggeredSpy(secondItem, SIGNAL(triggered()));
|
|
|
|
QTest::keyClick(window, Qt::Key_Space);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(secondTriggeredSpy.size(), 1);
|
|
|
|
QTRY_COMPARE(visibleSpy.size(), 2);
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!menu->isVisible());
|
2024-08-20 14:08:13 +00:00
|
|
|
QVERIFY(!parentItem->childItems().contains(menu->contentItem()));
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!firstItem->isHighlighted());
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!secondItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!secondItem->isHighlighted());
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
2019-02-01 15:03:56 +00:00
|
|
|
// Enter/return should also work.
|
|
|
|
// Open the menu.
|
2015-12-11 14:34:55 +00:00
|
|
|
menu->open();
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2024-08-20 14:08:13 +00:00
|
|
|
QCOMPARE(visibleSpy.size(), 3);
|
|
|
|
if (auto *popupWindow = menuPrivate->popupWindow) {
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(popupWindow));
|
|
|
|
QVERIFY(QQuickTest::qWaitForPolish(popupWindow));
|
|
|
|
}
|
2019-02-01 15:03:56 +00:00
|
|
|
// Give the first item focus.
|
|
|
|
QTest::keyClick(window, Qt::Key_Tab);
|
|
|
|
QVERIFY(firstItem->hasActiveFocus());
|
|
|
|
QVERIFY(firstItem->hasVisualFocus());
|
|
|
|
QVERIFY(firstItem->isHighlighted());
|
|
|
|
QCOMPARE(firstItem->focusReason(), Qt::TabFocusReason);
|
|
|
|
QCOMPARE(menu->currentIndex(), 0);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(0));
|
|
|
|
// Press enter.
|
|
|
|
QSignalSpy firstTriggeredSpy(firstItem, SIGNAL(triggered()));
|
|
|
|
QTest::keyClick(window, Qt::Key_Return);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(firstTriggeredSpy.size(), 1);
|
|
|
|
QTRY_COMPARE(visibleSpy.size(), 4);
|
2019-02-01 15:03:56 +00:00
|
|
|
QVERIFY(!menu->isVisible());
|
2024-08-20 14:08:13 +00:00
|
|
|
QVERIFY(!parentItem->childItems().contains(menu->contentItem()));
|
2019-02-01 15:03:56 +00:00
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
|
|
|
QVERIFY(!firstItem->hasVisualFocus());
|
|
|
|
QVERIFY(!firstItem->isHighlighted());
|
|
|
|
QVERIFY(!secondItem->hasActiveFocus());
|
|
|
|
QVERIFY(!secondItem->hasVisualFocus());
|
|
|
|
QVERIFY(!secondItem->isHighlighted());
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
|
|
|
menu->open();
|
2024-08-20 14:08:13 +00:00
|
|
|
if (auto *popupWindow = menuPrivate->popupWindow) {
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(popupWindow));
|
|
|
|
QVERIFY(QQuickTest::qWaitForPolish(popupWindow));
|
|
|
|
}
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2024-08-20 14:08:13 +00:00
|
|
|
QCOMPARE(visibleSpy.size(), 5);
|
|
|
|
QVERIFY(parentItem->childItems().contains(menu->contentItem()->parentItem()));
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!firstItem->isHighlighted());
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!secondItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!secondItem->isHighlighted());
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(firstItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(firstItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(firstItem->focusReason(), Qt::TabFocusReason);
|
2015-11-16 15:06:07 +00:00
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(secondItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(secondItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(secondItem->focusReason(), Qt::TabFocusReason);
|
2015-11-16 15:06:07 +00:00
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
2017-06-08 18:19:50 +00:00
|
|
|
QQuickMenuItem *thirdItem = qobject_cast<QQuickMenuItem *>(menu->itemAt(2));
|
|
|
|
QVERIFY(thirdItem);
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!firstItem->isHighlighted());
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!secondItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!secondItem->isHighlighted());
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(thirdItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(thirdItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(thirdItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(thirdItem->focusReason(), Qt::TabFocusReason);
|
2015-11-16 15:06:07 +00:00
|
|
|
|
|
|
|
// Key navigation shouldn't wrap by default.
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!firstItem->isHighlighted());
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(!secondItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!secondItem->isHighlighted());
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(thirdItem->hasActiveFocus());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(thirdItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(thirdItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(thirdItem->focusReason(), Qt::TabFocusReason);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Up);
|
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
|
|
|
QVERIFY(!firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!firstItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(secondItem->hasActiveFocus());
|
|
|
|
QVERIFY(secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(secondItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(secondItem->focusReason(), Qt::BacktabFocusReason);
|
|
|
|
QVERIFY(!thirdItem->hasActiveFocus());
|
|
|
|
QVERIFY(!thirdItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!thirdItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Backtab);
|
|
|
|
QVERIFY(firstItem->hasActiveFocus());
|
|
|
|
QVERIFY(firstItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(firstItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QCOMPARE(firstItem->focusReason(), Qt::BacktabFocusReason);
|
|
|
|
QVERIFY(!secondItem->hasActiveFocus());
|
|
|
|
QVERIFY(!secondItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!secondItem->isHighlighted());
|
2017-06-08 18:19:50 +00:00
|
|
|
QVERIFY(!thirdItem->hasActiveFocus());
|
|
|
|
QVERIFY(!thirdItem->hasVisualFocus());
|
2017-06-15 09:41:19 +00:00
|
|
|
QVERIFY(!thirdItem->isHighlighted());
|
2015-12-03 09:29:00 +00:00
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Escape);
|
2022-10-05 05:29:16 +00:00
|
|
|
QTRY_COMPARE(visibleSpy.size(), 6);
|
2015-12-03 09:29:00 +00:00
|
|
|
QVERIFY(!menu->isVisible());
|
2015-11-16 15:06:07 +00:00
|
|
|
}
|
|
|
|
|
2019-02-01 12:53:06 +00:00
|
|
|
// QTBUG-70181
|
|
|
|
void tst_QQuickMenu::disabledMenuItemKeyNavigation()
|
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2019-02-01 12:53:06 +00:00
|
|
|
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
|
|
|
|
QSKIP("This platform only allows tab focus for text controls");
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("disabledMenuItemKeyNavigation.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2019-02-01 12:53:06 +00:00
|
|
|
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2019-07-18 17:02:40 +00:00
|
|
|
centerOnScreen(window);
|
|
|
|
moveMouseAway(window);
|
2019-02-01 12:53:06 +00:00
|
|
|
window->show();
|
|
|
|
window->requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
QVERIFY(QGuiApplication::focusWindow() == window);
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
|
|
|
QQuickMenuItem *firstItem = qobject_cast<QQuickMenuItem *>(menu->itemAt(0));
|
|
|
|
QVERIFY(firstItem);
|
|
|
|
|
|
|
|
QQuickMenuItem *secondItem = qobject_cast<QQuickMenuItem *>(menu->itemAt(1));
|
|
|
|
QVERIFY(secondItem);
|
|
|
|
|
|
|
|
QQuickMenuItem *thirdItem = qobject_cast<QQuickMenuItem *>(menu->itemAt(2));
|
|
|
|
QVERIFY(thirdItem);
|
|
|
|
|
|
|
|
menu->setFocus(true);
|
|
|
|
menu->open();
|
|
|
|
QVERIFY(menu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2019-02-01 12:53:06 +00:00
|
|
|
QVERIFY(!firstItem->hasActiveFocus());
|
|
|
|
QVERIFY(!firstItem->property("highlighted").toBool());
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Tab);
|
|
|
|
QVERIFY(firstItem->hasActiveFocus());
|
|
|
|
QVERIFY(firstItem->hasVisualFocus());
|
|
|
|
QVERIFY(firstItem->isHighlighted());
|
|
|
|
QCOMPARE(firstItem->focusReason(), Qt::TabFocusReason);
|
|
|
|
QCOMPARE(menu->currentIndex(), 0);
|
|
|
|
|
|
|
|
// Shouldn't be possible to give focus to a disabled menu item.
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(!secondItem->hasActiveFocus());
|
|
|
|
QVERIFY(!secondItem->hasVisualFocus());
|
|
|
|
QVERIFY(!secondItem->isHighlighted());
|
|
|
|
QVERIFY(thirdItem->hasActiveFocus());
|
|
|
|
QVERIFY(thirdItem->hasVisualFocus());
|
|
|
|
QVERIFY(thirdItem->isHighlighted());
|
|
|
|
QCOMPARE(thirdItem->focusReason(), Qt::TabFocusReason);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Up);
|
|
|
|
QVERIFY(firstItem->hasActiveFocus());
|
|
|
|
QVERIFY(firstItem->hasVisualFocus());
|
|
|
|
QVERIFY(firstItem->isHighlighted());
|
|
|
|
QCOMPARE(firstItem->focusReason(), Qt::BacktabFocusReason);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Escape);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
2019-02-01 12:53:06 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::mnemonics()
|
2017-06-29 12:23:13 +00:00
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2017-06-29 12:23:13 +00:00
|
|
|
#ifdef Q_OS_MACOS
|
|
|
|
QSKIP("Mnemonics are not used on macOS");
|
|
|
|
#endif
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("mnemonics.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-29 12:23:13 +00:00
|
|
|
|
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
|
|
|
window->requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
|
2020-08-28 09:32:20 +00:00
|
|
|
MnemonicKeySimulator keySim(window);
|
|
|
|
|
2017-06-29 12:23:13 +00:00
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QQuickAction *action = window->property("action").value<QQuickAction *>();
|
|
|
|
QQuickMenuItem *menuItem = window->property("menuItem").value<QQuickMenuItem *>();
|
|
|
|
QQuickMenu *subMenu = window->property("subMenu").value<QQuickMenu *>();
|
|
|
|
QQuickMenuItem *subMenuItem = window->property("subMenuItem").value<QQuickMenuItem *>();
|
|
|
|
QVERIFY(menu && action && menuItem && subMenu && subMenuItem);
|
|
|
|
|
2024-08-29 12:57:04 +00:00
|
|
|
// TODO: QTBUG-128474
|
|
|
|
menu->setPopupType(QQuickPopup::Item);
|
|
|
|
|
2020-08-28 09:32:20 +00:00
|
|
|
keySim.press(Qt::Key_Alt);
|
2017-06-29 12:23:13 +00:00
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
|
|
|
|
QSignalSpy actionSpy(action, &QQuickAction::triggered);
|
|
|
|
QVERIFY(actionSpy.isValid());
|
2020-08-28 09:32:20 +00:00
|
|
|
keySim.click(Qt::Key_A); // "&Action"
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(actionSpy.size(), 1);
|
2017-06-29 12:23:13 +00:00
|
|
|
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
|
|
|
|
QSignalSpy menuItemSpy(menuItem, &QQuickMenuItem::triggered);
|
|
|
|
QVERIFY(menuItemSpy.isValid());
|
2020-08-28 09:32:20 +00:00
|
|
|
keySim.click(Qt::Key_I); // "Menu &Item"
|
|
|
|
keySim.release(Qt::Key_Alt);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(menuItemSpy.size(), 1);
|
2017-06-29 12:23:13 +00:00
|
|
|
|
2020-08-28 09:32:20 +00:00
|
|
|
keySim.press(Qt::Key_Alt);
|
2017-06-29 12:23:13 +00:00
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
|
2020-08-28 09:32:20 +00:00
|
|
|
keySim.click(Qt::Key_M); // "Sub &Menu"
|
2017-06-29 12:23:13 +00:00
|
|
|
QTRY_VERIFY(subMenu->isOpened());
|
|
|
|
|
|
|
|
QSignalSpy subMenuItemSpy(subMenuItem, &QQuickMenuItem::triggered);
|
|
|
|
QVERIFY(subMenuItemSpy.isValid());
|
2020-08-28 09:32:20 +00:00
|
|
|
keySim.click(Qt::Key_S); // "&Sub Menu Item"
|
|
|
|
keySim.release(Qt::Key_Alt);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(subMenuItemSpy.size(), 1);
|
2017-06-29 12:23:13 +00:00
|
|
|
}
|
|
|
|
|
2024-08-15 10:49:34 +00:00
|
|
|
#if QT_CONFIG(shortcut)
|
|
|
|
namespace CheckableMnemonics {
|
|
|
|
using MnemonicKey = std::pair<Qt::Key, QString>;
|
|
|
|
|
|
|
|
enum class MenuItemType {
|
|
|
|
Action,
|
|
|
|
MenuItem
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class SignalName {
|
|
|
|
CheckedChanged = 0x01,
|
|
|
|
Triggered = 0x02,
|
|
|
|
};
|
|
|
|
Q_DECLARE_FLAGS(SignalNames, SignalName);
|
|
|
|
|
|
|
|
class ItemSignalSpy
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ItemSignalSpy(MenuItemType type, QObject *item) : item(item)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case MenuItemType::Action:
|
|
|
|
initSignals<QQuickAction>(qobject_cast<QQuickAction *>(item));
|
|
|
|
break;
|
|
|
|
case MenuItemType::MenuItem:
|
|
|
|
initSignals<QQuickMenuItem>(qobject_cast<QQuickMenuItem *>(item));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] bool isValid() const
|
|
|
|
{
|
|
|
|
return ((checkedChangedSpy && checkedChangedSpy->isValid()) &&
|
|
|
|
(triggeredSpy && triggeredSpy->isValid()));
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] int signalSize(SignalName signal) const
|
|
|
|
{
|
|
|
|
constexpr int INVALID_SIZE = -1; // makes the test fail even when the signal is not expected
|
|
|
|
switch (signal) {
|
|
|
|
case SignalName::CheckedChanged:
|
|
|
|
return checkedChangedSpy ? checkedChangedSpy->size() : INVALID_SIZE;
|
|
|
|
case SignalName::Triggered:
|
|
|
|
return triggeredSpy ? triggeredSpy->size() : INVALID_SIZE;
|
|
|
|
}
|
|
|
|
Q_UNREACHABLE_RETURN(INVALID_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
template<typename Item>
|
|
|
|
void initSignals(Item *item)
|
|
|
|
{
|
|
|
|
checkedChangedSpy = std::make_unique<QSignalSpy>(item, &Item::checkedChanged);
|
|
|
|
triggeredSpy = std::make_unique<QSignalSpy>(item, &Item::triggered);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QPointer<QObject> item;
|
|
|
|
std::unique_ptr<QSignalSpy> checkedChangedSpy = nullptr;
|
|
|
|
std::unique_ptr<QSignalSpy> triggeredSpy = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::checkableMnemonics_data()
|
|
|
|
{
|
|
|
|
if (QKeySequence::mnemonic("&A").isEmpty())
|
|
|
|
QSKIP("Mnemonics are not enabled");
|
|
|
|
|
|
|
|
using namespace CheckableMnemonics;
|
|
|
|
|
|
|
|
QTest::addColumn<bool>("checkable");
|
|
|
|
QTest::addColumn<bool>("enabled");
|
|
|
|
QTest::addColumn<bool>("isSubMenu");
|
|
|
|
QTest::addColumn<MenuItemType>("itemType");
|
|
|
|
QTest::addColumn<MnemonicKey>("mnemonicKey");
|
|
|
|
QTest::addColumn<SignalNames>("expectedSignals");
|
|
|
|
|
|
|
|
QTest::addRow("checkable_enabled_action")
|
|
|
|
<< true << true << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
|
|
|
|
<< SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
|
|
|
|
QTest::addRow("checkable_disabled_action")
|
|
|
|
<< true << false << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
|
|
|
|
<< SignalNames{};
|
|
|
|
QTest::addRow("uncheckable_enabled_action")
|
|
|
|
<< false << true << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
|
|
|
|
<< SignalNames{SignalName::Triggered};
|
|
|
|
QTest::addRow("uncheckable_disabled_action")
|
|
|
|
<< false << false << false << MenuItemType::Action << MnemonicKey{Qt::Key_A, "A"}
|
|
|
|
<< SignalNames{};
|
|
|
|
|
|
|
|
QTest::addRow("checkable_enabled_menuItem")
|
|
|
|
<< true << true << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
|
|
|
|
<< SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
|
|
|
|
QTest::addRow("checkable_disabled_menuItem")
|
|
|
|
<< true << false << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
|
|
|
|
<< SignalNames{};
|
|
|
|
QTest::addRow("uncheckable_enabled_menuItem")
|
|
|
|
<< false << true << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
|
|
|
|
<< SignalNames{SignalName::Triggered};
|
|
|
|
QTest::addRow("uncheckable_disabled_menuItem")
|
|
|
|
<< false << false << false << MenuItemType::MenuItem << MnemonicKey{Qt::Key_I, "I"}
|
|
|
|
<< SignalNames{};
|
|
|
|
|
|
|
|
QTest::addRow("checkable_enabled_subMenuItem")
|
|
|
|
<< true << true << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
|
|
|
|
<< SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
|
|
|
|
QTest::addRow("checkable_disabled_subMenuItem")
|
|
|
|
<< true << false << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
|
|
|
|
<< SignalNames{};
|
|
|
|
QTest::addRow("uncheckable_enabled_subMenuItem")
|
|
|
|
<< false << true << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
|
|
|
|
<< SignalNames{SignalName::Triggered};
|
|
|
|
QTest::addRow("uncheckable_disabled_subMenuItem")
|
|
|
|
<< false << false << true << MenuItemType::MenuItem << MnemonicKey{Qt::Key_S, "S"}
|
|
|
|
<< SignalNames{};
|
|
|
|
|
|
|
|
QTest::addRow("checkable_enabled_subMenuAction")
|
|
|
|
<< true << true << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
|
|
|
|
<< SignalNames{SignalName::Triggered, SignalName::CheckedChanged};
|
|
|
|
QTest::addRow("checkable_disabled_subMenuAction")
|
|
|
|
<< true << false << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
|
|
|
|
<< SignalNames{};
|
|
|
|
QTest::addRow("uncheckable_enabled_subMenuAction")
|
|
|
|
<< false << true << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
|
|
|
|
<< SignalNames{SignalName::Triggered};
|
|
|
|
QTest::addRow("uncheckable_disabled_subMenuAction")
|
|
|
|
<< false << false << true << MenuItemType::Action << MnemonicKey{Qt::Key_U, "U"}
|
|
|
|
<< SignalNames{};
|
|
|
|
}
|
|
|
|
|
|
|
|
// QTBUG-96630
|
|
|
|
void tst_QQuickMenu::checkableMnemonics()
|
|
|
|
{
|
|
|
|
using namespace CheckableMnemonics;
|
|
|
|
|
|
|
|
QFETCH(bool, checkable);
|
|
|
|
QFETCH(bool, enabled);
|
|
|
|
QFETCH(bool, isSubMenu);
|
|
|
|
QFETCH(MenuItemType, itemType);
|
|
|
|
QFETCH(MnemonicKey, mnemonicKey);
|
|
|
|
QFETCH(SignalNames, expectedSignals);
|
|
|
|
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("mnemonics.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
|
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
|
|
|
window->requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
|
|
|
|
window->setProperty("checkable", checkable);
|
|
|
|
window->setProperty("enabled", enabled);
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
|
|
|
auto clickKey = [window](const MnemonicKey &mnemonic) mutable {
|
|
|
|
QTest::simulateEvent(window, true, mnemonic.first, Qt::NoModifier, mnemonic.second, false);
|
|
|
|
QTest::simulateEvent(window, false, mnemonic.first, Qt::NoModifier, mnemonic.second, false);
|
|
|
|
};
|
|
|
|
|
|
|
|
constexpr auto EMPTY_ITEM_NAME = "";
|
|
|
|
const char *itemName = EMPTY_ITEM_NAME;
|
|
|
|
switch (itemType) {
|
|
|
|
case MenuItemType::Action:
|
|
|
|
itemName = isSubMenu ? "subMenuAction" : "action";
|
|
|
|
break;
|
|
|
|
case MenuItemType::MenuItem:
|
|
|
|
itemName = isSubMenu ? "subMenuItem" : "menuItem";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
QCOMPARE_NE(itemName, EMPTY_ITEM_NAME);
|
|
|
|
|
|
|
|
QObject *menuItem = window->property(itemName).value<QObject*>();
|
|
|
|
QVERIFY(menuItem);
|
|
|
|
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
|
|
|
|
if (isSubMenu) {
|
|
|
|
QQuickMenu *subMenu = window->property("subMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
clickKey(MnemonicKey{Qt::Key_M, "M"}); // "Sub &Menu"
|
|
|
|
QTRY_VERIFY(subMenu->isOpened());
|
|
|
|
}
|
|
|
|
|
|
|
|
const ItemSignalSpy itemSignalSpy(itemType, menuItem);
|
|
|
|
QVERIFY(itemSignalSpy.isValid());
|
|
|
|
|
|
|
|
clickKey(mnemonicKey);
|
|
|
|
QCOMPARE(itemSignalSpy.signalSize(SignalName::CheckedChanged),
|
|
|
|
expectedSignals & SignalName::CheckedChanged ? 1 : 0);
|
|
|
|
QCOMPARE(itemSignalSpy.signalSize(SignalName::Triggered),
|
|
|
|
expectedSignals & SignalName::Triggered ? 1 : 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::menuButton()
|
2015-11-16 15:06:07 +00:00
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2015-11-16 15:06:07 +00:00
|
|
|
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)
|
|
|
|
QSKIP("This platform only allows tab focus for text controls");
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2016-09-03 13:15:53 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2015-11-16 15:06:07 +00:00
|
|
|
window->show();
|
|
|
|
window->requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
QVERIFY(QGuiApplication::focusWindow() == window);
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QQuickButton *menuButton = window->property("menuButton").value<QQuickButton*>();
|
|
|
|
QSignalSpy visibleSpy(menu, SIGNAL(visibleChanged()));
|
|
|
|
|
|
|
|
menuButton->setVisible(true);
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
menuButton->mapToScene(QPointF(menuButton->width() / 2, menuButton->height() / 2)).toPoint());
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(visibleSpy.size(), 1);
|
2015-11-16 15:06:07 +00:00
|
|
|
QVERIFY(menu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2015-11-16 15:06:07 +00:00
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Tab);
|
|
|
|
QQuickItem *firstItem = menu->itemAt(0);
|
|
|
|
QVERIFY(firstItem->hasActiveFocus());
|
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::addItem()
|
2016-05-10 11:58:05 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("addItem.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2016-09-03 13:15:53 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2016-05-10 11:58:05 +00:00
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2016-05-10 11:58:05 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
2016-05-11 13:15:17 +00:00
|
|
|
QVERIFY(menu->isVisible());
|
2016-05-10 11:58:05 +00:00
|
|
|
|
|
|
|
QQuickItem *menuItem = menu->itemAt(0);
|
2016-05-11 13:15:17 +00:00
|
|
|
QVERIFY(menuItem);
|
|
|
|
QTRY_VERIFY(!QQuickItemPrivate::get(menuItem)->culled); // QTBUG-53262
|
|
|
|
|
2016-05-10 11:58:05 +00:00
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
menuItem->mapToScene(QPointF(menuItem->width() / 2, menuItem->height() / 2)).toPoint());
|
2016-05-11 13:15:17 +00:00
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
2016-05-10 11:58:05 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::menuSeparator()
|
2016-07-21 14:04:18 +00:00
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("menuSeparator.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2016-09-06 16:52:28 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
2017-08-09 10:51:40 +00:00
|
|
|
centerOnScreen(window);
|
2017-06-29 13:15:31 +00:00
|
|
|
moveMouseAway(window);
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
tst_qquickmenu: call requestActivate before QTest::qWaitForWindowActive
On a Ubuntu 22.04.4 VM, every test after the first one that didn't call
requestActivate before QTest::qWaitForWindowActive would fail:
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
********* Start testing of tst_QQuickMenu *********
Config: Using QtTest library 6.8.0, Qt 6.8.0 (x86_64-little_endian-lp64 shared (dynamic) debug build; by GCC 11.4.0), ubuntu 22.04
PASS : tst_QQuickMenu::Basic::initTestCase()
PASS : tst_QQuickMenu::Basic::defaults()
PASS : tst_QQuickMenu::Basic::count()
PASS : tst_QQuickMenu::Basic::mouse()
PASS : tst_QQuickMenu::Basic::pressAndHold()
PASS : tst_QQuickMenu::Basic::contextMenuKeyboard()
PASS : tst_QQuickMenu::Basic::disabledMenuItemKeyNavigation()
PASS : tst_QQuickMenu::Basic::mnemonics()
PASS : tst_QQuickMenu::Basic::menuButton()
PASS : tst_QQuickMenu::Basic::addItem()
FAIL! : tst_QQuickMenu::Basic::menuSeparator() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(697)]
PASS : tst_QQuickMenu::Basic::repeater()
PASS : tst_QQuickMenu::Basic::order()
PASS : tst_QQuickMenu::Basic::popup()
PASS : tst_QQuickMenu::Basic::actions()
FAIL! : tst_QQuickMenu::Basic::actionShortcuts() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1084)]
PASS : tst_QQuickMenu::Basic::removeTakeItem()
PASS : tst_QQuickMenu::Basic::subMenuMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuMouse(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(non-cascading)
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuWithIcon()
PASS : tst_QQuickMenu::Basic::addRemoveSubMenus()
PASS : tst_QQuickMenu::Basic::scrollable(Window)
PASS : tst_QQuickMenu::Basic::scrollable(ApplicationWindow)
PASS : tst_QQuickMenu::Basic::scrollable(WithPadding)
PASS : tst_QQuickMenu::Basic::scrollable(FixedHeight)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared inside)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::menuItemWidth(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidth(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterRetranslate()
FAIL! : tst_QQuickMenu::Basic::giveMenuItemFocusOnButtonPress() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(2097)]
PASS : tst_QQuickMenu::Basic::customMenuCullItems()
PASS : tst_QQuickMenu::Basic::customMenuUseRepeaterAsTheContentItem()
PASS : tst_QQuickMenu::Basic::invalidUrlInImgTag()
PASS : tst_QQuickMenu::Basic::nativeStatic()
PASS : tst_QQuickMenu::Basic::nativeDynamicActions()
PASS : tst_QQuickMenu::Basic::nativeDynamicSubmenus()
PASS : tst_QQuickMenu::Basic::nativeMenuSeparator()
PASS : tst_QQuickMenu::Basic::dontUseNativeMenuWindowsChanges()
PASS : tst_QQuickMenu::Basic::nativeMixedItems()
PASS : tst_QQuickMenu::Basic::cleanupTestCase()
Totals: 59 passed, 11 failed, 0 skipped, 0 blacklisted, 74936ms
********* Finished testing of tst_QQuickMenu *********
Change-Id: I5ef76d2648fc7c8732aafab3a9a0a02844075c3e
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2024-03-20 07:33:47 +00:00
|
|
|
window->requestActivate();
|
2019-07-18 17:02:40 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
2016-07-21 14:04:18 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QVERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
QQuickMenuItem *newMenuItem = qobject_cast<QQuickMenuItem*>(menu->itemAt(0));
|
|
|
|
QVERIFY(newMenuItem);
|
|
|
|
QCOMPARE(newMenuItem->text(), QStringLiteral("New"));
|
|
|
|
|
|
|
|
QQuickMenuSeparator *menuSeparator = qobject_cast<QQuickMenuSeparator*>(menu->itemAt(1));
|
|
|
|
QVERIFY(menuSeparator);
|
|
|
|
|
|
|
|
QQuickMenuItem *saveMenuItem = qobject_cast<QQuickMenuItem*>(menu->itemAt(2));
|
|
|
|
QVERIFY(saveMenuItem);
|
|
|
|
QCOMPARE(saveMenuItem->text(), QStringLiteral("Save"));
|
|
|
|
QTRY_VERIFY(!QQuickItemPrivate::get(saveMenuItem)->culled); // QTBUG-53262
|
2022-07-20 15:55:21 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2016-07-21 14:04:18 +00:00
|
|
|
|
|
|
|
// Clicking on items should still close the menu.
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
newMenuItem->mapToScene(QPointF(newMenuItem->width() / 2, newMenuItem->height() / 2)).toPoint());
|
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
|
|
|
|
|
|
|
menu->open();
|
2022-07-20 15:55:21 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2016-07-21 14:04:18 +00:00
|
|
|
|
|
|
|
// Clicking on a separator shouldn't close the menu.
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
menuSeparator->mapToScene(QPointF(menuSeparator->width() / 2, menuSeparator->height() / 2)).toPoint());
|
|
|
|
QVERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
// Clicking on items should still close the menu.
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
saveMenuItem->mapToScene(QPointF(saveMenuItem->width() / 2, saveMenuItem->height() / 2)).toPoint());
|
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
2017-06-08 18:19:50 +00:00
|
|
|
|
2017-07-06 12:51:52 +00:00
|
|
|
moveMouseAway(window);
|
2017-07-03 08:32:29 +00:00
|
|
|
|
2017-06-08 18:19:50 +00:00
|
|
|
menu->open();
|
|
|
|
QVERIFY(menu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
2017-06-08 18:19:50 +00:00
|
|
|
|
|
|
|
// Key navigation skips separators
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(newMenuItem->hasActiveFocus());
|
|
|
|
QVERIFY(newMenuItem->hasVisualFocus());
|
|
|
|
QCOMPARE(newMenuItem->focusReason(), Qt::TabFocusReason);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(saveMenuItem->hasActiveFocus());
|
|
|
|
QVERIFY(saveMenuItem->hasVisualFocus());
|
|
|
|
QCOMPARE(saveMenuItem->focusReason(), Qt::TabFocusReason);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(saveMenuItem->hasActiveFocus());
|
|
|
|
QVERIFY(saveMenuItem->hasVisualFocus());
|
|
|
|
QCOMPARE(saveMenuItem->focusReason(), Qt::TabFocusReason);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Up);
|
|
|
|
QVERIFY(newMenuItem->hasActiveFocus());
|
|
|
|
QVERIFY(newMenuItem->hasVisualFocus());
|
|
|
|
QCOMPARE(newMenuItem->focusReason(), Qt::BacktabFocusReason);
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Up);
|
|
|
|
QVERIFY(newMenuItem->hasActiveFocus());
|
|
|
|
QVERIFY(newMenuItem->hasVisualFocus());
|
|
|
|
QCOMPARE(newMenuItem->focusReason(), Qt::BacktabFocusReason);
|
2016-07-21 14:04:18 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::repeater()
|
2016-12-28 19:24:18 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("repeater.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2016-12-28 19:24:18 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2016-12-28 19:24:18 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QVERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
QObject *repeater = window->property("repeater").value<QObject*>();
|
|
|
|
QVERIFY(repeater);
|
|
|
|
|
|
|
|
int count = repeater->property("count").toInt();
|
|
|
|
QCOMPARE(count, 5);
|
|
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
QQuickItem *item = menu->itemAt(i);
|
2018-11-14 10:13:15 +00:00
|
|
|
QVERIFY(item);
|
2016-12-28 19:24:18 +00:00
|
|
|
QCOMPARE(item->property("idx").toInt(), i);
|
|
|
|
|
|
|
|
QQuickItem *repeaterItem = nullptr;
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(repeater, "itemAt", Q_RETURN_ARG(QQuickItem*, repeaterItem), Q_ARG(int, i)));
|
|
|
|
QCOMPARE(item, repeaterItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
repeater->setProperty("model", 3);
|
|
|
|
|
|
|
|
count = repeater->property("count").toInt();
|
|
|
|
QCOMPARE(count, 3);
|
|
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
QQuickItem *item = menu->itemAt(i);
|
|
|
|
QVERIFY(item);
|
|
|
|
QCOMPARE(item->property("idx").toInt(), i);
|
|
|
|
|
|
|
|
QQuickItem *repeaterItem = nullptr;
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(repeater, "itemAt", Q_RETURN_ARG(QQuickItem*, repeaterItem), Q_ARG(int, i)));
|
|
|
|
QCOMPARE(item, repeaterItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
void tst_QQuickMenu::order()
|
2016-12-28 19:24:18 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("order.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2016-12-28 19:24:18 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2016-12-28 19:24:18 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QVERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
const QStringList texts = {"dynamic_0", "static_1", "repeated_2", "repeated_3", "static_4", "dynamic_5", "dynamic_6"};
|
|
|
|
|
2022-10-05 05:29:16 +00:00
|
|
|
for (int i = 0; i < texts.size(); ++i) {
|
2016-12-28 19:24:18 +00:00
|
|
|
QQuickItem *item = menu->itemAt(i);
|
|
|
|
QVERIFY(item);
|
|
|
|
QCOMPARE(item->property("text").toString(), texts.at(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-28 08:05:18 +00:00
|
|
|
#if QT_CONFIG(cursor)
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::popup()
|
2017-05-17 15:35:00 +00:00
|
|
|
{
|
2022-06-28 08:05:18 +00:00
|
|
|
#if defined(Q_OS_ANDROID)
|
|
|
|
QSKIP("Setting cursor position is not supported on Android");
|
|
|
|
#endif
|
|
|
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland")))
|
|
|
|
QSKIP("Setting cursor position is not supported on Wayland");
|
|
|
|
|
2022-07-21 15:44:03 +00:00
|
|
|
// Try moving the cursor from the current position
|
|
|
|
// Skip if it fails since the test relies on moving the cursor
|
|
|
|
const QPoint point = QCursor::pos() + QPoint(1, 1);
|
|
|
|
QCursor::setPos(point);
|
|
|
|
if (!QTest::qWaitFor([point]{ return QCursor::pos() == point; }))
|
|
|
|
QSKIP("Setting cursor position is not supported on this platform");
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("popup.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-07-11 12:20:37 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2017-08-09 10:51:40 +00:00
|
|
|
centerOnScreen(window);
|
2017-06-29 13:15:31 +00:00
|
|
|
moveMouseAway(window);
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2017-05-17 15:35:00 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
|
|
|
QQuickMenuItem *menuItem1 = window->property("menuItem1").value<QQuickMenuItem *>();
|
|
|
|
QVERIFY(menuItem1);
|
|
|
|
|
|
|
|
QQuickMenuItem *menuItem2 = window->property("menuItem2").value<QQuickMenuItem *>();
|
|
|
|
QVERIFY(menuItem2);
|
|
|
|
|
|
|
|
QQuickMenuItem *menuItem3 = window->property("menuItem3").value<QQuickMenuItem *>();
|
|
|
|
QVERIFY(menuItem3);
|
|
|
|
|
2017-07-11 12:20:37 +00:00
|
|
|
QQuickItem *button = window->property("button").value<QQuickItem *>();
|
|
|
|
QVERIFY(button);
|
|
|
|
|
2017-06-10 08:18:21 +00:00
|
|
|
QPoint oldCursorPos = QCursor::pos();
|
2017-05-17 15:35:00 +00:00
|
|
|
QPoint cursorPos = window->mapToGlobal(QPoint(11, 22));
|
|
|
|
QCursor::setPos(cursorPos);
|
|
|
|
QTRY_COMPARE(QCursor::pos(), cursorPos);
|
|
|
|
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupAtCursor"));
|
2017-07-11 12:20:37 +00:00
|
|
|
QCOMPARE(menu->parentItem(), window->contentItem());
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
2017-06-06 18:56:54 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
|
2019-04-29 11:03:25 +00:00
|
|
|
const qreal elevenOrLeftMargin = qMax(qreal(11), menu->leftMargin());
|
|
|
|
const qreal twentyTwoOrTopMargin = qMax(qreal(22), menu->topMargin());
|
|
|
|
// If the Menu has large margins, it may be moved to stay within them.
|
|
|
|
// QTBUG-75503: QTRY_COMPARE doesn't use qFuzzyCompare() in all cases,
|
|
|
|
// meaning a lot of these comparisons could trigger a 10 second wait;
|
|
|
|
// use QTRY_VERIFY and qFuzzyCompare instead.
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), elevenOrLeftMargin));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), twentyTwoOrTopMargin));
|
2017-05-17 15:35:00 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupAtPos", Q_ARG(QVariant, QPointF(33, 44))));
|
2017-07-11 12:20:37 +00:00
|
|
|
QCOMPARE(menu->parentItem(), window->contentItem());
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
2017-06-06 18:56:54 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
|
2022-08-01 16:26:48 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), qMax(qreal(33), menu->leftMargin())));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), qMax(qreal(44), menu->topMargin())));
|
2017-05-17 15:35:00 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupAtCoord", Q_ARG(QVariant, 55), Q_ARG(QVariant, 66)));
|
2017-07-11 12:20:37 +00:00
|
|
|
QCOMPARE(menu->parentItem(), window->contentItem());
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
|
2022-08-01 16:26:48 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), qMax(qreal(55), menu->leftMargin())));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), qMax(qreal(66), menu->topMargin())));
|
2017-07-11 12:20:37 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
menu->setParentItem(nullptr);
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupAtParentCursor", Q_ARG(QVariant, QVariant::fromValue(button))));
|
|
|
|
QCOMPARE(menu->parentItem(), button);
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), button->mapFromScene(QPointF(elevenOrLeftMargin, twentyTwoOrTopMargin)).x()));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), button->mapFromScene(QPointF(elevenOrLeftMargin, twentyTwoOrTopMargin)).y()));
|
2017-07-11 12:20:37 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
menu->setParentItem(nullptr);
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupAtParentPos", Q_ARG(QVariant, QVariant::fromValue(button)), Q_ARG(QVariant, QPointF(-11, -22))));
|
|
|
|
QCOMPARE(menu->parentItem(), button);
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
|
2019-04-29 11:03:25 +00:00
|
|
|
// Don't need to worry about margins here because we're opening close
|
|
|
|
// to the center of the window.
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), -11));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), -22));
|
2024-08-29 12:28:52 +00:00
|
|
|
QCOMPARE(menu->popupItem()->mapToGlobal({0,0}).toPoint(), button->mapToGlobal({-11, -22}).toPoint());
|
2017-07-11 12:20:37 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
menu->setParentItem(nullptr);
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupAtParentCoord", Q_ARG(QVariant, QVariant::fromValue(button)), Q_ARG(QVariant, -33), Q_ARG(QVariant, -44)));
|
|
|
|
QCOMPARE(menu->parentItem(), button);
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
2017-06-06 18:56:54 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), -1);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), -33));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), -44));
|
2024-08-29 12:28:52 +00:00
|
|
|
QCOMPARE(menu->popupItem()->mapToGlobal({0,0}).toPoint(), button->mapToGlobal({-33, -44}).toPoint());
|
2017-05-17 15:35:00 +00:00
|
|
|
menu->close();
|
|
|
|
|
2019-04-29 11:03:25 +00:00
|
|
|
const qreal twelveOrLeftMargin = qMax(qreal(12), menu->leftMargin());
|
|
|
|
cursorPos = window->mapToGlobal(QPoint(twelveOrLeftMargin, window->height() / 2));
|
2017-05-17 15:35:00 +00:00
|
|
|
QCursor::setPos(cursorPos);
|
|
|
|
QTRY_COMPARE(QCursor::pos(), cursorPos);
|
|
|
|
|
|
|
|
const QList<QQuickMenuItem *> menuItems = QList<QQuickMenuItem *>() << menuItem1 << menuItem2 << menuItem3;
|
|
|
|
for (QQuickMenuItem *menuItem : menuItems) {
|
2017-07-11 12:20:37 +00:00
|
|
|
menu->resetParentItem();
|
|
|
|
|
2017-05-17 15:35:00 +00:00
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupItemAtCursor", Q_ARG(QVariant, QVariant::fromValue(menuItem))));
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), menuItems.indexOf(menuItem));
|
2017-06-06 18:56:54 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), menuItems.indexOf(menuItem));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), twelveOrLeftMargin));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), window->height() / 2 - menu->topPadding() - menuItem->y()));
|
2017-05-17 15:35:00 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupItemAtPos", Q_ARG(QVariant, QPointF(33, window->height() / 3)), Q_ARG(QVariant, QVariant::fromValue(menuItem))));
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), menuItems.indexOf(menuItem));
|
2017-06-06 18:56:54 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), menuItems.indexOf(menuItem));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), 33));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), window->height() / 3 - menu->topPadding() - menuItem->y()));
|
2017-05-17 15:35:00 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupItemAtCoord", Q_ARG(QVariant, 55), Q_ARG(QVariant, window->height() / 3 * 2), Q_ARG(QVariant, QVariant::fromValue(menuItem))));
|
2017-06-14 22:16:57 +00:00
|
|
|
QCOMPARE(menu->currentIndex(), menuItems.indexOf(menuItem));
|
2017-06-06 18:56:54 +00:00
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), menuItems.indexOf(menuItem));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), 55));
|
|
|
|
QTRY_COMPARE_WITH_TIMEOUT(menu->y(), window->height() / 3 * 2 - menu->topPadding() - menuItem->y(), 500);
|
2017-07-11 12:20:37 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
menu->setParentItem(nullptr);
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupItemAtParentCursor", Q_ARG(QVariant, QVariant::fromValue(button)), Q_ARG(QVariant, QVariant::fromValue(menuItem))));
|
|
|
|
QCOMPARE(menu->parentItem(), button);
|
|
|
|
QCOMPARE(menu->currentIndex(), menuItems.indexOf(menuItem));
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), menuItems.indexOf(menuItem));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), button->mapFromScene(QPoint(twelveOrLeftMargin, window->height() / 2)).x()));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), button->mapFromScene(QPoint(twelveOrLeftMargin, window->height() / 2)).y() - menu->topPadding() - menuItem->y()));
|
2017-07-11 12:20:37 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
menu->setParentItem(nullptr);
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupItemAtParentPos", Q_ARG(QVariant, QVariant::fromValue(button)), Q_ARG(QVariant, QPointF(-11, -22)), Q_ARG(QVariant, QVariant::fromValue(menuItem))));
|
|
|
|
QCOMPARE(menu->parentItem(), button);
|
|
|
|
QCOMPARE(menu->currentIndex(), menuItems.indexOf(menuItem));
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), menuItems.indexOf(menuItem));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), -11));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), -22 - menu->topPadding() - menuItem->y()));
|
2024-08-29 12:28:52 +00:00
|
|
|
QCOMPARE(menu->popupItem()->mapToGlobal({0,0}).toPoint(), button->mapToGlobal({-11, -22 - menu->topPadding() - menuItem->y()}).toPoint());
|
2017-07-11 12:20:37 +00:00
|
|
|
menu->close();
|
|
|
|
|
|
|
|
menu->setParentItem(nullptr);
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "popupItemAtParentCoord", Q_ARG(QVariant, QVariant::fromValue(button)), Q_ARG(QVariant, -33), Q_ARG(QVariant, -44), Q_ARG(QVariant, QVariant::fromValue(menuItem))));
|
|
|
|
QCOMPARE(menu->parentItem(), button);
|
|
|
|
QCOMPARE(menu->currentIndex(), menuItems.indexOf(menuItem));
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex").toInt(), menuItems.indexOf(menuItem));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->x(), -33));
|
|
|
|
QTRY_VERIFY(qFuzzyCompare(menu->y(), -44 - menu->topPadding() - menuItem->y()));
|
2024-08-29 12:28:52 +00:00
|
|
|
QCOMPARE(menu->popupItem()->mapToGlobal({0,0}).toPoint(), button->mapToGlobal({-33, -44 - menu->topPadding() - menuItem->y()}).toPoint());
|
2017-05-17 15:35:00 +00:00
|
|
|
menu->close();
|
|
|
|
}
|
2017-06-10 08:18:21 +00:00
|
|
|
|
|
|
|
QCursor::setPos(oldCursorPos);
|
|
|
|
QTRY_COMPARE(QCursor::pos(), oldCursorPos);
|
2017-05-17 15:35:00 +00:00
|
|
|
}
|
2022-06-28 08:05:18 +00:00
|
|
|
#endif // QT_CONFIG(cursor)
|
2017-05-17 15:35:00 +00:00
|
|
|
|
2024-03-14 10:07:30 +00:00
|
|
|
void tst_QQuickMenu::openParentlessMenu()
|
|
|
|
{
|
|
|
|
// Check that we don't get a crash if the application sets a menu's parentItem
|
|
|
|
// to null. This will also result in the menu not showing at all, since it's
|
|
|
|
// no longer a part of the scene. Even if this limitiation is technically only
|
|
|
|
// relevant for non-native menus, we enforce it also for native menus to ensure
|
|
|
|
// that an application works the same on all platforms.
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("popup.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
centerOnScreen(window);
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("cannot show menu: parent is null"));
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->setParentItem(nullptr);
|
|
|
|
menu->popup();
|
|
|
|
QVERIFY(!menu->isVisible());
|
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::actions()
|
2017-06-01 18:09:44 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("actions.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-01 18:09:44 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2017-06-01 18:09:44 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
2017-07-10 09:59:47 +00:00
|
|
|
QPointer<QQuickAction> action1 = menu->actionAt(0);
|
|
|
|
QVERIFY(!action1.isNull());
|
|
|
|
|
|
|
|
QPointer<QQuickAction> action3 = menu->actionAt(2);
|
|
|
|
QVERIFY(!action3.isNull());
|
|
|
|
|
|
|
|
QVERIFY(!menu->actionAt(1));
|
|
|
|
QVERIFY(!menu->actionAt(3));
|
|
|
|
|
2017-06-09 08:09:28 +00:00
|
|
|
QPointer<QQuickMenuItem> menuItem1 = qobject_cast<QQuickMenuItem *>(menu->itemAt(0));
|
|
|
|
QVERIFY(!menuItem1.isNull());
|
2017-07-10 09:59:47 +00:00
|
|
|
QCOMPARE(menuItem1->action(), action1.data());
|
2017-06-01 18:09:44 +00:00
|
|
|
QCOMPARE(menuItem1->text(), "action1");
|
|
|
|
|
2017-06-09 08:09:28 +00:00
|
|
|
QPointer<QQuickMenuItem> menuItem2 = qobject_cast<QQuickMenuItem *>(menu->itemAt(1));
|
|
|
|
QVERIFY(!menuItem2.isNull());
|
2017-06-01 18:09:44 +00:00
|
|
|
QVERIFY(!menuItem2->action());
|
|
|
|
QCOMPARE(menuItem2->text(), "menuitem2");
|
|
|
|
|
2017-06-09 08:09:28 +00:00
|
|
|
QPointer<QQuickMenuItem> menuItem3 = qobject_cast<QQuickMenuItem *>(menu->itemAt(2));
|
|
|
|
QVERIFY(!menuItem3.isNull());
|
2017-07-10 09:59:47 +00:00
|
|
|
QCOMPARE(menuItem3->action(), action3.data());
|
2017-06-01 18:09:44 +00:00
|
|
|
QCOMPARE(menuItem3->text(), "action3");
|
|
|
|
|
2017-06-09 08:09:28 +00:00
|
|
|
QPointer<QQuickMenuItem> menuItem4 = qobject_cast<QQuickMenuItem *>(menu->itemAt(3));
|
|
|
|
QVERIFY(!menuItem4.isNull());
|
2017-06-01 18:09:44 +00:00
|
|
|
QVERIFY(!menuItem4->action());
|
|
|
|
QCOMPARE(menuItem4->text(), "menuitem4");
|
2017-06-09 08:09:28 +00:00
|
|
|
|
|
|
|
// takeAction(int) does not destroy the action, but does destroy the respective item
|
|
|
|
QCOMPARE(menu->takeAction(0), action1.data());
|
|
|
|
QVERIFY(!menu->itemAt(3));
|
|
|
|
QCoreApplication::sendPostedEvents(action1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(!action1.isNull());
|
|
|
|
QCoreApplication::sendPostedEvents(menuItem1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(menuItem1.isNull());
|
|
|
|
|
|
|
|
// takeAction(int) does not destroy an item that doesn't have an action
|
|
|
|
QVERIFY(!menuItem2->subMenu());
|
|
|
|
QVERIFY(!menu->takeAction(0));
|
|
|
|
QCoreApplication::sendPostedEvents(menuItem2, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(!menuItem2.isNull());
|
|
|
|
|
|
|
|
// addAction(Action) re-creates the respective item in the menu
|
|
|
|
menu->addAction(action1);
|
|
|
|
menuItem1 = qobject_cast<QQuickMenuItem *>(menu->itemAt(3));
|
|
|
|
QVERIFY(!menuItem1.isNull());
|
|
|
|
QCOMPARE(menuItem1->action(), action1.data());
|
|
|
|
|
|
|
|
// removeAction(Action) destroys both the action and the respective item
|
|
|
|
menu->removeAction(action1);
|
|
|
|
QVERIFY(!menu->itemAt(3));
|
|
|
|
QCoreApplication::sendPostedEvents(action1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(action1.isNull());
|
|
|
|
QCoreApplication::sendPostedEvents(menuItem1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(menuItem1.isNull());
|
2017-06-01 18:09:44 +00:00
|
|
|
}
|
|
|
|
|
2022-03-10 08:47:34 +00:00
|
|
|
#if QT_CONFIG(shortcut)
|
|
|
|
void tst_QQuickMenu::actionShortcuts()
|
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2022-03-10 08:47:34 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("actionShortcuts.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
tst_qquickmenu: call requestActivate before QTest::qWaitForWindowActive
On a Ubuntu 22.04.4 VM, every test after the first one that didn't call
requestActivate before QTest::qWaitForWindowActive would fail:
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
********* Start testing of tst_QQuickMenu *********
Config: Using QtTest library 6.8.0, Qt 6.8.0 (x86_64-little_endian-lp64 shared (dynamic) debug build; by GCC 11.4.0), ubuntu 22.04
PASS : tst_QQuickMenu::Basic::initTestCase()
PASS : tst_QQuickMenu::Basic::defaults()
PASS : tst_QQuickMenu::Basic::count()
PASS : tst_QQuickMenu::Basic::mouse()
PASS : tst_QQuickMenu::Basic::pressAndHold()
PASS : tst_QQuickMenu::Basic::contextMenuKeyboard()
PASS : tst_QQuickMenu::Basic::disabledMenuItemKeyNavigation()
PASS : tst_QQuickMenu::Basic::mnemonics()
PASS : tst_QQuickMenu::Basic::menuButton()
PASS : tst_QQuickMenu::Basic::addItem()
FAIL! : tst_QQuickMenu::Basic::menuSeparator() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(697)]
PASS : tst_QQuickMenu::Basic::repeater()
PASS : tst_QQuickMenu::Basic::order()
PASS : tst_QQuickMenu::Basic::popup()
PASS : tst_QQuickMenu::Basic::actions()
FAIL! : tst_QQuickMenu::Basic::actionShortcuts() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1084)]
PASS : tst_QQuickMenu::Basic::removeTakeItem()
PASS : tst_QQuickMenu::Basic::subMenuMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuMouse(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(non-cascading)
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuWithIcon()
PASS : tst_QQuickMenu::Basic::addRemoveSubMenus()
PASS : tst_QQuickMenu::Basic::scrollable(Window)
PASS : tst_QQuickMenu::Basic::scrollable(ApplicationWindow)
PASS : tst_QQuickMenu::Basic::scrollable(WithPadding)
PASS : tst_QQuickMenu::Basic::scrollable(FixedHeight)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared inside)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::menuItemWidth(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidth(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterRetranslate()
FAIL! : tst_QQuickMenu::Basic::giveMenuItemFocusOnButtonPress() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(2097)]
PASS : tst_QQuickMenu::Basic::customMenuCullItems()
PASS : tst_QQuickMenu::Basic::customMenuUseRepeaterAsTheContentItem()
PASS : tst_QQuickMenu::Basic::invalidUrlInImgTag()
PASS : tst_QQuickMenu::Basic::nativeStatic()
PASS : tst_QQuickMenu::Basic::nativeDynamicActions()
PASS : tst_QQuickMenu::Basic::nativeDynamicSubmenus()
PASS : tst_QQuickMenu::Basic::nativeMenuSeparator()
PASS : tst_QQuickMenu::Basic::dontUseNativeMenuWindowsChanges()
PASS : tst_QQuickMenu::Basic::nativeMixedItems()
PASS : tst_QQuickMenu::Basic::cleanupTestCase()
Totals: 59 passed, 11 failed, 0 skipped, 0 blacklisted, 74936ms
********* Finished testing of tst_QQuickMenu *********
Change-Id: I5ef76d2648fc7c8732aafab3a9a0a02844075c3e
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2024-03-20 07:33:47 +00:00
|
|
|
window->requestActivate();
|
2022-03-10 08:47:34 +00:00
|
|
|
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);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(action1TriggeredSpy.size(), 1);
|
2022-03-10 08:47:34 +00:00
|
|
|
|
|
|
|
// 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);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(subMenuAction1TriggeredSpy.size(), 1);
|
2022-03-10 08:47:34 +00:00
|
|
|
|
|
|
|
// 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);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(buttonMenuAction1TriggeredSpy.size(), 1);
|
2022-03-10 08:47:34 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::removeTakeItem()
|
2017-06-02 14:18:56 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("removeTakeItem.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-02 14:18:56 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2017-06-02 14:18:56 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
|
|
|
QPointer<QQuickMenuItem> menuItem1 = window->property("menuItem1").value<QQuickMenuItem *>();
|
|
|
|
QVERIFY(!menuItem1.isNull());
|
|
|
|
QCOMPARE(menuItem1->menu(), menu);
|
|
|
|
|
|
|
|
QPointer<QQuickMenuItem> menuItem2 = window->property("menuItem2").value<QQuickMenuItem *>();
|
|
|
|
QVERIFY(!menuItem2.isNull());
|
|
|
|
QCOMPARE(menuItem2->menu(), menu);
|
|
|
|
|
|
|
|
QPointer<QQuickMenuItem> menuItem3 = window->property("menuItem3").value<QQuickMenuItem *>();
|
|
|
|
QVERIFY(!menuItem3.isNull());
|
|
|
|
QCOMPARE(menuItem3->menu(), menu);
|
|
|
|
|
|
|
|
// takeItem(int) does not destroy
|
|
|
|
QVariant ret;
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "takeSecondItem", Q_RETURN_ARG(QVariant, ret)));
|
|
|
|
QCOMPARE(ret.value<QQuickMenuItem *>(), menuItem2);
|
|
|
|
QVERIFY(!menuItem2->menu());
|
|
|
|
QCoreApplication::sendPostedEvents(menuItem2, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(!menuItem2.isNull());
|
|
|
|
|
|
|
|
// removeItem(Item) destroys
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "removeFirstItem"));
|
|
|
|
QVERIFY(!menuItem1->menu());
|
|
|
|
QCoreApplication::sendPostedEvents(menuItem1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(menuItem1.isNull());
|
|
|
|
|
|
|
|
// removeItem(null) must not call removeItem(0)
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "removeNullItem"));
|
|
|
|
QCOMPARE(menuItem3->menu(), menu);
|
|
|
|
QCoreApplication::sendPostedEvents(menuItem3, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(!menuItem3.isNull());
|
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::subMenuMouse_data()
|
2017-06-01 21:36:57 +00:00
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("cascade");
|
2024-08-20 17:29:02 +00:00
|
|
|
QTest::addColumn<QQuickPopup::PopupType>("popupType");
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2024-08-20 17:29:02 +00:00
|
|
|
QTest::newRow("cascading, in-scene") << true << QQuickPopup::Item;
|
|
|
|
QTest::newRow("non-cascading, in-scene") << false << QQuickPopup::Item;
|
|
|
|
if (popupWindowsSupported) {
|
|
|
|
QTest::newRow("cascading, popup windows") << true << QQuickPopup::Window;
|
|
|
|
QTest::newRow("non-cascading, popup windows") << false << QQuickPopup::Window;
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::subMenuMouse()
|
2017-06-01 21:36:57 +00:00
|
|
|
{
|
2017-06-05 09:48:40 +00:00
|
|
|
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|
|
|
|
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
|
|
|
|
QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
|
|
|
|
|
2017-06-01 21:36:57 +00:00
|
|
|
QFETCH(bool, cascade);
|
2024-08-20 17:29:02 +00:00
|
|
|
QFETCH(QQuickPopup::PopupType, popupType);
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenus.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-01 21:36:57 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2017-08-09 10:51:40 +00:00
|
|
|
centerOnScreen(window);
|
2017-06-01 21:36:57 +00:00
|
|
|
moveMouseAway(window);
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
mainMenu->setCascade(cascade);
|
2024-08-20 17:29:02 +00:00
|
|
|
mainMenu->setPopupType(popupType);
|
2017-06-01 21:36:57 +00:00
|
|
|
QCOMPARE(mainMenu->cascade(), cascade);
|
|
|
|
|
|
|
|
QQuickMenu *subMenu1 = window->property("subMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu1);
|
2022-04-22 15:01:14 +00:00
|
|
|
subMenu1->setCascade(cascade);
|
|
|
|
QCOMPARE(subMenu1->cascade(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
QQuickMenu *subMenu2 = window->property("subMenu2").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu2);
|
2022-04-22 15:01:14 +00:00
|
|
|
subMenu2->setCascade(cascade);
|
|
|
|
QCOMPARE(subMenu2->cascade(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
QQuickMenu *subSubMenu1 = window->property("subSubMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subSubMenu1);
|
2022-04-22 15:01:14 +00:00
|
|
|
subSubMenu1->setCascade(cascade);
|
|
|
|
QCOMPARE(subSubMenu1->cascade(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
mainMenu->open();
|
|
|
|
QVERIFY(mainMenu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
2024-08-20 17:29:02 +00:00
|
|
|
|
|
|
|
auto *mainMenuPrivate = QQuickMenuPrivate::get(mainMenu);
|
|
|
|
QCOMPARE(mainMenuPrivate->resolvedPopupType(), popupType);
|
|
|
|
if (mainMenuPrivate->usePopupWindow()) {
|
|
|
|
QTRY_VERIFY(mainMenuPrivate->popupWindow);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(mainMenuPrivate->popupWindow));
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
|
|
|
|
// open the sub-menu with mouse click
|
|
|
|
QQuickMenuItem *subMenu1Item = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(1));
|
|
|
|
QVERIFY(subMenu1Item);
|
|
|
|
QCOMPARE(subMenu1Item->subMenu(), subMenu1);
|
2024-08-20 17:29:02 +00:00
|
|
|
const QPoint subMenu1ItemPos = subMenu1Item->mapToGlobal(QPoint(1, 1)).toPoint();
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, window->mapFromGlobal(subMenu1ItemPos));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(mainMenu->isVisible(), cascade);
|
2024-08-20 17:29:02 +00:00
|
|
|
|
|
|
|
auto *subMenu1Private = QQuickMenuPrivate::get(subMenu1);
|
|
|
|
QCOMPARE(subMenu1Private->resolvedPopupType(), popupType);
|
|
|
|
if (mainMenuPrivate->usePopupWindow()) {
|
|
|
|
QTRY_VERIFY(subMenu1Private->popupWindow);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(subMenu1Private->popupWindow));
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(subMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(subMenu1->isOpened());
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
|
2022-04-22 15:01:14 +00:00
|
|
|
// on Android mouse hover will not open and close sub-menus
|
|
|
|
#ifndef Q_OS_ANDROID
|
2017-06-01 21:36:57 +00:00
|
|
|
// open the cascading sub-sub-menu with mouse hover
|
|
|
|
QQuickMenuItem *subSubMenu1Item = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(2));
|
|
|
|
QVERIFY(subSubMenu1Item);
|
|
|
|
QCOMPARE(subSubMenu1Item->subMenu(), subSubMenu1);
|
2024-08-20 17:29:02 +00:00
|
|
|
const QPoint subSubMenu1ItemPos = subSubMenu1Item->mapToGlobal(QPoint(1, 1)).toPoint();
|
|
|
|
QTest::mouseMove(window, window->mapFromGlobal(subSubMenu1ItemPos));
|
2017-06-01 21:36:57 +00:00
|
|
|
QCOMPARE(mainMenu->isVisible(), cascade);
|
|
|
|
QVERIFY(subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
2017-06-08 05:12:53 +00:00
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
if (cascade) {
|
2017-06-08 05:12:53 +00:00
|
|
|
QTRY_VERIFY(subSubMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(subSubMenu1->isOpened());
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
// close the sub-sub-menu with mouse hover over another parent menu item
|
|
|
|
QQuickMenuItem *subMenuItem1 = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(0));
|
|
|
|
QVERIFY(subMenuItem1);
|
|
|
|
QVERIFY(!subMenuItem1->subMenu());
|
2024-08-20 17:29:02 +00:00
|
|
|
const QPoint subMenuItem1Pos = subMenuItem1->mapToGlobal(QPoint(1, 1)).toPoint();
|
|
|
|
QTest::mouseMove(window, window->mapFromGlobal(subMenuItem1Pos));
|
2017-06-01 21:36:57 +00:00
|
|
|
QCOMPARE(mainMenu->isVisible(), cascade);
|
|
|
|
QVERIFY(subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!subSubMenu1->isVisible());
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
// re-open the sub-sub-menu with mouse hover
|
2024-08-20 17:29:02 +00:00
|
|
|
QTest::mouseMove(window, window->mapFromGlobal(subSubMenu1ItemPos));
|
2017-06-01 21:36:57 +00:00
|
|
|
QCOMPARE(mainMenu->isVisible(), cascade);
|
|
|
|
QVERIFY(subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
2019-05-06 15:04:27 +00:00
|
|
|
if (!cascade) {
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
} else {
|
2017-06-08 05:12:53 +00:00
|
|
|
QTRY_VERIFY(subSubMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(subSubMenu1->isOpened());
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
// close sub-menu and sub-sub-menu with mouse hover in the main menu
|
|
|
|
QQuickMenuItem *mainMenuItem1 = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(0));
|
|
|
|
QVERIFY(mainMenuItem1);
|
2024-08-20 17:29:02 +00:00
|
|
|
const QPoint mainMenuItem1Pos = mainMenuItem1->mapToGlobal(QPoint(1, 1)).toPoint();
|
|
|
|
QTest::mouseMove(window, window->mapFromGlobal(mainMenuItem1Pos));
|
|
|
|
|
2017-06-01 21:36:57 +00:00
|
|
|
QCOMPARE(mainMenu->isVisible(), cascade);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(subMenu1->isVisible(), !cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
2024-08-20 17:29:02 +00:00
|
|
|
|
|
|
|
// close all menus by click triggering an item
|
|
|
|
QQuickAbstractButton *itemToClick = cascade ? mainMenuItem1 : subMenuItem1;
|
|
|
|
QVERIFY(itemToClick->isVisible());
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, window->mapFromGlobal(itemToClick->mapToGlobal({1,1}).toPoint()));
|
2022-04-22 15:01:14 +00:00
|
|
|
#else
|
|
|
|
QQuickMenuItem *mainMenuItem1 = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(0));
|
|
|
|
QVERIFY(mainMenuItem1);
|
2024-08-20 17:29:02 +00:00
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, mainMenuItem1->mapToScene({1,1}).toPoint());
|
2022-04-22 15:01:14 +00:00
|
|
|
#endif // !Q_OS_ANDROID
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!subMenu1->isVisible());
|
2024-08-20 17:29:02 +00:00
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
}
|
|
|
|
|
2019-01-28 12:14:30 +00:00
|
|
|
void tst_QQuickMenu::subMenuDisabledMouse_data()
|
|
|
|
{
|
|
|
|
subMenuMouse_data();
|
|
|
|
}
|
|
|
|
|
|
|
|
// QTBUG-69540
|
|
|
|
void tst_QQuickMenu::subMenuDisabledMouse()
|
|
|
|
{
|
|
|
|
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|
|
|
|
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
|
|
|
|
QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
|
|
|
|
|
|
|
|
QFETCH(bool, cascade);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenuDisabled.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2019-01-28 12:14:30 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
centerOnScreen(window);
|
|
|
|
moveMouseAway(window);
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2019-01-28 12:14:30 +00:00
|
|
|
|
|
|
|
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
mainMenu->setCascade(cascade);
|
|
|
|
QCOMPARE(mainMenu->cascade(), cascade);
|
2024-08-29 12:57:04 +00:00
|
|
|
// TODO: QTBUG-128474
|
|
|
|
mainMenu->setPopupType(QQuickPopup::Item);
|
2019-01-28 12:14:30 +00:00
|
|
|
|
|
|
|
QQuickMenuItem *menuItem1 = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(0));
|
|
|
|
QVERIFY(menuItem1);
|
|
|
|
|
|
|
|
QQuickMenu *subMenu = window->property("subMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
|
|
|
|
mainMenu->open();
|
2022-07-21 15:44:03 +00:00
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
2019-01-28 12:14:30 +00:00
|
|
|
QVERIFY(!menuItem1->isHighlighted());
|
|
|
|
QVERIFY(!subMenu->isVisible());
|
|
|
|
|
2022-07-21 15:44:03 +00:00
|
|
|
// Hover-highlighting does not work on Android
|
|
|
|
#ifndef Q_OS_ANDROID
|
|
|
|
// Generate a hover event to set the current index
|
|
|
|
QTest::mouseMove(window, menuItem1->mapToScene(QPoint(2, 2)).toPoint());
|
|
|
|
QTRY_VERIFY(menuItem1->isHighlighted());
|
|
|
|
#endif
|
2019-01-28 12:14:30 +00:00
|
|
|
// Open the sub-menu with a mouse click.
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, menuItem1->mapToScene(QPoint(1, 1)).toPoint());
|
2022-07-21 15:44:03 +00:00
|
|
|
// Need to use the TRY variant here,
|
|
|
|
// as e.g. Material, iOS style menus have transitions and don't open/close immediately.
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(mainMenu->isVisible(), cascade);
|
2022-07-21 15:44:03 +00:00
|
|
|
QTRY_VERIFY(subMenu->isOpened());
|
2020-09-03 20:54:36 +00:00
|
|
|
QTRY_VERIFY(menuItem1->isHighlighted());
|
2019-01-28 12:14:30 +00:00
|
|
|
// Now the sub-menu is open. The current behavior is that the first menu item
|
|
|
|
// in the new menu is highlighted; make sure that we choose the next item if
|
|
|
|
// the first is disabled.
|
|
|
|
QQuickMenuItem *subMenuItem1 = qobject_cast<QQuickMenuItem *>(subMenu->itemAt(0));
|
|
|
|
QVERIFY(subMenuItem1);
|
|
|
|
QQuickMenuItem *subMenuItem2 = qobject_cast<QQuickMenuItem *>(subMenu->itemAt(1));
|
|
|
|
QVERIFY(subMenuItem2);
|
|
|
|
QVERIFY(!subMenuItem1->isHighlighted());
|
|
|
|
QVERIFY(subMenuItem2->isHighlighted());
|
|
|
|
|
|
|
|
// Close all menus by clicking on the item that isn't disabled.
|
2024-08-22 12:34:53 +00:00
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, window->mapFromGlobal(subMenuItem2->mapToGlobal(QPoint(1, 1)).toPoint()));
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
|
|
|
QTRY_VERIFY(!subMenu->isVisible());
|
2019-01-28 12:14:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::subMenuKeyboard_data()
|
2017-06-01 21:36:57 +00:00
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("cascade");
|
|
|
|
QTest::addColumn<bool>("mirrored");
|
|
|
|
|
|
|
|
QTest::newRow("cascading") << true << false;
|
|
|
|
QTest::newRow("cascading,mirrored") << true << true;
|
|
|
|
QTest::newRow("non-cascading") << false << false;
|
|
|
|
QTest::newRow("non-cascading,mirrored") << false << true;
|
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::subMenuKeyboard()
|
2017-06-01 21:36:57 +00:00
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2017-06-01 21:36:57 +00:00
|
|
|
QFETCH(bool, cascade);
|
|
|
|
QFETCH(bool, mirrored);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenus.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-01 21:36:57 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2017-08-09 10:51:40 +00:00
|
|
|
centerOnScreen(window);
|
2017-06-01 21:36:57 +00:00
|
|
|
moveMouseAway(window);
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
tst_qquickmenu: call requestActivate before QTest::qWaitForWindowActive
On a Ubuntu 22.04.4 VM, every test after the first one that didn't call
requestActivate before QTest::qWaitForWindowActive would fail:
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
********* Start testing of tst_QQuickMenu *********
Config: Using QtTest library 6.8.0, Qt 6.8.0 (x86_64-little_endian-lp64 shared (dynamic) debug build; by GCC 11.4.0), ubuntu 22.04
PASS : tst_QQuickMenu::Basic::initTestCase()
PASS : tst_QQuickMenu::Basic::defaults()
PASS : tst_QQuickMenu::Basic::count()
PASS : tst_QQuickMenu::Basic::mouse()
PASS : tst_QQuickMenu::Basic::pressAndHold()
PASS : tst_QQuickMenu::Basic::contextMenuKeyboard()
PASS : tst_QQuickMenu::Basic::disabledMenuItemKeyNavigation()
PASS : tst_QQuickMenu::Basic::mnemonics()
PASS : tst_QQuickMenu::Basic::menuButton()
PASS : tst_QQuickMenu::Basic::addItem()
FAIL! : tst_QQuickMenu::Basic::menuSeparator() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(697)]
PASS : tst_QQuickMenu::Basic::repeater()
PASS : tst_QQuickMenu::Basic::order()
PASS : tst_QQuickMenu::Basic::popup()
PASS : tst_QQuickMenu::Basic::actions()
FAIL! : tst_QQuickMenu::Basic::actionShortcuts() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1084)]
PASS : tst_QQuickMenu::Basic::removeTakeItem()
PASS : tst_QQuickMenu::Basic::subMenuMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuMouse(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(non-cascading)
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuWithIcon()
PASS : tst_QQuickMenu::Basic::addRemoveSubMenus()
PASS : tst_QQuickMenu::Basic::scrollable(Window)
PASS : tst_QQuickMenu::Basic::scrollable(ApplicationWindow)
PASS : tst_QQuickMenu::Basic::scrollable(WithPadding)
PASS : tst_QQuickMenu::Basic::scrollable(FixedHeight)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared inside)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::menuItemWidth(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidth(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterRetranslate()
FAIL! : tst_QQuickMenu::Basic::giveMenuItemFocusOnButtonPress() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(2097)]
PASS : tst_QQuickMenu::Basic::customMenuCullItems()
PASS : tst_QQuickMenu::Basic::customMenuUseRepeaterAsTheContentItem()
PASS : tst_QQuickMenu::Basic::invalidUrlInImgTag()
PASS : tst_QQuickMenu::Basic::nativeStatic()
PASS : tst_QQuickMenu::Basic::nativeDynamicActions()
PASS : tst_QQuickMenu::Basic::nativeDynamicSubmenus()
PASS : tst_QQuickMenu::Basic::nativeMenuSeparator()
PASS : tst_QQuickMenu::Basic::dontUseNativeMenuWindowsChanges()
PASS : tst_QQuickMenu::Basic::nativeMixedItems()
PASS : tst_QQuickMenu::Basic::cleanupTestCase()
Totals: 59 passed, 11 failed, 0 skipped, 0 blacklisted, 74936ms
********* Finished testing of tst_QQuickMenu *********
Change-Id: I5ef76d2648fc7c8732aafab3a9a0a02844075c3e
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2024-03-20 07:33:47 +00:00
|
|
|
window->requestActivate();
|
2019-07-18 17:02:40 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2021-02-23 01:51:31 +00:00
|
|
|
if (mirrored) {
|
|
|
|
QQmlExpression mirroringExpression(qmlContext(window), window,
|
|
|
|
"LayoutMirroring.childrenInherit = true; LayoutMirroring.enabled = true");
|
|
|
|
QVERIFY2(mirroringExpression.evaluate().isValid(), qPrintable(mirroringExpression.error().toString()));
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
mainMenu->setCascade(cascade);
|
|
|
|
QCOMPARE(mainMenu->cascade(), cascade);
|
2024-08-29 12:57:04 +00:00
|
|
|
// TODO: QTBUG-128474
|
|
|
|
mainMenu->setPopupType(QQuickPopup::Item);
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
QQuickMenu *subMenu1 = window->property("subMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu1);
|
|
|
|
|
|
|
|
QQuickMenu *subMenu2 = window->property("subMenu2").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu2);
|
|
|
|
|
|
|
|
QQuickMenu *subSubMenu1 = window->property("subSubMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subSubMenu1);
|
|
|
|
|
|
|
|
mainMenu->open();
|
|
|
|
QVERIFY(mainMenu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
|
2019-04-29 11:03:25 +00:00
|
|
|
// navigate to the sub-menu item and trigger it to open the sub-menu
|
2017-06-01 21:36:57 +00:00
|
|
|
QQuickMenuItem *subMenu1Item = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(1));
|
|
|
|
QVERIFY(subMenu1Item);
|
|
|
|
QVERIFY(!subMenu1Item->isHighlighted());
|
|
|
|
QCOMPARE(subMenu1Item->subMenu(), subMenu1);
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(subMenu1Item->isHighlighted());
|
|
|
|
QTest::keyClick(window, Qt::Key_Space);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(mainMenu->isVisible(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(subMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(subMenu1->isOpened());
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
|
|
|
|
// navigate to the sub-sub-menu item and open it with the arrow key
|
|
|
|
QQuickMenuItem *subSubMenu1Item = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(2));
|
|
|
|
QVERIFY(subSubMenu1Item);
|
|
|
|
QVERIFY(!subSubMenu1Item->isHighlighted());
|
|
|
|
QCOMPARE(subSubMenu1Item->subMenu(), subSubMenu1);
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(subSubMenu1Item->isHighlighted());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(mainMenu->isVisible(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
QTest::keyClick(window, mirrored ? Qt::Key_Left : Qt::Key_Right);
|
|
|
|
QCOMPARE(mainMenu->isVisible(), cascade);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(subMenu1->isVisible(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(subSubMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(subSubMenu1->isOpened());
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
// navigate within the sub-sub-menu
|
|
|
|
QQuickMenuItem *subSubMenuItem1 = qobject_cast<QQuickMenuItem *>(subSubMenu1->itemAt(0));
|
|
|
|
QVERIFY(subSubMenuItem1);
|
|
|
|
QQuickMenuItem *subSubMenuItem2 = qobject_cast<QQuickMenuItem *>(subSubMenu1->itemAt(1));
|
|
|
|
QVERIFY(subSubMenuItem2);
|
|
|
|
QVERIFY(subSubMenuItem1->isHighlighted());
|
|
|
|
QVERIFY(!subSubMenuItem2->isHighlighted());
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(!subSubMenuItem1->isHighlighted());
|
|
|
|
QVERIFY(subSubMenuItem2->isHighlighted());
|
|
|
|
|
|
|
|
// navigate to the parent menu with the arrow key
|
|
|
|
QTest::keyClick(window, mirrored ? Qt::Key_Right : Qt::Key_Left);
|
|
|
|
QVERIFY(subSubMenu1Item->isHighlighted());
|
|
|
|
QCOMPARE(mainMenu->isVisible(), cascade);
|
|
|
|
QVERIFY(subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!subSubMenu1->isVisible());
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
// navigate within the sub-menu
|
|
|
|
QQuickMenuItem *subMenuItem1 = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(0));
|
|
|
|
QVERIFY(subMenuItem1);
|
|
|
|
QQuickMenuItem *subMenuItem2 = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(1));
|
|
|
|
QVERIFY(subMenuItem2);
|
|
|
|
QVERIFY(!subMenuItem1->isHighlighted());
|
|
|
|
QVERIFY(!subMenuItem2->isHighlighted());
|
|
|
|
QVERIFY(subSubMenu1Item->isHighlighted());
|
|
|
|
QTest::keyClick(window, Qt::Key_Up);
|
|
|
|
QVERIFY(!subMenuItem1->isHighlighted());
|
|
|
|
QVERIFY(subMenuItem2->isHighlighted());
|
|
|
|
QVERIFY(!subSubMenu1Item->isHighlighted());
|
|
|
|
|
|
|
|
// close the menus with esc
|
|
|
|
QTest::keyClick(window, Qt::Key_Escape);
|
|
|
|
QCOMPARE(mainMenu->isVisible(), cascade);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!subMenu1->isVisible());
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
QTest::keyClick(window, Qt::Key_Escape);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
}
|
|
|
|
|
2019-01-28 12:14:30 +00:00
|
|
|
void tst_QQuickMenu::subMenuDisabledKeyboard_data()
|
|
|
|
{
|
|
|
|
subMenuKeyboard_data();
|
|
|
|
}
|
|
|
|
|
|
|
|
// QTBUG-69540
|
|
|
|
void tst_QQuickMenu::subMenuDisabledKeyboard()
|
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2019-01-28 12:14:30 +00:00
|
|
|
QFETCH(bool, cascade);
|
|
|
|
QFETCH(bool, mirrored);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenuDisabled.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2019-01-28 12:14:30 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
centerOnScreen(window);
|
|
|
|
moveMouseAway(window);
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
tst_qquickmenu: call requestActivate before QTest::qWaitForWindowActive
On a Ubuntu 22.04.4 VM, every test after the first one that didn't call
requestActivate before QTest::qWaitForWindowActive would fail:
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
********* Start testing of tst_QQuickMenu *********
Config: Using QtTest library 6.8.0, Qt 6.8.0 (x86_64-little_endian-lp64 shared (dynamic) debug build; by GCC 11.4.0), ubuntu 22.04
PASS : tst_QQuickMenu::Basic::initTestCase()
PASS : tst_QQuickMenu::Basic::defaults()
PASS : tst_QQuickMenu::Basic::count()
PASS : tst_QQuickMenu::Basic::mouse()
PASS : tst_QQuickMenu::Basic::pressAndHold()
PASS : tst_QQuickMenu::Basic::contextMenuKeyboard()
PASS : tst_QQuickMenu::Basic::disabledMenuItemKeyNavigation()
PASS : tst_QQuickMenu::Basic::mnemonics()
PASS : tst_QQuickMenu::Basic::menuButton()
PASS : tst_QQuickMenu::Basic::addItem()
FAIL! : tst_QQuickMenu::Basic::menuSeparator() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(697)]
PASS : tst_QQuickMenu::Basic::repeater()
PASS : tst_QQuickMenu::Basic::order()
PASS : tst_QQuickMenu::Basic::popup()
PASS : tst_QQuickMenu::Basic::actions()
FAIL! : tst_QQuickMenu::Basic::actionShortcuts() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1084)]
PASS : tst_QQuickMenu::Basic::removeTakeItem()
PASS : tst_QQuickMenu::Basic::subMenuMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuMouse(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(non-cascading)
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuWithIcon()
PASS : tst_QQuickMenu::Basic::addRemoveSubMenus()
PASS : tst_QQuickMenu::Basic::scrollable(Window)
PASS : tst_QQuickMenu::Basic::scrollable(ApplicationWindow)
PASS : tst_QQuickMenu::Basic::scrollable(WithPadding)
PASS : tst_QQuickMenu::Basic::scrollable(FixedHeight)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared inside)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::menuItemWidth(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidth(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterRetranslate()
FAIL! : tst_QQuickMenu::Basic::giveMenuItemFocusOnButtonPress() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(2097)]
PASS : tst_QQuickMenu::Basic::customMenuCullItems()
PASS : tst_QQuickMenu::Basic::customMenuUseRepeaterAsTheContentItem()
PASS : tst_QQuickMenu::Basic::invalidUrlInImgTag()
PASS : tst_QQuickMenu::Basic::nativeStatic()
PASS : tst_QQuickMenu::Basic::nativeDynamicActions()
PASS : tst_QQuickMenu::Basic::nativeDynamicSubmenus()
PASS : tst_QQuickMenu::Basic::nativeMenuSeparator()
PASS : tst_QQuickMenu::Basic::dontUseNativeMenuWindowsChanges()
PASS : tst_QQuickMenu::Basic::nativeMixedItems()
PASS : tst_QQuickMenu::Basic::cleanupTestCase()
Totals: 59 passed, 11 failed, 0 skipped, 0 blacklisted, 74936ms
********* Finished testing of tst_QQuickMenu *********
Change-Id: I5ef76d2648fc7c8732aafab3a9a0a02844075c3e
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2024-03-20 07:33:47 +00:00
|
|
|
window->requestActivate();
|
2019-07-18 17:02:40 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
2019-01-28 12:14:30 +00:00
|
|
|
|
2021-02-23 01:51:31 +00:00
|
|
|
if (mirrored) {
|
|
|
|
QQmlExpression mirroringExpression(qmlContext(window), window,
|
|
|
|
"LayoutMirroring.childrenInherit = true; LayoutMirroring.enabled = true");
|
|
|
|
QVERIFY2(mirroringExpression.evaluate().isValid(), qPrintable(mirroringExpression.error().toString()));
|
|
|
|
}
|
2019-01-28 12:14:30 +00:00
|
|
|
|
|
|
|
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
mainMenu->setCascade(cascade);
|
|
|
|
QCOMPARE(mainMenu->cascade(), cascade);
|
2024-08-29 12:57:04 +00:00
|
|
|
// TODO: QTBUG-128474
|
|
|
|
mainMenu->setPopupType(QQuickPopup::Item);
|
2019-01-28 12:14:30 +00:00
|
|
|
|
|
|
|
QQuickMenuItem *menuItem1 = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(0));
|
|
|
|
QVERIFY(menuItem1);
|
|
|
|
|
|
|
|
QQuickMenu *subMenu = window->property("subMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
|
|
|
|
mainMenu->open();
|
|
|
|
QVERIFY(mainMenu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
2019-01-28 12:14:30 +00:00
|
|
|
QVERIFY(!menuItem1->isHighlighted());
|
|
|
|
QVERIFY(!subMenu->isVisible());
|
|
|
|
|
|
|
|
// Highlight the top-level menu item.
|
|
|
|
QTest::keyClick(window, Qt::Key_Down);
|
|
|
|
QVERIFY(menuItem1->isHighlighted());
|
|
|
|
|
|
|
|
QQuickMenuItem *subMenuItem1 = qobject_cast<QQuickMenuItem *>(subMenu->itemAt(0));
|
|
|
|
QVERIFY(subMenuItem1);
|
|
|
|
QQuickMenuItem *subMenuItem2 = qobject_cast<QQuickMenuItem *>(subMenu->itemAt(1));
|
|
|
|
QVERIFY(subMenuItem2);
|
|
|
|
|
|
|
|
// Open the sub-menu.
|
|
|
|
QTest::keyClick(window, mirrored ? Qt::Key_Left : Qt::Key_Right);
|
|
|
|
// The first sub-menu item is disabled, so it should highlight the second one.
|
|
|
|
QVERIFY(!subMenuItem1->isHighlighted());
|
|
|
|
QVERIFY(subMenuItem2->isHighlighted());
|
|
|
|
|
|
|
|
// Close the menus with escape.
|
|
|
|
QTest::keyClick(window, Qt::Key_Escape);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(mainMenu->isVisible(), cascade);
|
|
|
|
QTRY_VERIFY(!subMenu->isVisible());
|
2019-01-28 12:14:30 +00:00
|
|
|
QTest::keyClick(window, Qt::Key_Escape);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
2019-01-28 12:14:30 +00:00
|
|
|
QVERIFY(!subMenu->isVisible());
|
|
|
|
}
|
|
|
|
|
2020-07-14 12:18:29 +00:00
|
|
|
/*
|
|
|
|
QCOMPARE() compares doubles with 1-in-1e12 precision, which is too fine for these tests.
|
|
|
|
Casting to floats, compared with 1-in-1e5 precision, gives more robust results.
|
|
|
|
*/
|
|
|
|
#define FLOAT_EQ(u, v) QCOMPARE(float(u), float(v))
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::subMenuPosition_data()
|
2017-06-01 21:36:57 +00:00
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("cascade");
|
2017-06-16 03:28:59 +00:00
|
|
|
QTest::addColumn<bool>("flip");
|
2017-06-01 21:36:57 +00:00
|
|
|
QTest::addColumn<bool>("mirrored");
|
|
|
|
QTest::addColumn<qreal>("overlap");
|
2024-08-27 00:18:04 +00:00
|
|
|
QTest::addColumn<QQuickPopup::PopupType>("popupType");
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2024-08-27 00:18:04 +00:00
|
|
|
QTest::newRow("cascading") << true << false << false << 0.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("cascading,flip") << true << true << false << 0.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("cascading,overlap") << true << false << false << 10.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("cascading,flip,overlap") << true << true << false << 10.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("cascading,mirrored") << true << false << true << 0.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("cascading,mirrored,flip") << true << true << true << 0.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("cascading,mirrored,overlap") << true << false << true << 10.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("cascading,mirrored,flip,overlap") << true << true << true << 10.0 << QQuickPopup::Item;
|
|
|
|
QTest::newRow("non-cascading") << false << false << false << 0.0 << QQuickPopup::Item;
|
|
|
|
// Flipping is tested in subMenuFlipsPositionWhenOutOfBounds(), and mirroring doesn't currently work when using popup windows.
|
|
|
|
|
|
|
|
// TODO: enable the test rows below, once they're fixed to not be flaky. QTBUG-128471
|
|
|
|
//QTest::newRow("cascading,window") << true << false << false << 0.0 << QQuickPopup::Window;
|
|
|
|
//QTest::newRow("non-cascading,window") << false << false << false << 0.0 << QQuickPopup::Window;
|
2017-06-01 21:36:57 +00:00
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::subMenuPosition()
|
2017-06-01 21:36:57 +00:00
|
|
|
{
|
|
|
|
QFETCH(bool, cascade);
|
2017-06-16 03:28:59 +00:00
|
|
|
QFETCH(bool, flip);
|
2017-06-01 21:36:57 +00:00
|
|
|
QFETCH(bool, mirrored);
|
|
|
|
QFETCH(qreal, overlap);
|
2024-08-27 00:18:04 +00:00
|
|
|
QFETCH(QQuickPopup::PopupType, popupType);
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenus.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-01 21:36:57 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
2017-06-16 03:28:59 +00:00
|
|
|
|
2019-04-29 11:03:25 +00:00
|
|
|
// Ensure that the default size of the window fits three menus side by side.
|
|
|
|
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
window->setWidth(mainMenu->width() * 3 + mainMenu->leftMargin() + mainMenu->rightMargin());
|
|
|
|
|
2017-06-16 03:28:59 +00:00
|
|
|
// the default size of the window fits three menus side by side.
|
|
|
|
// when testing flipping, we resize the window so that the first
|
|
|
|
// sub-menu fits, but the second doesn't
|
|
|
|
if (flip)
|
2019-04-29 11:03:25 +00:00
|
|
|
window->setWidth(window->width() - mainMenu->width());
|
2017-06-16 03:28:59 +00:00
|
|
|
|
2017-08-09 10:51:40 +00:00
|
|
|
centerOnScreen(window);
|
2017-06-01 21:36:57 +00:00
|
|
|
moveMouseAway(window);
|
2022-04-22 15:01:14 +00:00
|
|
|
#ifndef Q_OS_ANDROID
|
2019-07-18 17:02:40 +00:00
|
|
|
window->show();
|
2022-04-22 15:01:14 +00:00
|
|
|
#else
|
|
|
|
// On Android the desired size does not fit into the screen, so we just
|
|
|
|
// call showNormal. This will make the window larger than the screen, but
|
|
|
|
// all the geometry calculations will be correct. Otherwise we'll get
|
|
|
|
// unpredictable results
|
|
|
|
window->showNormal();
|
|
|
|
#endif
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2021-02-23 01:51:31 +00:00
|
|
|
if (mirrored) {
|
|
|
|
QQmlExpression mirroringExpression(qmlContext(window), window,
|
|
|
|
"LayoutMirroring.childrenInherit = true; LayoutMirroring.enabled = true");
|
|
|
|
QVERIFY2(mirroringExpression.evaluate().isValid(), qPrintable(mirroringExpression.error().toString()));
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
mainMenu->setCascade(cascade);
|
|
|
|
QCOMPARE(mainMenu->cascade(), cascade);
|
|
|
|
mainMenu->setOverlap(overlap);
|
|
|
|
QCOMPARE(mainMenu->overlap(), overlap);
|
2024-08-27 00:18:04 +00:00
|
|
|
mainMenu->setPopupType(popupType);
|
|
|
|
QCOMPARE(mainMenu->popupType(), popupType);
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
QQuickMenu *subMenu1 = window->property("subMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu1);
|
|
|
|
subMenu1->setCascade(cascade);
|
|
|
|
QCOMPARE(subMenu1->cascade(), cascade);
|
|
|
|
subMenu1->setOverlap(overlap);
|
|
|
|
QCOMPARE(subMenu1->overlap(), overlap);
|
|
|
|
|
|
|
|
QQuickMenu *subMenu2 = window->property("subMenu2").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu2);
|
|
|
|
subMenu2->setCascade(cascade);
|
|
|
|
QCOMPARE(subMenu2->cascade(), cascade);
|
|
|
|
subMenu2->setOverlap(overlap);
|
|
|
|
QCOMPARE(subMenu2->overlap(), overlap);
|
|
|
|
|
|
|
|
QQuickMenu *subSubMenu1 = window->property("subSubMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subSubMenu1);
|
|
|
|
subSubMenu1->setCascade(cascade);
|
|
|
|
QCOMPARE(subSubMenu1->cascade(), cascade);
|
|
|
|
subSubMenu1->setOverlap(overlap);
|
|
|
|
QCOMPARE(subSubMenu1->overlap(), overlap);
|
|
|
|
|
2017-06-16 03:28:59 +00:00
|
|
|
// choose the main menu position so that there's room for the
|
|
|
|
// sub-menus to cascade to the left when mirrored
|
2017-06-01 21:36:57 +00:00
|
|
|
if (mirrored)
|
2019-04-29 11:03:25 +00:00
|
|
|
mainMenu->setX(window->width() - mainMenu->width());
|
2017-06-01 21:36:57 +00:00
|
|
|
|
|
|
|
mainMenu->open();
|
|
|
|
QVERIFY(mainMenu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
2024-08-27 00:18:04 +00:00
|
|
|
|
|
|
|
QQuickMenuPrivate *mainMenuPrivate = QQuickMenuPrivate::get(mainMenu);
|
|
|
|
QVERIFY(mainMenuPrivate);
|
|
|
|
if (mainMenuPrivate->usePopupWindow()) {
|
|
|
|
QTRY_VERIFY(mainMenuPrivate->popupWindow);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(mainMenuPrivate->popupWindow));
|
|
|
|
QVERIFY(QQuickTest::qWaitForPolish(mainMenuPrivate->popupWindow));
|
|
|
|
}
|
|
|
|
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu1->isVisible());
|
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
|
2017-06-16 03:28:59 +00:00
|
|
|
// open the sub-menu (never flips)
|
2017-06-01 21:36:57 +00:00
|
|
|
QQuickMenuItem *subMenu1Item = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(1));
|
|
|
|
QVERIFY(subMenu1Item);
|
|
|
|
QCOMPARE(subMenu1Item->subMenu(), subMenu1);
|
|
|
|
emit subMenu1Item->triggered();
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(mainMenu->isVisible(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(subMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(subMenu1->isOpened());
|
2024-08-27 00:18:04 +00:00
|
|
|
|
|
|
|
QQuickMenuPrivate *subMenu1Private = QQuickMenuPrivate::get(subMenu1);
|
|
|
|
QVERIFY(subMenu1Private);
|
|
|
|
if (subMenu1Private->usePopupWindow()) {
|
|
|
|
QTRY_VERIFY(subMenu1Private->popupWindow);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(subMenu1Private->popupWindow));
|
|
|
|
QVERIFY(QQuickTest::qWaitForPolish(subMenu1Private->popupWindow));
|
|
|
|
}
|
|
|
|
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(!subSubMenu1->isVisible());
|
|
|
|
if (cascade) {
|
|
|
|
QCOMPARE(subMenu1->parentItem(), subMenu1Item);
|
|
|
|
// vertically aligned to the parent menu item
|
2020-07-14 12:18:29 +00:00
|
|
|
// We cast to float here because we want to use its larger tolerance for equality (because it has less precision than double).
|
2024-08-27 00:18:04 +00:00
|
|
|
QCOMPARE(subMenu1->popupItem()->mapToGlobal({0, 0}).y(), mainMenu->popupItem()->mapToGlobal({0, subMenu1Item->y()}).y());
|
2019-04-29 11:03:25 +00:00
|
|
|
if (mirrored) {
|
|
|
|
// on the left of the parent menu
|
2020-07-14 12:18:29 +00:00
|
|
|
FLOAT_EQ(subMenu1->popupItem()->x(), mainMenu->popupItem()->x() - subMenu1->width() + overlap);
|
2019-04-29 11:03:25 +00:00
|
|
|
} else {
|
|
|
|
// on the right of the parent menu
|
2024-08-27 00:18:04 +00:00
|
|
|
if (!mainMenuPrivate->usePopupWindow())
|
|
|
|
FLOAT_EQ(subMenu1->popupItem()->x(), mainMenu->popupItem()->x() + mainMenu->width() - overlap);
|
|
|
|
else
|
|
|
|
QCOMPARE(subMenu1->popupItem()->mapToGlobal({0, 0}).x(), mainMenu->popupItem()->mapToGlobal({subMenu1->width() - overlap,0}).x());
|
2019-04-29 11:03:25 +00:00
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
} else {
|
|
|
|
QCOMPARE(subMenu1->parentItem(), mainMenu->parentItem());
|
|
|
|
// centered over the parent menu
|
2024-08-27 00:18:04 +00:00
|
|
|
if (!mainMenuPrivate->usePopupWindow()) {
|
|
|
|
FLOAT_EQ(subMenu1->popupItem()->x(), mainMenu->popupItem()->x() + (mainMenu->width() - subMenu1->width()) / 2);
|
|
|
|
FLOAT_EQ(subMenu1->popupItem()->y(), mainMenu->popupItem()->y() + (mainMenu->height() - subMenu1->height()) / 2);
|
|
|
|
} else {
|
|
|
|
QCOMPARE(subMenu1->popupItem()->mapToGlobal({0, 0}),
|
|
|
|
mainMenu->popupItem()->mapToGlobal({(mainMenu->width() - subMenu1->width()) / 2, (mainMenu->height() - subMenu1->height()) / 2}));
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
}
|
|
|
|
|
2017-06-16 03:28:59 +00:00
|
|
|
// open the sub-sub-menu (can flip)
|
2017-06-01 21:36:57 +00:00
|
|
|
QQuickMenuItem *subSubMenu1Item = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(2));
|
|
|
|
QVERIFY(subSubMenu1Item);
|
|
|
|
QCOMPARE(subSubMenu1Item->subMenu(), subSubMenu1);
|
|
|
|
emit subSubMenu1Item->triggered();
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_COMPARE(mainMenu->isVisible(), cascade);
|
|
|
|
QTRY_COMPARE(subMenu1->isVisible(), cascade);
|
2017-06-01 21:36:57 +00:00
|
|
|
QVERIFY(!subMenu2->isVisible());
|
|
|
|
QVERIFY(subSubMenu1->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(subSubMenu1->isOpened());
|
2017-06-01 21:36:57 +00:00
|
|
|
|
2024-08-27 00:18:04 +00:00
|
|
|
QQuickMenuPrivate *subSubMenu1Private = QQuickMenuPrivate::get(subSubMenu1);
|
|
|
|
if (subSubMenu1Private->usePopupWindow()) {
|
|
|
|
QTRY_VERIFY(subSubMenu1Private->popupWindow);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(subSubMenu1Private->popupWindow));
|
|
|
|
QVERIFY(QQuickTest::qWaitForPolish(subSubMenu1Private->popupWindow));
|
|
|
|
}
|
|
|
|
|
2017-06-01 21:36:57 +00:00
|
|
|
if (cascade) {
|
|
|
|
QCOMPARE(subSubMenu1->parentItem(), subSubMenu1Item);
|
|
|
|
// vertically aligned to the parent menu item
|
2024-08-27 00:18:04 +00:00
|
|
|
QCOMPARE(subSubMenu1->popupItem()->mapToGlobal({0, 0}).y(), subMenu1->popupItem()->mapToGlobal({0, subSubMenu1Item->y()}).y());
|
2019-04-29 11:03:25 +00:00
|
|
|
if (mirrored != flip) {
|
|
|
|
// on the left of the parent menu
|
2024-08-27 00:18:04 +00:00
|
|
|
if (!mainMenuPrivate->usePopupWindow())
|
|
|
|
FLOAT_EQ(subSubMenu1->popupItem()->x(), subMenu1->popupItem()->x() - subSubMenu1->width() + overlap);
|
|
|
|
else
|
|
|
|
QCOMPARE(subSubMenu1->popupItem()->mapToGlobal({0, 0}).x(), subMenu1->popupItem()->mapToGlobal({overlap - subSubMenu1->width(),0}).x());
|
2019-04-29 11:03:25 +00:00
|
|
|
} else {
|
|
|
|
// on the right of the parent menu
|
2024-08-27 00:18:04 +00:00
|
|
|
if (!mainMenuPrivate->usePopupWindow())
|
|
|
|
FLOAT_EQ(subSubMenu1->popupItem()->x(), subMenu1->popupItem()->x() + subMenu1->width() - overlap);
|
|
|
|
else
|
|
|
|
QCOMPARE(subSubMenu1->popupItem()->mapToGlobal({0, 0}).x(), subMenu1->popupItem()->mapToGlobal({subMenu1->width() - overlap,0}).x());
|
2019-04-29 11:03:25 +00:00
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
} else {
|
|
|
|
QCOMPARE(subSubMenu1->parentItem(), subMenu1->parentItem());
|
|
|
|
// centered over the parent menu
|
2024-08-27 00:18:04 +00:00
|
|
|
if (!mainMenuPrivate->usePopupWindow()) {
|
|
|
|
FLOAT_EQ(subSubMenu1->popupItem()->x(), subMenu1->popupItem()->x() + (subMenu1->width() - subSubMenu1->width()) / 2);
|
|
|
|
FLOAT_EQ(subSubMenu1->popupItem()->y(), subMenu1->popupItem()->y() + (subMenu1->height() - subSubMenu1->height()) / 2);
|
|
|
|
} else {
|
|
|
|
const QWindow *subSubMenu1Window = subSubMenu1->popupItem()->window();
|
|
|
|
const QWindow *subMenu1Window = subMenu1->popupItem()->window();
|
|
|
|
const QPoint offset((subMenu1->width() - subSubMenu1->width()) / 2, (subMenu1->height() - subSubMenu1->height()) / 2);
|
|
|
|
|
|
|
|
QTRY_COMPARE(subSubMenu1Window->position(), subMenu1Window->position() + offset);
|
|
|
|
}
|
2017-06-01 21:36:57 +00:00
|
|
|
}
|
2024-08-27 00:18:04 +00:00
|
|
|
subSubMenu1->close();
|
|
|
|
subMenu1->close();
|
|
|
|
mainMenu->close();
|
2017-06-01 21:36:57 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 12:18:29 +00:00
|
|
|
#undef FLOAT_EQ
|
|
|
|
|
2022-09-14 11:09:49 +00:00
|
|
|
void tst_QQuickMenu::subMenuWithIcon()
|
|
|
|
{
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenus.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
|
|
|
|
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
|
|
|
|
QQuickMenu *subMenu = window->property("subMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
|
|
|
|
const int iconWidth = 14;
|
|
|
|
const int iconHeight = 20;
|
|
|
|
const QUrl iconSource("qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/check.png");
|
|
|
|
|
|
|
|
QQuickIcon icon;
|
|
|
|
icon.setSource(iconSource);
|
|
|
|
icon.setWidth(iconWidth);
|
|
|
|
icon.setHeight(iconHeight);
|
|
|
|
|
|
|
|
subMenu->setIcon(icon);
|
|
|
|
QCOMPARE(subMenu->icon().source(), iconSource);
|
|
|
|
QCOMPARE(subMenu->icon().width(), iconWidth);
|
|
|
|
QCOMPARE(subMenu->icon().height(), iconHeight);
|
|
|
|
|
|
|
|
QQuickMenuItem *subMenuItem = qobject_cast<QQuickMenuItem *>(mainMenu->itemAt(1));
|
|
|
|
QVERIFY(subMenuItem);
|
|
|
|
QCOMPARE(subMenuItem->icon().source(), iconSource);
|
|
|
|
QCOMPARE(subMenuItem->icon().width(), iconWidth);
|
|
|
|
QCOMPARE(subMenuItem->icon().height(), iconHeight);
|
|
|
|
}
|
|
|
|
|
2017-09-27 18:07:23 +00:00
|
|
|
void tst_QQuickMenu::addRemoveSubMenus()
|
2017-06-07 17:43:58 +00:00
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenus.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2017-06-07 17:43:58 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2017-06-07 17:43:58 +00:00
|
|
|
|
|
|
|
QQuickMenu *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
|
2017-07-10 09:59:47 +00:00
|
|
|
QVERIFY(!mainMenu->menuAt(0));
|
|
|
|
|
2017-06-07 17:43:58 +00:00
|
|
|
QPointer<QQuickMenu> subMenu1 = window->property("subMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(!subMenu1.isNull());
|
2017-07-10 09:59:47 +00:00
|
|
|
QCOMPARE(mainMenu->menuAt(1), subMenu1.data());
|
|
|
|
|
|
|
|
QVERIFY(!mainMenu->menuAt(2));
|
2017-06-07 17:43:58 +00:00
|
|
|
|
|
|
|
QPointer<QQuickMenu> subMenu2 = window->property("subMenu2").value<QQuickMenu *>();
|
|
|
|
QVERIFY(!subMenu2.isNull());
|
2017-07-10 09:59:47 +00:00
|
|
|
QCOMPARE(mainMenu->menuAt(3), subMenu2.data());
|
|
|
|
|
|
|
|
QVERIFY(!mainMenu->menuAt(4));
|
2017-06-07 17:43:58 +00:00
|
|
|
|
|
|
|
QPointer<QQuickMenu> subSubMenu1 = window->property("subSubMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(!subSubMenu1.isNull());
|
|
|
|
|
|
|
|
// takeMenu(int) does not destroy the menu, but does destroy the respective item in the parent menu
|
|
|
|
QPointer<QQuickMenuItem> subSubMenu1Item = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(2));
|
|
|
|
QVERIFY(subSubMenu1Item);
|
|
|
|
QCOMPARE(subSubMenu1Item->subMenu(), subSubMenu1.data());
|
|
|
|
QCOMPARE(subMenu1->takeMenu(2), subSubMenu1.data());
|
|
|
|
QVERIFY(!subMenu1->itemAt(2));
|
|
|
|
QCoreApplication::sendPostedEvents(subSubMenu1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(!subSubMenu1.isNull());
|
|
|
|
QCoreApplication::sendPostedEvents(subSubMenu1Item, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(subSubMenu1Item.isNull());
|
|
|
|
|
|
|
|
// takeMenu(int) does not destroy an item that doesn't present a menu
|
|
|
|
QPointer<QQuickMenuItem> subMenuItem1 = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(0));
|
|
|
|
QVERIFY(subMenuItem1);
|
|
|
|
QVERIFY(!subMenuItem1->subMenu());
|
|
|
|
QVERIFY(!subMenu1->takeMenu(0));
|
|
|
|
QCoreApplication::sendPostedEvents(subMenuItem1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(!subMenuItem1.isNull());
|
|
|
|
|
|
|
|
// addMenu(Menu) re-creates the respective item in the parent menu
|
|
|
|
subMenu1->addMenu(subSubMenu1);
|
|
|
|
subSubMenu1Item = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(2));
|
|
|
|
QVERIFY(!subSubMenu1Item.isNull());
|
|
|
|
|
|
|
|
// removeMenu(Menu) destroys both the menu and the respective item in the parent menu
|
|
|
|
subMenu1->removeMenu(subSubMenu1);
|
|
|
|
QVERIFY(!subMenu1->itemAt(2));
|
|
|
|
QCoreApplication::sendPostedEvents(subSubMenu1, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(subSubMenu1.isNull());
|
|
|
|
QCoreApplication::sendPostedEvents(subSubMenu1Item, QEvent::DeferredDelete);
|
|
|
|
QVERIFY(subSubMenu1Item.isNull());
|
|
|
|
}
|
|
|
|
|
2024-06-12 11:50:31 +00:00
|
|
|
void tst_QQuickMenu::subMenuPopupType()
|
|
|
|
{
|
2024-08-23 15:53:26 +00:00
|
|
|
if (!popupWindowsSupported)
|
|
|
|
QSKIP("The platform doesn't support popup windows. Skipping test.");
|
|
|
|
|
2024-06-27 10:18:09 +00:00
|
|
|
// Undo the setting of AA_DontUseNativeMenuWindows to true from init()
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
|
|
|
|
|
2024-06-12 11:50:31 +00:00
|
|
|
// Check that all sub-menus will end up with an effective popup
|
|
|
|
// type equal to the root menu.
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("subMenus.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
auto *mainMenu = window->property("mainMenu").value<QQuickMenu *>();
|
|
|
|
auto *subMenu1 = window->property("subMenu1").value<QQuickMenu *>();
|
|
|
|
auto *subSubMenu1 = window->property("subSubMenu1").value<QQuickMenu *>();
|
|
|
|
QVERIFY(mainMenu);
|
|
|
|
QVERIFY(subMenu1);
|
|
|
|
QVERIFY(subSubMenu1);
|
|
|
|
auto *mainMenu_d = QQuickMenuPrivate::get(mainMenu);
|
|
|
|
auto *subMenu1_d = QQuickMenuPrivate::get(subMenu1);
|
|
|
|
auto *subSubMenu1_d = QQuickMenuPrivate::get(subSubMenu1);
|
|
|
|
|
|
|
|
mainMenu->setPopupType(QQuickPopup::Item);
|
|
|
|
QCOMPARE(mainMenu->popupType(), QQuickPopup::Item);
|
|
|
|
mainMenu->open();
|
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
|
|
|
QCOMPARE(mainMenu_d->resolvedPopupType(), QQuickPopup::Item);
|
|
|
|
QCOMPARE(subMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
|
|
|
QCOMPARE(subSubMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
|
|
|
mainMenu->close();
|
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
|
|
|
|
|
|
|
// Even if we set QQuickPopup::Window as preferred popup type for
|
|
|
|
// subMenu1, the the effective type will still be the same as the
|
|
|
|
// parent menu: QQuickPopup::Item
|
|
|
|
subMenu1->setPopupType(QQuickPopup::Window);
|
|
|
|
QCOMPARE(subMenu1->popupType(), QQuickPopup::Window);
|
|
|
|
QCOMPARE(mainMenu->popupType(), QQuickPopup::Item);
|
|
|
|
mainMenu->open();
|
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
|
|
|
QCOMPARE(mainMenu_d->resolvedPopupType(), QQuickPopup::Item);
|
|
|
|
QCOMPARE(subMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
|
|
|
QCOMPARE(subSubMenu1_d->resolvedPopupType(), QQuickPopup::Item);
|
|
|
|
mainMenu->close();
|
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
|
|
|
|
|
|
|
// Setting QQuickPopup::Window on the root menu will force all sub-menus
|
|
|
|
// to use QQuickPopup::Window as well, if it's supported on the platform
|
|
|
|
// where the test runs. Otherwise it will fall back to QQuickPopup::Item.
|
|
|
|
mainMenu->setPopupType(QQuickPopup::Window);
|
|
|
|
QCOMPARE(mainMenu->popupType(), QQuickPopup::Window);
|
|
|
|
QCOMPARE(subMenu1->popupType(), QQuickPopup::Window);
|
|
|
|
mainMenu->open();
|
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
2024-08-23 15:53:26 +00:00
|
|
|
QCOMPARE(mainMenu_d->resolvedPopupType(), QQuickPopup::Window);
|
|
|
|
QCOMPARE(subMenu1_d->resolvedPopupType(), QQuickPopup::Window);
|
|
|
|
QCOMPARE(subSubMenu1_d->resolvedPopupType(), QQuickPopup::Window);
|
2024-06-12 11:50:31 +00:00
|
|
|
mainMenu->close();
|
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
|
|
|
|
|
|
|
// Setting QQuickPopup::Native on the root menu will force all sub-menus
|
|
|
|
// to use QQuickPopup::Native as well, if it's supported on the platform
|
|
|
|
// where the test runs. Otherwise it will fall back to either
|
|
|
|
// QQuickPopup::Window or QQuickPopup::Item.
|
|
|
|
mainMenu->setPopupType(QQuickPopup::Native);
|
|
|
|
QCOMPARE(mainMenu->popupType(), QQuickPopup::Native);
|
|
|
|
QCOMPARE(subMenu1->popupType(), QQuickPopup::Window);
|
|
|
|
if (nativeMenuSupported) {
|
|
|
|
// Note that we cannot actually show a native popup while testing, since
|
|
|
|
// that will be a blocking call. Instead we just verify that we
|
|
|
|
// intend to use a native menu.
|
|
|
|
QVERIFY(mainMenu_d->useNativeMenu());
|
|
|
|
QVERIFY(subMenu1_d->useNativeMenu());
|
|
|
|
QVERIFY(subSubMenu1_d->useNativeMenu());
|
|
|
|
} else {
|
|
|
|
// When Native is not supported, we fall back to either Window or Item
|
|
|
|
mainMenu->open();
|
|
|
|
QTRY_VERIFY(mainMenu->isOpened());
|
2024-08-23 15:53:26 +00:00
|
|
|
QCOMPARE(mainMenu_d->resolvedPopupType(), QQuickPopup::Window);
|
|
|
|
QCOMPARE(subMenu1_d->resolvedPopupType(), QQuickPopup::Window);
|
|
|
|
QCOMPARE(subSubMenu1_d->resolvedPopupType(), QQuickPopup::Window);
|
2024-06-12 11:50:31 +00:00
|
|
|
mainMenu->close();
|
|
|
|
QTRY_VERIFY(!mainMenu->isVisible());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-18 09:38:40 +00:00
|
|
|
void tst_QQuickMenu::scrollable_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QString>("qmlFilePath");
|
|
|
|
|
|
|
|
QTest::addRow("Window") << QString::fromLatin1("windowScrollable.qml");
|
|
|
|
QTest::addRow("ApplicationWindow") << QString::fromLatin1("applicationWindowScrollable.qml");
|
2020-03-17 19:19:41 +00:00
|
|
|
QTest::addRow("WithPadding") << QString::fromLatin1("scrollableWithPadding.qml");
|
2023-11-13 12:44:22 +00:00
|
|
|
QTest::addRow("FixedHeight") << QString::fromLatin1("scrollableWithFixedHeight.qml");
|
2018-06-18 09:38:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::scrollable()
|
|
|
|
{
|
|
|
|
QFETCH(QString, qmlFilePath);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, qmlFilePath);
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2018-06-18 09:38:40 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
2022-04-22 15:01:14 +00:00
|
|
|
#ifndef Q_OS_ANDROID
|
2018-06-18 09:38:40 +00:00
|
|
|
window->show();
|
2022-04-22 15:01:14 +00:00
|
|
|
#else
|
|
|
|
window->showNormal();
|
|
|
|
#endif
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2018-06-18 09:38:40 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
2024-08-29 12:57:04 +00:00
|
|
|
// TODO: QTBUG-128474
|
|
|
|
menu->setPopupType(QQuickPopup::Item);
|
2018-06-18 09:38:40 +00:00
|
|
|
menu->open();
|
|
|
|
QVERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
QQuickItem *contentItem = menu->contentItem();
|
|
|
|
QCOMPARE(contentItem->property("interactive").toBool(), true);
|
|
|
|
}
|
|
|
|
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
void tst_QQuickMenu::disableWhenTriggered_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<int>("menuItemIndex");
|
|
|
|
QTest::addColumn<int>("subMenuItemIndex");
|
|
|
|
|
|
|
|
QTest::addRow("Action") << 0 << -1;
|
|
|
|
QTest::addRow("MenuItem with Action") << 1 << -1;
|
|
|
|
QTest::addRow("MenuItem with Action declared outside menu") << 2 << -1;
|
|
|
|
QTest::addRow("MenuItem with no Action") << 3 << -1;
|
|
|
|
|
|
|
|
QTest::addRow("Sub-Action") << 4 << 0;
|
|
|
|
QTest::addRow("Sub-MenuItem with Action declared inside") << 4 << 1;
|
|
|
|
QTest::addRow("Sub-MenuItem with Action declared outside menu") << 4 << 2;
|
|
|
|
QTest::addRow("Sub-MenuItem with no Action") << 4 << 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests that the menu is dismissed when a menu item sets "enabled = false" in onTriggered().
|
|
|
|
void tst_QQuickMenu::disableWhenTriggered()
|
|
|
|
{
|
|
|
|
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|
|
|
|
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
|
|
|
|
QSKIP("Mouse hovering not functional on offscreen/minimal platforms");
|
|
|
|
|
|
|
|
QFETCH(int, menuItemIndex);
|
|
|
|
QFETCH(int, subMenuItemIndex);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("disableWhenTriggered.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
QQuickWindow *window = helper.window;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->findChild<QQuickMenu*>("Menu");
|
|
|
|
QVERIFY(menu);
|
2024-08-29 12:57:04 +00:00
|
|
|
// TODO: QTBUG-128474
|
|
|
|
menu->setPopupType(QQuickPopup::Item);
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
|
|
|
|
menu->open();
|
|
|
|
QVERIFY(menu->isVisible());
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(menu->isOpened());
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
|
2024-08-27 06:32:07 +00:00
|
|
|
QQuickMenuPrivate *menuPrivate = QQuickMenuPrivate::get(menu);
|
|
|
|
if (menuPrivate->usePopupWindow()) {
|
|
|
|
QTRY_VERIFY(menuPrivate->popupWindow);
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(menuPrivate->popupWindow));
|
|
|
|
}
|
|
|
|
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
QPointer<QQuickMenuItem> menuItem = qobject_cast<QQuickMenuItem*>(menu->itemAt(menuItemIndex));
|
|
|
|
QVERIFY(menuItem);
|
|
|
|
|
|
|
|
if (subMenuItemIndex == -1) {
|
|
|
|
// Click a top-level menu item.
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
menuItem->mapToScene(QPointF(menuItem->width() / 2, menuItem->height() / 2)).toPoint());
|
|
|
|
QCOMPARE(menuItem->isEnabled(), false);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
} else {
|
|
|
|
// Click a sub-menu item.
|
|
|
|
QPointer<QQuickMenu> subMenu = menuItem->subMenu();
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
|
|
|
|
QPointer<QQuickMenuItem> subMenuItem = qobject_cast<QQuickMenuItem*>(subMenu->itemAt(subMenuItemIndex));
|
|
|
|
QVERIFY(subMenuItem);
|
|
|
|
|
|
|
|
// First, open the sub-menu.
|
2022-09-12 12:41:40 +00:00
|
|
|
#if !defined(Q_OS_ANDROID) and !defined(Q_OS_WEBOS)
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
QTest::mouseMove(window, menuItem->mapToScene(QPoint(1, 1)).toPoint());
|
2022-04-22 15:01:14 +00:00
|
|
|
#else
|
2022-09-12 12:41:40 +00:00
|
|
|
// On Android and webOS mouseHover does not open sub-menu, so just click on it
|
2022-04-22 15:01:14 +00:00
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
menuItem->mapToScene(QPointF(menuItem->width() / 2, menuItem->height() / 2)).toPoint());
|
|
|
|
#endif
|
|
|
|
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
QTRY_VERIFY(subMenu->isVisible());
|
2023-01-05 12:48:20 +00:00
|
|
|
#if !defined(Q_OS_ANDROID) and !defined(Q_OS_WEBOS)
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
QVERIFY(menuItem->isHovered());
|
|
|
|
QTRY_VERIFY(subMenu->contentItem()->property("contentHeight").toReal() > 0.0);
|
2022-04-22 15:01:14 +00:00
|
|
|
#endif
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
|
|
|
|
// Click the item.
|
|
|
|
QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier,
|
|
|
|
subMenuItem->mapToScene(QPointF(subMenuItem->width() / 2, subMenuItem->height() / 2)).toPoint());
|
|
|
|
QCOMPARE(subMenuItem->isEnabled(), false);
|
2019-04-29 11:03:25 +00:00
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
Fix Menu not being dismissed when the triggered item disables itself
Problem
Consider the following code:
Menu {
title: "Menu"
Action {
text: "Item"
onTriggered: enabled = false
}
}
A MenuItem (AbstractButton) is created for the Action, and when it is
clicked, this function is called:
void QQuickAbstractButtonPrivate::trigger()
{
Q_Q(QQuickAbstractButton);
if (action && action->isEnabled())
QQuickActionPrivate::get(action)->trigger(q, false);
else if (effectiveEnable)
emit q->clicked();
}
QQuickActionPrivate::get(action)->trigger(q, false) results in this
function being called:
void QQuickAbstractButtonPrivate::click()
{
Q_Q(QQuickAbstractButton);
if (effectiveEnable)
emit q->clicked();
}
Since the action (and hence the menu item) was disabled in the signal
handler, the effectiveEnable check fails and clicked() is not emitted.
This causes the menu to not be dismissed.
Solution
Before calling QQuickActionPrivate::get(action)->trigger(), store
the button's enabled state. If triggering the action causes the action
to be disabled (due to the signal handler), we can then choose whether
or not we emit QQuickAbstractButton::clicked(). Specifically, we emit
clicked() if:
- we were enabled before triggering the action, and
- we have no associated action, or it's no longer enabled
Task-number: QTBUG-69682
Change-Id: Ib4e3c313b776decc74089a6beffe415605c430be
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2018-07-30 12:28:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-12 13:21:42 +00:00
|
|
|
void tst_QQuickMenu::menuItemWidth_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("mirrored");
|
|
|
|
|
|
|
|
QTest::newRow("non-mirrored") << false;
|
|
|
|
QTest::newRow("mirrored") << true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::menuItemWidth()
|
|
|
|
{
|
|
|
|
QFETCH(bool, mirrored);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("menuItemWidths.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2019-04-12 13:21:42 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2019-04-12 13:21:42 +00:00
|
|
|
|
2021-02-23 01:51:31 +00:00
|
|
|
if (mirrored) {
|
|
|
|
QQmlExpression mirroringExpression(qmlContext(window), window,
|
|
|
|
"LayoutMirroring.childrenInherit = true; LayoutMirroring.enabled = true");
|
|
|
|
QVERIFY2(mirroringExpression.evaluate().isValid(), qPrintable(mirroringExpression.error().toString()));
|
|
|
|
}
|
2019-04-12 13:21:42 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
for (int i = 0; i < menu->count(); ++i)
|
|
|
|
QCOMPARE(menu->itemAt(i)->width(), menu->availableWidth());
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::menuItemWidthAfterMenuWidthChanged_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("mirrored");
|
|
|
|
|
|
|
|
QTest::newRow("non-mirrored") << false;
|
|
|
|
QTest::newRow("mirrored") << true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::menuItemWidthAfterMenuWidthChanged()
|
|
|
|
{
|
|
|
|
QFETCH(bool, mirrored);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("menuItemWidths.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2019-04-12 13:21:42 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2019-04-12 13:21:42 +00:00
|
|
|
|
2021-02-23 01:51:31 +00:00
|
|
|
if (mirrored) {
|
|
|
|
QQmlExpression mirroringExpression(qmlContext(window), window,
|
|
|
|
"LayoutMirroring.childrenInherit = true; LayoutMirroring.enabled = true");
|
|
|
|
QVERIFY2(mirroringExpression.evaluate().isValid(), qPrintable(mirroringExpression.error().toString()));
|
|
|
|
}
|
2019-04-12 13:21:42 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
for (int i = 0; i < menu->count(); ++i) {
|
|
|
|
// Check that the width of menu items is correct before we resize the menu.
|
|
|
|
const QQuickItem *item = menu->itemAt(i);
|
|
|
|
QVERIFY2(qFuzzyCompare(item->width(), menu->availableWidth()),
|
|
|
|
qPrintable(QString::fromLatin1("Expected width of %1 to be %2, but it's %3")
|
|
|
|
.arg(item->objectName()).arg(menu->availableWidth()).arg(item->width())));
|
|
|
|
}
|
|
|
|
|
|
|
|
menu->setWidth(menu->width() + 10);
|
|
|
|
|
|
|
|
// Check that the width of menu items is correct after we resize the menu.
|
|
|
|
for (int i = 0; i < menu->count(); ++i) {
|
|
|
|
// Check that the width of menu items is correct after we resize the menu.
|
|
|
|
const QQuickItem *item = menu->itemAt(i);
|
|
|
|
QVERIFY2(qFuzzyCompare(item->width(), menu->availableWidth()),
|
|
|
|
qPrintable(QString::fromLatin1("Expected width of %1 to be %2, but it's %3")
|
|
|
|
.arg(item->objectName()).arg(menu->availableWidth()).arg(item->width())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::menuItemWidthAfterImplicitWidthChanged_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("mirrored");
|
|
|
|
|
|
|
|
QTest::newRow("non-mirrored") << false;
|
|
|
|
QTest::newRow("mirrored") << true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::menuItemWidthAfterImplicitWidthChanged()
|
|
|
|
{
|
|
|
|
QFETCH(bool, mirrored);
|
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("menuItemWidths.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2019-04-12 13:21:42 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2019-04-12 13:21:42 +00:00
|
|
|
|
2021-02-23 01:51:31 +00:00
|
|
|
if (mirrored) {
|
|
|
|
QQmlExpression mirroringExpression(qmlContext(window), window,
|
|
|
|
"LayoutMirroring.childrenInherit = true; LayoutMirroring.enabled = true");
|
|
|
|
QVERIFY2(mirroringExpression.evaluate().isValid(), qPrintable(mirroringExpression.error().toString()));
|
|
|
|
}
|
2019-04-12 13:21:42 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
// Check that the width of the menu item is correct before we change its font size.
|
|
|
|
QQuickMenuItem *menuItem = qobject_cast<QQuickMenuItem*>(menu->itemAt(0));
|
|
|
|
QCOMPARE(menuItem->width(), menu->availableWidth());
|
|
|
|
|
|
|
|
// Add some text to increase the implicitWidth of the MenuItem.
|
|
|
|
const qreal oldImplicitWidth = menuItem->implicitWidth();
|
|
|
|
for (int i = 0; menuItem->implicitWidth() <= oldImplicitWidth; ++i) {
|
|
|
|
menuItem->setText(menuItem->text() + QLatin1String("---"));
|
|
|
|
if (i == 100)
|
|
|
|
QFAIL("Shouldn't need 100 iterations to increase MenuItem's implicitWidth; something is wrong here");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the width of the menu item is correct after we change its font size.
|
|
|
|
QCOMPARE(menuItem->width(), menu->availableWidth());
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::menuItemWidthAfterRetranslate()
|
|
|
|
{
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("menuItemWidths.qml"));
|
2020-06-05 08:28:16 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
2019-04-12 13:21:42 +00:00
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
2022-06-24 10:39:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
2019-04-12 13:21:42 +00:00
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu *>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
for (int i = 0; i < menu->count(); ++i) {
|
|
|
|
// Check that the width of each menu item is correct before we retranslate.
|
|
|
|
const QQuickItem *item = menu->itemAt(i);
|
|
|
|
QVERIFY2(qFuzzyCompare(item->width(), menu->availableWidth()),
|
|
|
|
qPrintable(QString::fromLatin1("Expected width of %1 to be %2, but it's %3")
|
|
|
|
.arg(item->objectName()).arg(menu->availableWidth()).arg(item->width())));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call retranslate() and cause all bindings to be re-evaluated.
|
|
|
|
helper.engine.retranslate();
|
|
|
|
|
|
|
|
for (int i = 0; i < menu->count(); ++i) {
|
|
|
|
// Check that the width of each menu item is correct after we retranslate.
|
|
|
|
const QQuickItem *item = menu->itemAt(i);
|
|
|
|
QVERIFY2(qFuzzyCompare(item->width(), menu->availableWidth()),
|
|
|
|
qPrintable(QString::fromLatin1("Expected width of %1 to be %2, but it's %3")
|
|
|
|
.arg(item->objectName()).arg(menu->availableWidth()).arg(item->width())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-20 08:34:53 +00:00
|
|
|
void tst_QQuickMenu::giveMenuItemFocusOnButtonPress()
|
|
|
|
{
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2022-06-24 10:39:41 +00:00
|
|
|
|
2021-08-06 10:27:35 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("giveMenuItemFocusOnButtonPress.qml"));
|
2020-10-20 08:34:53 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
tst_qquickmenu: call requestActivate before QTest::qWaitForWindowActive
On a Ubuntu 22.04.4 VM, every test after the first one that didn't call
requestActivate before QTest::qWaitForWindowActive would fail:
qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in ""
********* Start testing of tst_QQuickMenu *********
Config: Using QtTest library 6.8.0, Qt 6.8.0 (x86_64-little_endian-lp64 shared (dynamic) debug build; by GCC 11.4.0), ubuntu 22.04
PASS : tst_QQuickMenu::Basic::initTestCase()
PASS : tst_QQuickMenu::Basic::defaults()
PASS : tst_QQuickMenu::Basic::count()
PASS : tst_QQuickMenu::Basic::mouse()
PASS : tst_QQuickMenu::Basic::pressAndHold()
PASS : tst_QQuickMenu::Basic::contextMenuKeyboard()
PASS : tst_QQuickMenu::Basic::disabledMenuItemKeyNavigation()
PASS : tst_QQuickMenu::Basic::mnemonics()
PASS : tst_QQuickMenu::Basic::menuButton()
PASS : tst_QQuickMenu::Basic::addItem()
FAIL! : tst_QQuickMenu::Basic::menuSeparator() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(697)]
PASS : tst_QQuickMenu::Basic::repeater()
PASS : tst_QQuickMenu::Basic::order()
PASS : tst_QQuickMenu::Basic::popup()
PASS : tst_QQuickMenu::Basic::actions()
FAIL! : tst_QQuickMenu::Basic::actionShortcuts() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1084)]
PASS : tst_QQuickMenu::Basic::removeTakeItem()
PASS : tst_QQuickMenu::Basic::subMenuMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuMouse(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(cascading)
PASS : tst_QQuickMenu::Basic::subMenuDisabledMouse(non-cascading)
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1384)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
FAIL! : tst_QQuickMenu::Basic::subMenuDisabledKeyboard(non-cascading,mirrored) 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(1513)]
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(cascading,mirrored,flip,overlap)
PASS : tst_QQuickMenu::Basic::subMenuPosition(non-cascading)
PASS : tst_QQuickMenu::Basic::subMenuWithIcon()
PASS : tst_QQuickMenu::Basic::addRemoveSubMenus()
PASS : tst_QQuickMenu::Basic::scrollable(Window)
PASS : tst_QQuickMenu::Basic::scrollable(ApplicationWindow)
PASS : tst_QQuickMenu::Basic::scrollable(WithPadding)
PASS : tst_QQuickMenu::Basic::scrollable(FixedHeight)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-Action)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared inside)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with Action declared outside menu)
PASS : tst_QQuickMenu::Basic::disableWhenTriggered(Sub-MenuItem with no Action)
PASS : tst_QQuickMenu::Basic::menuItemWidth(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidth(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterMenuWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(non-mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterImplicitWidthChanged(mirrored)
PASS : tst_QQuickMenu::Basic::menuItemWidthAfterRetranslate()
FAIL! : tst_QQuickMenu::Basic::giveMenuItemFocusOnButtonPress() 'QTest::qWaitForWindowActive(window)' returned FALSE. ()
Loc: [/home/mitch/dev/qt-dev/qtdeclarative/tests/auto/quickcontrols/qquickmenu/tst_qquickmenu.cpp(2097)]
PASS : tst_QQuickMenu::Basic::customMenuCullItems()
PASS : tst_QQuickMenu::Basic::customMenuUseRepeaterAsTheContentItem()
PASS : tst_QQuickMenu::Basic::invalidUrlInImgTag()
PASS : tst_QQuickMenu::Basic::nativeStatic()
PASS : tst_QQuickMenu::Basic::nativeDynamicActions()
PASS : tst_QQuickMenu::Basic::nativeDynamicSubmenus()
PASS : tst_QQuickMenu::Basic::nativeMenuSeparator()
PASS : tst_QQuickMenu::Basic::dontUseNativeMenuWindowsChanges()
PASS : tst_QQuickMenu::Basic::nativeMixedItems()
PASS : tst_QQuickMenu::Basic::cleanupTestCase()
Totals: 59 passed, 11 failed, 0 skipped, 0 blacklisted, 74936ms
********* Finished testing of tst_QQuickMenu *********
Change-Id: I5ef76d2648fc7c8732aafab3a9a0a02844075c3e
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
2024-03-20 07:33:47 +00:00
|
|
|
window->requestActivate();
|
2020-10-20 08:34:53 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
|
|
|
|
// Press enter on the button to open the menu.
|
|
|
|
QQuickButton *menuButton = window->property("menuButton").value<QQuickButton*>();
|
|
|
|
QVERIFY(menuButton);
|
|
|
|
menuButton->forceActiveFocus();
|
|
|
|
QVERIFY(menuButton->hasActiveFocus());
|
|
|
|
|
|
|
|
QSignalSpy clickedSpy(window, SIGNAL(menuButtonClicked()));
|
|
|
|
QVERIFY(clickedSpy.isValid());
|
|
|
|
|
|
|
|
QTest::keyClick(window, Qt::Key_Return);
|
2022-10-05 05:29:16 +00:00
|
|
|
QCOMPARE(clickedSpy.size(), 1);
|
2020-10-20 08:34:53 +00:00
|
|
|
|
|
|
|
// The menu should still be open.
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
}
|
|
|
|
|
2022-06-21 03:10:00 +00:00
|
|
|
void tst_QQuickMenu::customMenuCullItems()
|
|
|
|
{
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("customMenuCullItems.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
|
|
|
|
QQuickItem *menuItemFirst = menu->itemAt(0);
|
|
|
|
QQuickItem *menuItemLast = menu->itemAt(menu->count() - 1);
|
|
|
|
QVERIFY(menuItemFirst);
|
|
|
|
QVERIFY(menuItemLast);
|
|
|
|
QTRY_VERIFY(!QQuickItemPrivate::get(menuItemFirst)->culled);
|
|
|
|
QTRY_VERIFY(QQuickItemPrivate::get(menuItemLast)->culled);
|
|
|
|
}
|
|
|
|
|
2022-06-24 02:38:51 +00:00
|
|
|
void tst_QQuickMenu::customMenuUseRepeaterAsTheContentItem()
|
|
|
|
{
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("customMenuUseRepeaterAsTheContentItem.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
QQuickItem *menuItemFirst = menu->itemAt(0);
|
|
|
|
QQuickItem *menuItemLast = menu->itemAt(menu->count() - 1);
|
|
|
|
QTRY_VERIFY(!QQuickItemPrivate::get(menuItemFirst)->culled);
|
|
|
|
QTRY_VERIFY(!QQuickItemPrivate::get(menuItemLast)->culled);
|
|
|
|
}
|
|
|
|
|
2023-09-05 20:14:30 +00:00
|
|
|
// QTBUG-116672 - Application loads menu containing invalid styled text
|
|
|
|
// (img tag) without crash
|
|
|
|
void tst_QQuickMenu::invalidUrlInImgTag()
|
|
|
|
{
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, "StyledText - Invalid base url in img tag");
|
|
|
|
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("invalidUrlInImgTag.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
menu->open();
|
|
|
|
QTRY_VERIFY(menu->isVisible());
|
|
|
|
|
|
|
|
QQuickMenuItem *menuItemFirst = qobject_cast<QQuickMenuItem *>(menu->itemAt(0));
|
|
|
|
QVERIFY(menuItemFirst);
|
|
|
|
}
|
|
|
|
|
2024-02-07 07:10:33 +00:00
|
|
|
void tst_QQuickMenu::nativeStatic()
|
2024-02-05 08:51:46 +00:00
|
|
|
{
|
2024-06-27 10:18:09 +00:00
|
|
|
// Undo the setting of AA_DontUseNativeMenuWindows to true from init()
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
|
|
|
|
|
2024-02-07 07:10:33 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeStatic.qml"));
|
2024-02-05 08:51:46 +00:00
|
|
|
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);
|
2024-03-14 03:36:06 +00:00
|
|
|
QVERIFY(contextMenuPrivate->useNativeMenu());
|
2024-02-05 08:51:46 +00:00
|
|
|
|
|
|
|
// Check that the actions of the parent menu can be accessed
|
|
|
|
// and are in the appropriate places in contentModel and contentData.
|
|
|
|
auto *action1 = contextMenu->actionAt(0);
|
|
|
|
QVERIFY(action1);
|
|
|
|
auto *action1MenuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0));
|
|
|
|
QVERIFY(action1MenuItem);
|
|
|
|
QCOMPARE(action1MenuItem->action(), action1);
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(contextMenuPrivate->contentData.at(0)),
|
|
|
|
action1MenuItem);
|
|
|
|
|
2024-02-07 07:10:33 +00:00
|
|
|
auto *menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(1));
|
|
|
|
QVERIFY(menuItem);
|
|
|
|
QVERIFY(menuItem->action());
|
|
|
|
QCOMPARE(menuItem->action()->text(), "menuItemAction");
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(contextMenuPrivate->contentData.at(1)), menuItem);
|
2024-02-05 08:51:46 +00:00
|
|
|
|
|
|
|
// Check that the sub-menu can be accessed and is in the
|
|
|
|
// appropriate place in contentData.
|
|
|
|
auto *subMenu = contextMenu->menuAt(2);
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
|
|
|
|
auto *subMenuAction1 = subMenu->actionAt(0);
|
|
|
|
QVERIFY(subMenuAction1);
|
|
|
|
auto *subMenuAction1MenuItem = qobject_cast<QQuickMenuItem *>(subMenu->itemAt(0));
|
|
|
|
QVERIFY(subMenuAction1MenuItem);
|
|
|
|
QCOMPARE(subMenuAction1MenuItem->action(), subMenuAction1);
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(subMenuPrivate->contentData.at(0)),
|
|
|
|
subMenuAction1MenuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::nativeDynamicActions()
|
|
|
|
{
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeEmptyMenu.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");
|
|
|
|
auto *action1MenuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0));
|
|
|
|
QVERIFY(action1MenuItem);
|
|
|
|
QCOMPARE(action1MenuItem->action(), action1);
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(contextMenuPrivate->contentData.at(0)),
|
|
|
|
action1MenuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
auto *action2MenuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(1));
|
|
|
|
QVERIFY(action2MenuItem);
|
|
|
|
QCOMPARE(action2MenuItem->action(), action2);
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(contextMenuPrivate->contentData.at(1)),
|
|
|
|
action2MenuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
auto *action0MenuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0));
|
|
|
|
QVERIFY(action0MenuItem);
|
|
|
|
QCOMPARE(action0MenuItem->action(), action0);
|
|
|
|
// New items are always appended to contentData, regardless of the actual insertion index
|
|
|
|
// in contentModel.
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(contextMenuPrivate->contentData.at(2)),
|
|
|
|
action0MenuItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::nativeDynamicSubmenus()
|
|
|
|
{
|
2024-06-27 10:18:09 +00:00
|
|
|
// Undo the setting of AA_DontUseNativeMenuWindows to true from init()
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
|
|
|
|
|
2024-02-05 08:51:46 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeDynamicSubmenus.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);
|
|
|
|
|
|
|
|
// We construct the sub-menu first in QML. At least on Windows, menu items
|
|
|
|
// added to an empty sub-menu won't show up (tested with Widgets): QTBUG-120494.
|
|
|
|
// So, this adds an already-populated menu as a sub-menu.
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "addSubMenu", Q_ARG(QString, "subMenu1")));
|
|
|
|
auto subMenu1 = contextMenu->menuAt(0);
|
|
|
|
QVERIFY(subMenu1);
|
|
|
|
QCOMPARE(subMenu1->title(), "subMenu1");
|
|
|
|
auto *subMenu1Private = QQuickMenuPrivate::get(subMenu1);
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported) {
|
|
|
|
QVERIFY(subMenu1Private->handle);
|
|
|
|
QCOMPARE(subMenu1Private->nativeItems.size(), 1);
|
|
|
|
}
|
2024-02-05 08:51:46 +00:00
|
|
|
auto *subMenu1MenuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0));
|
|
|
|
QVERIFY(subMenu1MenuItem);
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(contextMenuPrivate->contentData.at(0)),
|
|
|
|
subMenu1MenuItem);
|
|
|
|
QCOMPARE(contextMenuPrivate->contentData.size(), 1);
|
|
|
|
{
|
|
|
|
auto subMenuAction1 = subMenu1->actionAt(0);
|
|
|
|
QVERIFY(subMenuAction1);
|
|
|
|
QCOMPARE(subMenuAction1->text(), "subMenu1Action1");
|
|
|
|
auto *subMenuAction1MenuItem = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(0));
|
|
|
|
QVERIFY(subMenuAction1MenuItem);
|
|
|
|
QCOMPARE(subMenuAction1MenuItem->action(), subMenuAction1);
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(subMenu1Private->contentData.at(0)),
|
|
|
|
subMenuAction1MenuItem);
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported)
|
|
|
|
QCOMPARE(subMenu1Private->nativeItems.size(), 1);
|
2024-02-05 08:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check that actions can be appended after existing items in the sub-menu.
|
|
|
|
QCOMPARE(subMenu1->actionAt(1), nullptr);
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "addAction",
|
|
|
|
Q_ARG(QQuickMenu *, subMenu1), Q_ARG(QString, "subMenu1Action2")));
|
|
|
|
{
|
|
|
|
auto subMenu1Action2 = subMenu1->actionAt(1);
|
|
|
|
QVERIFY(subMenu1Action2);
|
|
|
|
QCOMPARE(subMenu1Action2->text(), "subMenu1Action2");
|
|
|
|
auto *subMenu1Action2MenuItem = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(1));
|
|
|
|
QVERIFY(subMenu1Action2MenuItem);
|
|
|
|
QCOMPARE(subMenu1Action2MenuItem->action(), subMenu1Action2);
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(subMenu1Private->contentData.at(1)),
|
|
|
|
subMenu1Action2MenuItem);
|
|
|
|
QCOMPARE(subMenu1Private->contentData.size(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that actions can be inserted before existing items in the sub-menu.
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "insertAction",
|
|
|
|
Q_ARG(QQuickMenu *, subMenu1), Q_ARG(int, 0), Q_ARG(QString, "subMenu1Action0")));
|
|
|
|
{
|
|
|
|
auto subMenu1Action0 = subMenu1->actionAt(0);
|
|
|
|
QVERIFY(subMenu1Action0);
|
|
|
|
QCOMPARE(subMenu1Action0->text(), "subMenu1Action0");
|
|
|
|
auto *subMenu1Action0MenuItem = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(0));
|
|
|
|
QVERIFY(subMenu1Action0MenuItem);
|
|
|
|
QCOMPARE(subMenu1Action0MenuItem->action(), subMenu1Action0);
|
|
|
|
// New items are always appended to contentData, regardless of the actual insertion index
|
|
|
|
// in contentModel.
|
|
|
|
COMPARE_MENUITEMS(qobject_cast<QQuickMenuItem *>(subMenu1Private->contentData.at(2)),
|
|
|
|
subMenu1Action0MenuItem);
|
|
|
|
QCOMPARE(subMenu1Private->contentData.size(), 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Check that takeMenu works.
|
|
|
|
auto *takenSubMenu = contextMenu->takeMenu(0);
|
|
|
|
QCOMPARE(takenSubMenu, subMenu1);
|
|
|
|
QCOMPARE(contextMenuPrivate->contentData.size(), 0);
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported) {
|
|
|
|
QVERIFY(!subMenu1Private->handle);
|
|
|
|
QCOMPARE(subMenu1Private->nativeItems.size(), 0);
|
|
|
|
}
|
2024-02-05 08:51:46 +00:00
|
|
|
|
|
|
|
// Check that the sub-menu can be added back in to the menu.
|
|
|
|
contextMenu->addMenu(takenSubMenu);
|
|
|
|
QCOMPARE(contextMenuPrivate->contentData.size(), 1);
|
|
|
|
auto *subMenu1MenuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0));
|
|
|
|
QVERIFY(subMenu1MenuItem);
|
|
|
|
QCOMPARE(subMenu1MenuItem->text(), "subMenu1");
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported) {
|
|
|
|
QVERIFY(subMenu1Private->handle);
|
|
|
|
QCOMPARE(subMenu1Private->nativeItems.size(), 3);
|
|
|
|
}
|
2024-02-05 08:51:46 +00:00
|
|
|
QCOMPARE(subMenu1Private->contentData.size(), 3);
|
|
|
|
|
|
|
|
auto *subMenu1Action0MenuItem = qobject_cast<QQuickMenuItem *>(subMenu1->itemAt(0));
|
|
|
|
QVERIFY(subMenu1Action0MenuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that removeMenu works.
|
|
|
|
QVERIFY(contextMenu->menuAt(0));
|
|
|
|
contextMenu->removeMenu(contextMenu->menuAt(0));
|
|
|
|
QCOMPARE(contextMenuPrivate->contentData.size(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::nativeMenuSeparator()
|
|
|
|
{
|
2024-06-27 10:18:09 +00:00
|
|
|
// Undo the setting of AA_DontUseNativeMenuWindows to true from init()
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
|
|
|
|
|
2024-02-05 08:51:46 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeMenuSeparator.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
// Check that separators in menus are where we expect them to be.
|
|
|
|
QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(contextMenu);
|
|
|
|
auto *contextMenuSeparatorAsItem = contextMenu->itemAt(1);
|
|
|
|
QVERIFY(contextMenuSeparatorAsItem);
|
|
|
|
auto *contextMenuSeparator = qobject_cast<QQuickMenuSeparator *>(contextMenuSeparatorAsItem);
|
|
|
|
QVERIFY(contextMenuSeparator);
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported) {
|
|
|
|
auto *contextMenuPrivate = QQuickMenuPrivate::get(contextMenu);
|
|
|
|
QCOMPARE(contextMenuPrivate->nativeItems.size(), 3);
|
|
|
|
auto *contextMenuSeparatorNativeItem = contextMenuPrivate->nativeItems.at(1);
|
|
|
|
QVERIFY(contextMenuSeparatorNativeItem);
|
|
|
|
QVERIFY(contextMenuSeparatorNativeItem->separator());
|
|
|
|
}
|
2024-02-05 08:51:46 +00:00
|
|
|
|
|
|
|
// Check that separators in sub-menus are where we expect them to be.
|
|
|
|
QQuickMenu *subMenu = window->property("contextMenu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
auto *subMenuSeparatorAsItem = subMenu->itemAt(1);
|
|
|
|
QVERIFY(subMenuSeparatorAsItem);
|
|
|
|
auto *subMenuSeparator = qobject_cast<QQuickMenuSeparator *>(subMenuSeparatorAsItem);
|
|
|
|
QVERIFY(subMenuSeparator);
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported) {
|
|
|
|
auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
|
|
|
|
QCOMPARE(subMenuPrivate->nativeItems.size(), 3);
|
|
|
|
auto *subMenuSeparatorNativeItem = subMenuPrivate->nativeItems.at(1);
|
|
|
|
QVERIFY(subMenuSeparatorNativeItem);
|
|
|
|
QVERIFY(subMenuSeparatorNativeItem->separator());
|
|
|
|
}
|
2024-02-05 08:51:46 +00:00
|
|
|
}
|
|
|
|
|
2024-06-27 10:18:09 +00:00
|
|
|
void tst_QQuickMenu::AA_DontUseNativeMenuWindows()
|
|
|
|
{
|
|
|
|
// Check that we end up with a non-native menu when AA_DontUseNativeMenuWindows
|
|
|
|
// is set, even if popupType is Native.
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
|
|
|
|
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
QQuickMenuPrivate *menu_d = QQuickMenuPrivate::get(menu);
|
|
|
|
|
|
|
|
menu->setPopupType(QQuickPopup::Native);
|
|
|
|
// Note: since a native menu is blocking, we cannot open it, in case
|
|
|
|
// the test fails and it actually opens. That would block the test as
|
|
|
|
// well. So we just check that useNativeMenu returns false for now.
|
|
|
|
QVERIFY(!menu_d->useNativeMenu());
|
|
|
|
QVERIFY(!menu_d->maybeNativeHandle());
|
|
|
|
}
|
|
|
|
|
2024-03-14 03:36:06 +00:00
|
|
|
void tst_QQuickMenu::dontUseNativeMenuWindowsChanges()
|
2024-02-05 08:51:46 +00:00
|
|
|
{
|
Replace AA_DontUsePopupWindows with Popup::popupType
The application attribute approach has some weaknesses which makes it
less suitable, than simply having a property which can be set per
instance of Popup. In addition, given just how many of our own tests
that are failing, when changing the default behavior of popup, it`s a
much safer approach to introduce this feature as an opt-in, in the
beginning. We can instead potentially modify the behavior of
PopupType::Default in the future, when we feel that it's less risky
to do so.
To give more context to why the AA approach is bad:
- The AA is too grandiose of a solution, which adds an arbitrary
limitation of not allowing mixing of in-scene popups and popup windows.
This also affects 3rd party libraries.
- It causes inconveniences when styling the various popup types.
Dialog, for instance, has a title property, which should be shown in
the window title, and not inside the popup window.
- It also makes it less flexible for potential future changes. We've
learned that this change is riskier, than initially expected, since
the qobject and visual hierarchy of popup objects are different when
using popup windows, and tests that simulate events,
will need to send the events to a different window.
The introduction of the PopupType::Default value, allows us to, for
instance, add a property later in ApplicationWindow, or somewhere else,
to change the behavior of PopupType::Default. Meaning that we can still
add a more grandiose API, that affects the behavior of multiple popup
instances, if we think it's too cumbersome for developers to add
`popupType: Popup.Window` to all popup instances.
Task-number: QTBUG-121363
Change-Id: I544da820261607621a9b9ad5c4c9679e676e44a0
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2024-05-27 22:01:20 +00:00
|
|
|
QSKIP("QTBUG-125967 This test will need to be fixed, by using popupType: Popup.Native instead of AA_DontUseNativeMenuWindows.");
|
|
|
|
|
2024-03-14 03:36:06 +00:00
|
|
|
if (QSysInfo::productType() == QLatin1String("b2qt"))
|
|
|
|
QSKIP("b2qt doesn't support native menus");
|
|
|
|
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
|
2024-02-07 07:10:33 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeStatic.qml"));
|
2024-02-05 08:51:46 +00:00
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
window->show();
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(window));
|
|
|
|
|
|
|
|
QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(contextMenu);
|
|
|
|
QCOMPARE(contextMenu->count(), 3);
|
2024-03-14 03:36:06 +00:00
|
|
|
// Sub-menus should respect the native-ness of their parents.
|
2024-02-05 08:51:46 +00:00
|
|
|
auto *subMenu = contextMenu->menuAt(2);
|
|
|
|
auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
|
|
|
|
QVERIFY(subMenuPrivate->useNativeMenu());
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported)
|
|
|
|
QVERIFY(subMenuPrivate->handle);
|
|
|
|
else
|
|
|
|
QVERIFY(!subMenuPrivate->handle);
|
2024-02-05 08:51:46 +00:00
|
|
|
|
|
|
|
// Ensure that the menu and its sub-menu have enough room to open.
|
|
|
|
if (window->width() / 2 <= contextMenu->width())
|
|
|
|
window->setWidth(contextMenu->width() * 2 + 1);
|
|
|
|
if (window->height() <= contextMenu->height())
|
|
|
|
window->setHeight(contextMenu->height() + 1);
|
|
|
|
QTRY_COMPARE(window->contentItem()->size(), window->size());
|
|
|
|
|
|
|
|
// We can't test that aboutToShow/aboutToHide is emitted for native menus
|
|
|
|
// because when they are shown, the event loop is blocked until they are closed.
|
|
|
|
// So we just check that a native menu is actually in use before going on to test
|
|
|
|
// non-native menus.
|
|
|
|
auto *contextMenuPrivate = QQuickMenuPrivate::get(contextMenu);
|
2024-02-07 12:52:20 +00:00
|
|
|
if (nativeMenuSupported)
|
|
|
|
QVERIFY(contextMenuPrivate->handle);
|
|
|
|
else
|
|
|
|
QVERIFY(!contextMenuPrivate->handle);
|
2024-03-14 03:36:06 +00:00
|
|
|
|
|
|
|
// We need to wait until the menu is opened before it picks up the changes,
|
|
|
|
// which is why we don't check the native handle here yet.
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
|
2024-02-02 10:32:34 +00:00
|
|
|
QVERIFY(!contextMenuPrivate->useNativeMenu());
|
2024-02-05 08:51:46 +00:00
|
|
|
QVERIFY(!subMenuPrivate->useNativeMenu());
|
|
|
|
|
|
|
|
// Check that we can open the menu by right-clicking (or just open it manually
|
|
|
|
// if the platform doesn't support (moving) QCursor).
|
|
|
|
QSignalSpy aboutToShowSpy(contextMenu, &QQuickMenu::aboutToShow);
|
|
|
|
QVERIFY(aboutToShowSpy.isValid());
|
|
|
|
bool couldMoveCursorPos = false;
|
|
|
|
const QPoint cursorPos(1, 1);
|
|
|
|
#if QT_CONFIG(cursor)
|
|
|
|
// Try moving the cursor from the current position to test if the platform
|
|
|
|
// supports moving the cursor.
|
|
|
|
const QPoint point = QCursor::pos() + QPoint(1, 1);
|
|
|
|
QCursor::setPos(point);
|
|
|
|
if (QTest::qWaitFor([point]{ return QCursor::pos() == point; })) {
|
|
|
|
couldMoveCursorPos = true;
|
|
|
|
const QPoint globalCursorPos = window->mapToGlobal(cursorPos);
|
|
|
|
QCursor::setPos(globalCursorPos);
|
|
|
|
QTest::mouseClick(window, Qt::RightButton, Qt::NoModifier, cursorPos);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!couldMoveCursorPos) {
|
|
|
|
contextMenu->setX(cursorPos.x());
|
|
|
|
contextMenu->setY(cursorPos.y());
|
|
|
|
contextMenu->open();
|
|
|
|
}
|
|
|
|
QVERIFY(contextMenu->isVisible());
|
|
|
|
QTRY_VERIFY(contextMenu->isOpened());
|
|
|
|
QCOMPARE(aboutToShowSpy.size(), 1);
|
2024-03-14 03:36:06 +00:00
|
|
|
// Now that it's open and has picked up the changes to Qt::AA_DontUseNativeMenuWindows, we can check it.
|
|
|
|
QVERIFY(!contextMenuPrivate->handle);
|
|
|
|
QVERIFY(!subMenuPrivate->handle);
|
2024-02-05 08:51:46 +00:00
|
|
|
// Check that it opened at the mouse cursor and actually has menu items.
|
|
|
|
QCOMPARE(contextMenu->x(), cursorPos.x());
|
|
|
|
QCOMPARE(contextMenu->y(), cursorPos.y());
|
|
|
|
auto *action1MenuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0));
|
|
|
|
QVERIFY(action1MenuItem);
|
|
|
|
QCOMPARE(action1MenuItem->text(), "action1");
|
|
|
|
|
2024-03-14 03:36:06 +00:00
|
|
|
// Test setting Qt::AA_DontUseNativeMenuWindows while visible has no effect
|
|
|
|
// (until it's re-opened, which we can't test because we can't test opening native menus).
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
|
|
|
|
QVERIFY(contextMenuPrivate->useNativeMenu());
|
2024-02-06 06:20:10 +00:00
|
|
|
QVERIFY(!contextMenuPrivate->handle);
|
|
|
|
QVERIFY(!subMenuPrivate->handle);
|
2024-02-05 08:51:46 +00:00
|
|
|
|
|
|
|
// Also check the submenu.
|
|
|
|
auto *subAction1MenuItem = qobject_cast<QQuickMenuItem *>(subMenu->itemAt(0));
|
|
|
|
QVERIFY(subAction1MenuItem);
|
|
|
|
QCOMPARE(subAction1MenuItem->text(), "subAction1");
|
|
|
|
|
|
|
|
// Test closing the non-native menu by clicking on an item.
|
|
|
|
QSignalSpy aboutToHideSpy(contextMenu, &QQuickMenu::aboutToHide);
|
|
|
|
QVERIFY(aboutToHideSpy.isValid());
|
|
|
|
QVERIFY(clickButton(action1MenuItem));
|
|
|
|
QVERIFY(!contextMenu->isOpened());
|
|
|
|
QTRY_VERIFY(!contextMenu->isVisible());
|
|
|
|
QCOMPARE(aboutToShowSpy.size(), 1);
|
|
|
|
|
|
|
|
// Although we can't open the native menu, we can at least check that
|
2024-02-06 06:20:10 +00:00
|
|
|
// attempting (the changes won't come into effect until it's re-opened)
|
|
|
|
// to make the menu native again doesn't e.g. crash.
|
2024-02-05 08:51:46 +00:00
|
|
|
QVERIFY(contextMenuPrivate->useNativeMenu());
|
|
|
|
QVERIFY(subMenuPrivate->useNativeMenu());
|
2024-02-06 06:20:10 +00:00
|
|
|
QVERIFY(!contextMenuPrivate->handle);
|
|
|
|
QVERIFY(!subMenuPrivate->handle);
|
2024-02-05 08:51:46 +00:00
|
|
|
}
|
|
|
|
|
2024-02-12 06:35:17 +00:00
|
|
|
// Check that non-menu items (e.g. Rectangles) can be inserted between menu items without issues.
|
|
|
|
void tst_QQuickMenu::nativeMixedItems()
|
|
|
|
{
|
2024-03-14 03:36:06 +00:00
|
|
|
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
|
2024-02-12 06:35:17 +00:00
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeMixedItems.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);
|
|
|
|
|
|
|
|
// Insert a Rectangle between the Action and MenuItem in the top-level menu.
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "insertRectangle",
|
|
|
|
Q_ARG(QQuickMenu *, contextMenu), Q_ARG(int, 1), Q_ARG(QColor, QColorConstants::Red)));
|
|
|
|
{
|
|
|
|
auto *action = contextMenu->actionAt(0);
|
|
|
|
QVERIFY(action);
|
|
|
|
QCOMPARE(action->text(), "action");
|
|
|
|
auto *rectangle = qobject_cast<QQuickRectangle *>(contextMenu->itemAt(1));
|
|
|
|
QVERIFY(rectangle);
|
|
|
|
QCOMPARE(rectangle->color(), QColorConstants::Red);
|
|
|
|
auto *menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(2));
|
|
|
|
QVERIFY(menuItem);
|
|
|
|
QCOMPARE(menuItem->text(), "menuItem");
|
|
|
|
auto *subMenu = contextMenu->menuAt(3);
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
QCOMPARE(subMenu->title(), "subMenu");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert a Rectangle at the end of all of the items (which were: {Action, Rectangle, MenuItem, Menu}).
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "insertRectangle",
|
|
|
|
Q_ARG(QQuickMenu *, contextMenu), Q_ARG(int, 4), Q_ARG(QColor, QColorConstants::Blue)));
|
|
|
|
{
|
|
|
|
auto *action = contextMenu->actionAt(0);
|
|
|
|
QVERIFY(action);
|
|
|
|
QCOMPARE(action->text(), "action");
|
|
|
|
auto *rectangle1 = qobject_cast<QQuickRectangle *>(contextMenu->itemAt(1));
|
|
|
|
QVERIFY(rectangle1);
|
|
|
|
QCOMPARE(rectangle1->color(), QColorConstants::Red);
|
|
|
|
auto *menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(2));
|
|
|
|
QVERIFY(menuItem);
|
|
|
|
QCOMPARE(menuItem->text(), "menuItem");
|
|
|
|
auto *subMenu = contextMenu->menuAt(3);
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
QCOMPARE(subMenu->title(), "subMenu");
|
|
|
|
auto *rectangle2 = qobject_cast<QQuickRectangle *>(contextMenu->itemAt(4));
|
|
|
|
QVERIFY(rectangle2);
|
|
|
|
QCOMPARE(rectangle2->color(), QColorConstants::Blue);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the sub-menu can be accessed and is in the
|
|
|
|
// appropriate place in contentData.
|
|
|
|
auto *subMenu = contextMenu->menuAt(3);
|
|
|
|
QVERIFY(subMenu);
|
|
|
|
// Insert a Rectangle between the Action and MenuItem in the top-level menu.
|
|
|
|
QVERIFY(QMetaObject::invokeMethod(window, "insertRectangle",
|
|
|
|
Q_ARG(QQuickMenu *, subMenu), Q_ARG(int, 1), Q_ARG(QColor, QColorConstants::Green)));
|
|
|
|
{
|
|
|
|
auto *action1 = subMenu->actionAt(0);
|
|
|
|
QVERIFY(action1);
|
|
|
|
QCOMPARE(action1->text(), "subAction1");
|
|
|
|
auto *rectangle = qobject_cast<QQuickRectangle *>(subMenu->itemAt(1));
|
|
|
|
QVERIFY(rectangle);
|
|
|
|
QCOMPARE(rectangle->color(), QColorConstants::Green);
|
|
|
|
auto *action2 = subMenu->actionAt(2);
|
|
|
|
QVERIFY(action2);
|
|
|
|
QCOMPARE(action2->text(), "subAction2");
|
|
|
|
}
|
|
|
|
}
|
2024-06-03 14:03:48 +00:00
|
|
|
void tst_QQuickMenu::effectivePosition_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QQuickPopup::PopupType>("popupType");
|
|
|
|
|
|
|
|
QTest::newRow("Item") << QQuickPopup::Item;
|
2024-09-03 00:04:46 +00:00
|
|
|
if (popupWindowsSupported)
|
|
|
|
QTest::newRow("Window") << QQuickPopup::Window;
|
2024-06-03 14:03:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::effectivePosition()
|
|
|
|
{
|
QQuickPopup: support popups with negative insets
A Popup can have negative insets. This means that the
the background will be drawn outside the bounds of the
popup. This is fine when the popup is an item in the
scene, since items are allowed to draw out-of-bounds.
But when placing the popup inside a window, this is no
longer true; Since the window will be resized to the
same size as the popup, the background will be clipped.
Since we plan to make popup windows the default in
Qt 6.8, this clipping will quickly be seen as a regression.
This issue can be seen if opening a context menu in the
Imagine style, since the menus in that style use negative
insets to draw a drop-shadow outside the popup.
In order to make sure that we end up drawing the whole
popup also when using popup windows, this patch will ensure
that we resize the window to also include the (negative)
insets. By positioning the popupItem inside the window
according to the insets, and at the same time, move the window
a bit back and up accordingly, the whole popupItem will
be visible and end up at the requested position.
With this change, the menus in the Imagine style will be
drawn correctly. So update the macOS and Windows styles
as well so that they all implement shadows the same way.
This also allowes us to remove some early changes done
to QQuickPopupItem, like overriding the contains()
function.
Pick-to: 6.8
Change-Id: I7a0b597bc768d3c485a125c644139dfc7eb0b8ab
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2024-06-17 08:52:38 +00:00
|
|
|
// Check that the top-left corner of the background delegate is placed
|
|
|
|
// at the requested menu position. If the popup have negative insets, it
|
|
|
|
// means that the background is drawn outside the bounds of the popup.
|
|
|
|
// To ensure that the whole background still ends up visible on screen, also
|
|
|
|
// when using QQuickPopup::Window, the window will be resized bigger to
|
|
|
|
// encompass both the background and the insets. The window will then be
|
|
|
|
// moved a bit left and up relative to the requested popup position, and the
|
|
|
|
// background the opposite way, to ensure that the top-left of the background
|
|
|
|
// ends up at the requested position.
|
2024-06-03 14:03:48 +00:00
|
|
|
QFETCH(QQuickPopup::PopupType, popupType);
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2024-06-03 14:03:48 +00:00
|
|
|
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
centerOnScreen(window);
|
|
|
|
window->show();
|
|
|
|
window->requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
|
|
|
menu->setPopupType(popupType);
|
|
|
|
|
QQuickPopup: support popups with negative insets
A Popup can have negative insets. This means that the
the background will be drawn outside the bounds of the
popup. This is fine when the popup is an item in the
scene, since items are allowed to draw out-of-bounds.
But when placing the popup inside a window, this is no
longer true; Since the window will be resized to the
same size as the popup, the background will be clipped.
Since we plan to make popup windows the default in
Qt 6.8, this clipping will quickly be seen as a regression.
This issue can be seen if opening a context menu in the
Imagine style, since the menus in that style use negative
insets to draw a drop-shadow outside the popup.
In order to make sure that we end up drawing the whole
popup also when using popup windows, this patch will ensure
that we resize the window to also include the (negative)
insets. By positioning the popupItem inside the window
according to the insets, and at the same time, move the window
a bit back and up accordingly, the whole popupItem will
be visible and end up at the requested position.
With this change, the menus in the Imagine style will be
drawn correctly. So update the macOS and Windows styles
as well so that they all implement shadows the same way.
This also allowes us to remove some early changes done
to QQuickPopupItem, like overriding the contains()
function.
Pick-to: 6.8
Change-Id: I7a0b597bc768d3c485a125c644139dfc7eb0b8ab
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2024-06-17 08:52:38 +00:00
|
|
|
// Move the background delegate a bit out of the popup, to simulate using a drop-shadow.
|
|
|
|
const QPointF insets = QPointF{-20, -10};
|
2024-06-03 14:03:48 +00:00
|
|
|
menu->setLeftInset(insets.x());
|
|
|
|
menu->setTopInset(insets.y());
|
|
|
|
|
|
|
|
const QPointF requestedPos{100, 100};
|
|
|
|
menu->popup(requestedPos);
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
|
QQuickPopup: support popups with negative insets
A Popup can have negative insets. This means that the
the background will be drawn outside the bounds of the
popup. This is fine when the popup is an item in the
scene, since items are allowed to draw out-of-bounds.
But when placing the popup inside a window, this is no
longer true; Since the window will be resized to the
same size as the popup, the background will be clipped.
Since we plan to make popup windows the default in
Qt 6.8, this clipping will quickly be seen as a regression.
This issue can be seen if opening a context menu in the
Imagine style, since the menus in that style use negative
insets to draw a drop-shadow outside the popup.
In order to make sure that we end up drawing the whole
popup also when using popup windows, this patch will ensure
that we resize the window to also include the (negative)
insets. By positioning the popupItem inside the window
according to the insets, and at the same time, move the window
a bit back and up accordingly, the whole popupItem will
be visible and end up at the requested position.
With this change, the menus in the Imagine style will be
drawn correctly. So update the macOS and Windows styles
as well so that they all implement shadows the same way.
This also allowes us to remove some early changes done
to QQuickPopupItem, like overriding the contains()
function.
Pick-to: 6.8
Change-Id: I7a0b597bc768d3c485a125c644139dfc7eb0b8ab
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2024-06-17 08:52:38 +00:00
|
|
|
// The logical position of the menu should still be the requested position
|
2024-06-03 14:03:48 +00:00
|
|
|
QTRY_COMPARE(menu->position(), requestedPos);
|
|
|
|
QCOMPARE(menu->x(), requestedPos.x());
|
|
|
|
QCOMPARE(menu->y(), requestedPos.y());
|
|
|
|
|
QQuickPopup: support popups with negative insets
A Popup can have negative insets. This means that the
the background will be drawn outside the bounds of the
popup. This is fine when the popup is an item in the
scene, since items are allowed to draw out-of-bounds.
But when placing the popup inside a window, this is no
longer true; Since the window will be resized to the
same size as the popup, the background will be clipped.
Since we plan to make popup windows the default in
Qt 6.8, this clipping will quickly be seen as a regression.
This issue can be seen if opening a context menu in the
Imagine style, since the menus in that style use negative
insets to draw a drop-shadow outside the popup.
In order to make sure that we end up drawing the whole
popup also when using popup windows, this patch will ensure
that we resize the window to also include the (negative)
insets. By positioning the popupItem inside the window
according to the insets, and at the same time, move the window
a bit back and up accordingly, the whole popupItem will
be visible and end up at the requested position.
With this change, the menus in the Imagine style will be
drawn correctly. So update the macOS and Windows styles
as well so that they all implement shadows the same way.
This also allowes us to remove some early changes done
to QQuickPopupItem, like overriding the contains()
function.
Pick-to: 6.8
Change-Id: I7a0b597bc768d3c485a125c644139dfc7eb0b8ab
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2024-06-17 08:52:38 +00:00
|
|
|
// But in reality, the background delegate is placed at the
|
|
|
|
// requested position pluss the (negative) insets
|
2024-06-03 14:03:48 +00:00
|
|
|
const auto background = menu->background();
|
|
|
|
QVERIFY(background);
|
2024-09-03 00:04:46 +00:00
|
|
|
const QPoint actualBgPos = background->mapToItem(menu->parentItem(), {0, 0}).toPoint();
|
|
|
|
const QPoint requestedPlusInsets = (requestedPos + insets).toPoint();
|
|
|
|
QVERIFY2(qAbs(actualBgPos.x() - requestedPlusInsets.x()) < 2,
|
|
|
|
qPrintable(QStringLiteral("The background's requsted x value %1 didn't match the actual value of %2")
|
|
|
|
.arg(actualBgPos.x()).arg(requestedPlusInsets.x())));
|
|
|
|
QVERIFY2(qAbs(actualBgPos.y() - requestedPlusInsets.y()) < 2,
|
|
|
|
qPrintable(QStringLiteral("The background's requsted y value %1 didn't match the actual value of %2")
|
|
|
|
.arg(actualBgPos.y()).arg(requestedPlusInsets.y())));
|
2024-06-03 14:03:48 +00:00
|
|
|
}
|
2024-02-12 06:35:17 +00:00
|
|
|
|
2024-05-22 20:15:53 +00:00
|
|
|
void tst_QQuickMenu::textPadding()
|
|
|
|
{
|
|
|
|
// Check that you can set implicitTextPadding on each MenuItem, and that
|
|
|
|
// textPadding will end up as the maximum implicitTextPadding among all the
|
|
|
|
// MenuItems in the same Menu.
|
|
|
|
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeMixedItems.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);
|
Replace AA_DontUsePopupWindows with Popup::popupType
The application attribute approach has some weaknesses which makes it
less suitable, than simply having a property which can be set per
instance of Popup. In addition, given just how many of our own tests
that are failing, when changing the default behavior of popup, it`s a
much safer approach to introduce this feature as an opt-in, in the
beginning. We can instead potentially modify the behavior of
PopupType::Default in the future, when we feel that it's less risky
to do so.
To give more context to why the AA approach is bad:
- The AA is too grandiose of a solution, which adds an arbitrary
limitation of not allowing mixing of in-scene popups and popup windows.
This also affects 3rd party libraries.
- It causes inconveniences when styling the various popup types.
Dialog, for instance, has a title property, which should be shown in
the window title, and not inside the popup window.
- It also makes it less flexible for potential future changes. We've
learned that this change is riskier, than initially expected, since
the qobject and visual hierarchy of popup objects are different when
using popup windows, and tests that simulate events,
will need to send the events to a different window.
The introduction of the PopupType::Default value, allows us to, for
instance, add a property later in ApplicationWindow, or somewhere else,
to change the behavior of PopupType::Default. Meaning that we can still
add a more grandiose API, that affects the behavior of multiple popup
instances, if we think it's too cumbersome for developers to add
`popupType: Popup.Window` to all popup instances.
Task-number: QTBUG-121363
Change-Id: I544da820261607621a9b9ad5c4c9679e676e44a0
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
2024-05-27 22:01:20 +00:00
|
|
|
contextMenu->setPopupType(QQuickPopup::Item);
|
2024-05-22 20:15:53 +00:00
|
|
|
|
|
|
|
contextMenu->setVisible(true);
|
|
|
|
|
|
|
|
// Go through all MenuItems, and give them an implicitTextPadding of 0
|
|
|
|
for (int i = 0; i < contextMenu->count(); ++i) {
|
|
|
|
auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i));
|
|
|
|
QVERIFY(menuItem);
|
|
|
|
menuItem->setImplicitTextPadding(0);
|
|
|
|
QCOMPARE(menuItem->implicitTextPadding(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that all MenuItems now has a textPadding of 0
|
|
|
|
for (int i = 0; i < contextMenu->count(); ++i) {
|
|
|
|
auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i));
|
|
|
|
QCOMPARE(menuItem->textPadding(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let the first MenuItem get a implicitTextPadding of 100. This will
|
|
|
|
// make all MenuItems get a textPadding of 100.
|
|
|
|
auto firstItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(0));
|
|
|
|
firstItem->setImplicitTextPadding(100);
|
|
|
|
QCOMPARE(firstItem->implicitTextPadding(), 100);
|
|
|
|
QCOMPARE(firstItem->textPadding(), 100);
|
|
|
|
for (int i = 1; i < contextMenu->count(); ++i) {
|
|
|
|
auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i));
|
|
|
|
QCOMPARE(menuItem->implicitTextPadding(), 0);
|
|
|
|
QCOMPARE(menuItem->textPadding(), 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hide the MenuItem with implicitTextPadding set to 100. This
|
|
|
|
// should make all the MenuItems get a textPadding of 0 again.
|
|
|
|
firstItem->setVisible(false);
|
|
|
|
QCOMPARE(firstItem->implicitTextPadding(), 100);
|
|
|
|
for (int i = 0; i < contextMenu->count(); ++i) {
|
|
|
|
auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i));
|
|
|
|
QCOMPARE(menuItem->textPadding(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show it again
|
|
|
|
firstItem->setVisible(true);
|
|
|
|
for (int i = 0; i < contextMenu->count(); ++i) {
|
|
|
|
auto menuItem = qobject_cast<QQuickMenuItem *>(contextMenu->itemAt(i));
|
|
|
|
QCOMPARE(menuItem->textPadding(), 100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-03 09:58:44 +00:00
|
|
|
void tst_QQuickMenu::resetCurrentIndexUponPopup_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<QQuickPopup::PopupType>("popupType");
|
|
|
|
|
|
|
|
QTest::newRow("Item") << QQuickPopup::Item;
|
|
|
|
QTest::newRow("Window") << QQuickPopup::Window;
|
|
|
|
|
|
|
|
// Note: a call to Menu.popup() will be a blocking call on many platforms
|
|
|
|
// when using native menus (e.g macOS and Windows). We can therefore not
|
|
|
|
// check a Menus internal state, nor make a MenuItem current, while it's visible.
|
|
|
|
// QTest::newRow("Native") << QQuickPopup::Native;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QQuickMenu::resetCurrentIndexUponPopup()
|
|
|
|
{
|
|
|
|
// Check that currentIndex is reset back to -1 when
|
|
|
|
// a menu is repopened using the popup() function without
|
|
|
|
// providing a MenuItem as argument.
|
|
|
|
QFETCH(QQuickPopup::PopupType, popupType);
|
2024-09-05 07:02:00 +00:00
|
|
|
SKIP_IF_NO_WINDOW_ACTIVATION;
|
2024-06-03 09:58:44 +00:00
|
|
|
|
|
|
|
QQuickControlsApplicationHelper helper(this, QLatin1String("applicationwindow.qml"));
|
|
|
|
QVERIFY2(helper.ready, helper.failureMessage());
|
|
|
|
|
|
|
|
QQuickApplicationWindow *window = helper.appWindow;
|
|
|
|
centerOnScreen(window);
|
|
|
|
window->show();
|
|
|
|
window->requestActivate();
|
|
|
|
QVERIFY(QTest::qWaitForWindowActive(window));
|
|
|
|
|
|
|
|
QQuickMenu *menu = window->property("menu").value<QQuickMenu*>();
|
|
|
|
QVERIFY(menu);
|
|
|
|
|
|
|
|
menu->setPopupType(popupType);
|
|
|
|
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
|
|
|
menu->popup();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
|
|
|
|
menu->setCurrentIndex(1);
|
|
|
|
QCOMPARE(menu->currentIndex(), 1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(1));
|
|
|
|
|
|
|
|
menu->close();
|
|
|
|
QTRY_VERIFY(!menu->isVisible());
|
|
|
|
|
|
|
|
menu->popup();
|
|
|
|
QTRY_VERIFY(menu->isOpened());
|
|
|
|
QCOMPARE(menu->currentIndex(), -1);
|
|
|
|
QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1));
|
|
|
|
}
|
|
|
|
|
2019-04-29 11:03:25 +00:00
|
|
|
QTEST_QUICKCONTROLS_MAIN(tst_QQuickMenu)
|
2015-11-16 15:06:07 +00:00
|
|
|
|
2017-09-27 13:14:32 +00:00
|
|
|
#include "tst_qquickmenu.moc"
|