Menu, MenuBar: remove requestNative and rely solely on app attributes

It was decided that we'll have two attributes:

- AA_DontUseNativeMenuBar
- AA_DontUseNativeMenuWindows

Setting AA_DontUseNativeMenuWindows only affects windows we create
(context menus, combobox menus, menus of non-native menu bars).
So, setting AA_DontUseNativeMenuWindows restores Qt to today's behavior.
But we can't control the windows of native menu bars, so if you don't
want those to be native, you have to set AA_DontUseNativeMenuBar just
like today.

By removing requestNative, we also effectively default to native menus
and menu bars, as the attributes are not set by default.

[ChangeLog][Controls][Important Behavior Changes] Menu and MenuBar
now use native menus by default on platforms where they're supported.

Task-number: QTBUG-69558
Change-Id: Ia917c2f820634def0cf815aa8ca8895ca79db75d
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Mitch Curtis 2024-03-14 11:36:06 +08:00
parent 5580620706
commit 822db20f84
38 changed files with 304 additions and 287 deletions

View File

@ -284,9 +284,6 @@ void QQuickMenuPrivate::init()
{
Q_Q(QQuickMenu);
contentModel = new QQmlObjectModel(q);
// TODO: use an env var until we get Qt::AA_DontUseNativeMenu
requestNative = qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_USE_NATIVE_MENUS");
}
QQuickMenu *QQuickMenuPrivate::rootMenu() const
@ -313,7 +310,7 @@ bool QQuickMenuPrivate::useNativeMenu() const
if (QQuickMenuBarPrivate::get(menuBar)->useNativeMenuBar())
return true;
}
return root->requestNative();
return !QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuWindows);
}
QPlatformMenu *QQuickMenuPrivate::nativeHandle()
@ -440,10 +437,10 @@ void QQuickMenuPrivate::syncWithNativeMenu()
qCDebug(lcNativeMenus) << "... finished syncing" << q;
}
void QQuickMenuPrivate::syncWithRequestNative()
void QQuickMenuPrivate::syncWithUseNativeMenu()
{
Q_Q(QQuickMenu);
// Users can change requestNative on sub-menus and menus that are visible,
// Users can change AA_DontUseNativeMenuWindows while a menu is visible,
// but the changes won't take affect until the menu is re-opened.
if (q->isVisible() || parentMenu)
return;
@ -1531,11 +1528,11 @@ void QQuickMenu::setVisible(bool visible)
if (visible && ((d->useNativeMenu() && !d->maybeNativeHandle())
|| (!d->useNativeMenu() && d->maybeNativeHandle()))) {
// We've been made visible, and our actual native state doesn't match our requested state,
// which means requestNative was set while we were visible or had a parent. Try to sync our
// state again now that we're about to be re-opened.
// which means AA_DontUseNativeMenuWindows was set while we were visible or had a parent.
// Try to sync our state again now that we're about to be re-opened.
qCDebug(lcNativeMenus) << "setVisible called - useNativeMenu:" << d->useNativeMenu()
<< "maybeNativeHandle:" << d->maybeNativeHandle();
d->syncWithRequestNative();
d->syncWithUseNativeMenu();
}
if (d->maybeNativeHandle()) {
d->setNativeMenuVisible(visible);
@ -1702,29 +1699,6 @@ void QQuickMenu::resetCascade()
setCascade(shouldCascade());
}
bool QQuickMenu::requestNative() const
{
Q_D(const QQuickMenu);
return d->requestNative;
}
void QQuickMenu::setRequestNative(bool native)
{
Q_D(QQuickMenu);
if (d->requestNative == native)
return;
d->requestNative = native;
if (d->complete)
d->syncWithRequestNative();
emit requestNativeChanged();
}
void QQuickMenu::resetRequestNative()
{
setRequestNative(true);
}
/*!
\since QtQuick.Controls 2.3 (Qt 5.10)
\qmlproperty real QtQuick.Controls::Menu::overlap
@ -1991,7 +1965,7 @@ void QQuickMenu::componentComplete()
Q_D(QQuickMenu);
QQuickPopup::componentComplete();
d->resizeItems();
d->syncWithRequestNative();
d->syncWithUseNativeMenu();
}
void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)

View File

@ -45,9 +45,6 @@ class Q_QUICKTEMPLATES2_EXPORT QQuickMenu : public QQuickPopup
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL REVISION(2, 3))
// 6.5 (Qt 6.5)
Q_PROPERTY(QQuickIcon icon READ icon WRITE setIcon NOTIFY iconChanged FINAL REVISION(6, 5))
// 6.8
Q_PROPERTY(bool requestNative READ requestNative WRITE setRequestNative RESET resetRequestNative
NOTIFY requestNativeChanged REVISION(6, 8))
Q_CLASSINFO("DefaultProperty", "contentData")
QML_NAMED_ELEMENT(Menu)
QML_ADDED_IN_VERSION(2, 0)
@ -75,10 +72,6 @@ public:
void setCascade(bool cascade);
void resetCascade();
bool requestNative() const;
void setRequestNative(bool native);
void resetRequestNative();
qreal overlap() const;
void setOverlap(qreal overlap);
@ -129,8 +122,6 @@ Q_SIGNALS:
Q_REVISION(2, 3) void currentIndexChanged();
// 6.5 (Qt 6.5)
Q_REVISION(6, 5) void iconChanged(const QQuickIcon &icon);
// 6.7
Q_REVISION(6, 7) void requestNativeChanged();
protected:
void timerEvent(QTimerEvent *event) override;

View File

@ -52,7 +52,7 @@ public:
bool useNativeMenu() const;
bool createNativeMenu();
void syncWithNativeMenu();
void syncWithRequestNative();
void syncWithUseNativeMenu();
static void recursivelyDestroyNativeSubMenus(QQuickMenu *menu);
void setNativeMenuVisible(bool visible);
@ -120,7 +120,6 @@ public:
QPalette defaultPalette() const override;
bool cascade = false;
bool requestNative = false;
bool triedToCreateNativeMenu = false;
int hoverTimer = 0;
int currentIndex = -1;

View File

@ -448,7 +448,7 @@ QQuickMenu *QQuickMenuBarPrivate::takeMenu(int index)
bool QQuickMenuBarPrivate::useNativeMenuBar() const
{
return requestNative && !QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar);
return !QCoreApplication::testAttribute(Qt::AA_DontUseNativeMenuBar);
}
void QQuickMenuBarPrivate::syncNativeMenuBarVisible()
@ -458,6 +458,8 @@ void QQuickMenuBarPrivate::syncNativeMenuBarVisible()
return;
const bool shouldBeVisible = q->isVisible() && useNativeMenuBar();
qCDebug(lcMenuBar) << "syncNativeMenuBarVisible called - q->isVisible()" << q->isVisible()
<< "useNativeMenuBar()" << useNativeMenuBar() << "handle" << handle.get();
if (shouldBeVisible && !handle)
createNativeMenuBar();
else if (!shouldBeVisible && handle)
@ -846,6 +848,7 @@ void QQuickMenuBar::itemChange(QQuickItem::ItemChange change, const QQuickItem::
}
break;
case ItemVisibleHasChanged:
qCDebug(lcMenuBar) << "visibility of" << this << "changed to" << isVisible();
d->syncNativeMenuBarVisible();
break;
default:
@ -908,28 +911,6 @@ QAccessible::Role QQuickMenuBar::accessibleRole() const
}
#endif
bool QQuickMenuBar::requestNative() const
{
return d_func()->requestNative;
}
void QQuickMenuBar::setRequestNative(bool requestNative)
{
Q_D(QQuickMenuBar);
if (d->requestNative == requestNative)
return;
d->requestNative = requestNative;
d->syncNativeMenuBarVisible();
emit requestNativeChanged();
}
void QQuickMenuBar::resetRequestNative()
{
setRequestNative(false);
}
QT_END_NAMESPACE
#include "moc_qquickmenubar_p.cpp"

View File

@ -30,8 +30,6 @@ class Q_QUICKTEMPLATES2_EXPORT QQuickMenuBar : public QQuickContainer
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
Q_PRIVATE_PROPERTY(QQuickMenuBar::d_func(), QQmlListProperty<QQuickMenu> menus READ menus NOTIFY menusChanged FINAL)
Q_PRIVATE_PROPERTY(QQuickMenuBar::d_func(), QQmlListProperty<QObject> contentData READ contentData FINAL)
Q_PROPERTY(bool requestNative READ requestNative WRITE setRequestNative RESET resetRequestNative
NOTIFY requestNativeChanged REVISION(6, 8))
QML_NAMED_ELEMENT(MenuBar)
QML_ADDED_IN_VERSION(2, 3)
@ -48,14 +46,9 @@ public:
Q_INVOKABLE void removeMenu(QQuickMenu *menu);
Q_INVOKABLE QQuickMenu *takeMenu(int index);
bool requestNative() const;
void setRequestNative(bool requestNative);
void resetRequestNative();
Q_SIGNALS:
void delegateChanged();
void menusChanged();
void requestNativeChanged();
protected:
bool eventFilter(QObject *object, QEvent *event) override;

View File

@ -88,7 +88,6 @@ public:
bool triggering = false;
bool altPressed = false;
bool requestNative = false;
QQmlComponent *delegate = nullptr;
QPointer<QQuickMenuBarItem> currentItem;
QPointer<QQuickItem> windowContentItem;

View File

@ -8,6 +8,9 @@ int main(int argc, char *argv[])
{
QTEST_SET_MAIN_SOURCE_PATH
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("Basic");
return quick_test_main(argc, argv, "tst_controls::Basic", TST_CONTROLS_DATA);
}

View File

@ -8,6 +8,9 @@ int main(int argc, char *argv[])
{
QTEST_SET_MAIN_SOURCE_PATH
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("Fusion");
return quick_test_main(argc, argv, "tst_controls::Fusion", TST_CONTROLS_DATA);
}

View File

@ -8,6 +8,9 @@ int main(int argc, char *argv[])
{
QTEST_SET_MAIN_SOURCE_PATH
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("Imagine");
return quick_test_main(argc, argv, "tst_controls::Imagine", TST_CONTROLS_DATA);
}

View File

@ -8,6 +8,9 @@ int main(int argc, char *argv[])
{
QTEST_SET_MAIN_SOURCE_PATH
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("iOS");
return quick_test_main(argc, argv, "tst_controls::iOS", TST_CONTROLS_DATA);
}

View File

@ -10,6 +10,9 @@ int main(int argc, char *argv[])
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
// See comment in tst_windows.cpp.
qputenv("QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("macOS");
return quick_test_main(argc, argv, "tst_controls::macOS", TST_CONTROLS_DATA);
}

View File

@ -8,6 +8,9 @@ int main(int argc, char *argv[])
{
QTEST_SET_MAIN_SOURCE_PATH
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("Material");
return quick_test_main(argc, argv, "tst_controls::Material", TST_CONTROLS_DATA);
}

View File

@ -8,6 +8,9 @@ int main(int argc, char *argv[])
{
QTEST_SET_MAIN_SOURCE_PATH
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("Universal");
return quick_test_main(argc, argv, "tst_controls::Universal", TST_CONTROLS_DATA);
}

View File

@ -17,6 +17,9 @@ int main(int argc, char *argv[])
// issued when default-constructing controls. For that we have
// tst_customization::noCustomizationWarningsForDefaultControls.
qputenv("QT_QUICK_CONTROLS_IGNORE_CUSTOMIZATION_WARNINGS", "1");
// The tests were originally written before native menus existed,
// and some of them try to open menus, which we can't test natively.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("Windows");
return quick_test_main(argc, argv, "tst_controls::Windows", TST_CONTROLS_DATA);
}

View File

@ -52,6 +52,7 @@ tst_focus::tst_focus()
void tst_focus::initTestCase()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickStyle::setStyle("Basic");
QQmlDataTest::initTestCase();
}

View File

@ -58,6 +58,8 @@ private slots:
tst_QQuickApplicationWindow::tst_QQuickApplicationWindow()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QQuickStyle::setStyle("Basic");
}

View File

@ -2,4 +2,18 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(tst_qquickmaterialstyle)
class Setup : public QObject
{
Q_OBJECT
public slots:
void applicationAvailable()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
}
};
QUICK_TEST_MAIN_WITH_SETUP(tst_qquickmaterialstyle, Setup)
#include "tst_qquickmaterialstyle.moc"

View File

@ -47,6 +47,5 @@ ApplicationWindow {
Menu {
id: contextMenu
objectName: "menu"
requestNative: true
}
}

View File

@ -46,6 +46,5 @@ ApplicationWindow {
Menu {
id: contextMenu
objectName: "menu"
requestNative: true
}
}

View File

@ -13,7 +13,6 @@ ApplicationWindow {
Menu {
id: contextMenu
objectName: "menu"
requestNative: true
Action {
objectName: text

View File

@ -36,7 +36,6 @@ ApplicationWindow {
Menu {
id: contextMenu
objectName: "menu"
requestNative: true
Action {
objectName: text

View File

@ -14,7 +14,6 @@ ApplicationWindow {
Menu {
id: contextMenu
objectName: "menu"
requestNative: true
Action {
objectName: text
@ -36,7 +35,6 @@ ApplicationWindow {
title: "subMenu"
objectName: title
// TODO: remove me when the defaults are true
requestNative: true
Action {
objectName: text

View File

@ -46,6 +46,8 @@ public:
tst_QQuickMenu();
private slots:
void init();
void defaults();
void count();
void mouse();
@ -98,7 +100,7 @@ private slots:
void nativeDynamicActions();
void nativeDynamicSubmenus();
void nativeMenuSeparator();
void requestNativeChanges();
void dontUseNativeMenuWindowsChanges();
void nativeMixedItems();
private:
@ -123,6 +125,16 @@ tst_QQuickMenu::tst_QQuickMenu()
nativeMenuSupported = platformMenu != nullptr;
}
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);
}
bool tst_QQuickMenu::hasWindowActivation()
{
return (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation));
@ -2189,6 +2201,7 @@ void tst_QQuickMenu::invalidUrlInImgTag()
void tst_QQuickMenu::nativeStatic()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeStatic.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
@ -2197,8 +2210,8 @@ void tst_QQuickMenu::nativeStatic()
QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>();
QVERIFY(contextMenu);
QVERIFY(contextMenu->requestNative());
auto *contextMenuPrivate = QQuickMenuPrivate::get(contextMenu);
QVERIFY(contextMenuPrivate->useNativeMenu());
// Check that the actions of the parent menu can be accessed
// and are in the appropriate places in contentModel and contentData.
@ -2232,6 +2245,7 @@ void tst_QQuickMenu::nativeStatic()
void tst_QQuickMenu::nativeDynamicActions()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeEmptyMenu.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
@ -2291,6 +2305,7 @@ void tst_QQuickMenu::nativeDynamicActions()
void tst_QQuickMenu::nativeDynamicSubmenus()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeDynamicSubmenus.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
@ -2398,6 +2413,7 @@ void tst_QQuickMenu::nativeDynamicSubmenus()
void tst_QQuickMenu::nativeMenuSeparator()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeMenuSeparator.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
@ -2435,8 +2451,12 @@ void tst_QQuickMenu::nativeMenuSeparator()
}
}
void tst_QQuickMenu::requestNativeChanges()
void tst_QQuickMenu::dontUseNativeMenuWindowsChanges()
{
if (QSysInfo::productType() == QLatin1String("b2qt"))
QSKIP("b2qt doesn't support native menus");
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeStatic.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
@ -2445,9 +2465,8 @@ void tst_QQuickMenu::requestNativeChanges()
QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>();
QVERIFY(contextMenu);
QVERIFY(contextMenu->requestNative());
QCOMPARE(contextMenu->count(), 3);
// Sub-menus should respect the value of requestNative of their parents.
// Sub-menus should respect the native-ness of their parents.
auto *subMenu = contextMenu->menuAt(2);
auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
QVERIFY(subMenuPrivate->useNativeMenu());
@ -2472,12 +2491,12 @@ void tst_QQuickMenu::requestNativeChanges()
QVERIFY(contextMenuPrivate->handle);
else
QVERIFY(!contextMenuPrivate->handle);
contextMenu->setRequestNative(false);
QVERIFY(!contextMenu->requestNative());
// 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);
QVERIFY(!contextMenuPrivate->useNativeMenu());
QVERIFY(!contextMenuPrivate->handle);
QVERIFY(!subMenuPrivate->useNativeMenu());
QVERIFY(!subMenuPrivate->handle);
// Check that we can open the menu by right-clicking (or just open it manually
// if the platform doesn't support (moving) QCursor).
@ -2505,6 +2524,9 @@ void tst_QQuickMenu::requestNativeChanges()
QVERIFY(contextMenu->isVisible());
QTRY_VERIFY(contextMenu->isOpened());
QCOMPARE(aboutToShowSpy.size(), 1);
// 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);
// Check that it opened at the mouse cursor and actually has menu items.
QCOMPARE(contextMenu->x(), cursorPos.x());
QCOMPARE(contextMenu->y(), cursorPos.y());
@ -2512,10 +2534,10 @@ void tst_QQuickMenu::requestNativeChanges()
QVERIFY(action1MenuItem);
QCOMPARE(action1MenuItem->text(), "action1");
// Test setting requestNative while visible has no effect (until it's re-opened, which we can't
// test because we can't test opening native menus).
contextMenu->setRequestNative(true);
QVERIFY(contextMenu->requestNative());
// 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());
QVERIFY(!contextMenuPrivate->handle);
QVERIFY(!subMenuPrivate->handle);
@ -2535,23 +2557,16 @@ void tst_QQuickMenu::requestNativeChanges()
// Although we can't open the native menu, we can at least check that
// attempting (the changes won't come into effect until it's re-opened)
// to make the menu native again doesn't e.g. crash.
contextMenu->setRequestNative(true);
QVERIFY(contextMenuPrivate->useNativeMenu());
QVERIFY(subMenuPrivate->useNativeMenu());
QVERIFY(!contextMenuPrivate->handle);
QVERIFY(!subMenuPrivate->handle);
// Check that setting requestNative has no (immediate) effect on a sub-menu.
subMenu->setRequestNative(false);
// Its parent still is still requesting to be native, otherwise this would be false.
QVERIFY(subMenuPrivate->useNativeMenu());
QVERIFY(!subMenuPrivate->requestNative);
QVERIFY(!subMenuPrivate->handle);
}
// Check that non-menu items (e.g. Rectangles) can be inserted between menu items without issues.
void tst_QQuickMenu::nativeMixedItems()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, false);
QQuickControlsApplicationHelper helper(this, QLatin1String("nativeMixedItems.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
QQuickApplicationWindow *window = helper.appWindow;
@ -2560,7 +2575,6 @@ void tst_QQuickMenu::nativeMixedItems()
QQuickMenu *contextMenu = window->property("contextMenu").value<QQuickMenu*>();
QVERIFY(contextMenu);
QVERIFY(contextMenu->requestNative());
// Insert a Rectangle between the Action and MenuItem in the top-level menu.
QVERIFY(QMetaObject::invokeMethod(window, "insertRectangle",

View File

@ -6,7 +6,6 @@ import QtQuick.Controls
ApplicationWindow {
id: root
property bool requestNative: false
property bool menuBarVisible: true
property alias fileMenu: fileMenu
property alias contents: contents
@ -16,7 +15,6 @@ ApplicationWindow {
visible: true
header: MenuBar {
requestNative: root.requestNative
visible: root.menuBarVisible
Menu {
id: fileMenu

View File

@ -7,7 +7,6 @@ import QtQuick.Controls
ApplicationWindow {
id: root
readonly property Button oopsButton: oopsButton
property bool requestNative: false
property alias fileMenu: fileMenu
width: 400
@ -15,7 +14,6 @@ ApplicationWindow {
visible: true
menuBar: MenuBar {
requestNative: root.requestNative
MenuBarItem {
menu: Menu {
id: fileMenu

View File

@ -6,7 +6,6 @@ import QtQuick.Controls
ApplicationWindow {
id: root
property bool requestNative: false
property bool menuBarVisible: true
property alias fileMenu: fileMenu
property alias contents: contents
@ -16,7 +15,6 @@ ApplicationWindow {
visible: true
menuBar: MenuBar {
requestNative: root.requestNative
visible: root.menuBarVisible
Menu {
id: fileMenu

View File

@ -6,7 +6,6 @@ import QtQuick.Controls
ApplicationWindow {
id: root
property bool requestNative: false
property bool menuBarVisible: true
property alias fileMenu: fileMenu
property alias contents: contents
@ -16,7 +15,6 @@ ApplicationWindow {
visible: true
menuBar: MenuBar {
requestNative: root.requestNative
visible: root.menuBarVisible
Menu {
id: fileMenu

View File

@ -50,8 +50,6 @@ private slots:
void addRemoveExistingMenus();
void checkHighlightWhenMenuDismissed();
void hoverAfterClosingWithEscape();
void requestNative_data();
void requestNative();
void AA_DontUseNativeMenuBar();
void containerItems_data();
void containerItems();
@ -89,7 +87,7 @@ bool tst_qquickmenubar::hasWindowActivation()
void tst_qquickmenubar::init()
{
// Enable non-native menubars by default.
// Note that the AA_DontUseNativeMenuBar test will set this property to 'true', which
// Note that some tests will set this property to 'true', which
// is why we need to set it back to 'false' here.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false);
}
@ -109,6 +107,9 @@ void tst_qquickmenubar::delegate()
void tst_qquickmenubar::mouse()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
if (!hasWindowActivation())
QSKIP("Window activation is not supported");
@ -294,6 +295,8 @@ void tst_qquickmenubar::mouse()
// - It's what happens with e.g. overflow menus on Android.
void tst_qquickmenubar::touch()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQuickControlsApplicationHelper helper(this, QLatin1String("touch.qml"));
QVERIFY2(helper.ready, helper.failureMessage());
centerOnScreen(helper.window);
@ -321,6 +324,8 @@ void tst_qquickmenubar::touch()
void tst_qquickmenubar::keys()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
if (!hasWindowActivation())
QSKIP("Window activation is not supported");
@ -513,6 +518,8 @@ void tst_qquickmenubar::keys()
void tst_qquickmenubar::mnemonics()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
if (!hasWindowActivation())
QSKIP("Window activation is not supported");
@ -666,6 +673,8 @@ void tst_qquickmenubar::mnemonics()
void tst_qquickmenubar::altNavigation()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
if (!QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::MenuBarFocusOnAltPressRelease).toBool())
QSKIP("Menu doesn't get focus via Alt press&release on this platform");
@ -706,7 +715,7 @@ void tst_qquickmenubar::altNavigation()
void tst_qquickmenubar::addRemove_data()
{
QTest::addColumn<QString>("testUrl");
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("menuitems, not native") << QStringLiteral("empty.qml") << false;
QTest::newRow("menuitems, native") << QStringLiteral("empty.qml") << true;
}
@ -714,17 +723,17 @@ void tst_qquickmenubar::addRemove_data()
void tst_qquickmenubar::addRemove()
{
QFETCH(QString, testUrl);
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl(testUrl));
QQuickMenuBar *menuBar = qobject_cast<QQuickMenuBar *>(engine.rootObjects().value(0));
QVERIFY(menuBar);
QQuickMenuBarPrivate *menuBarPrivate = QQuickMenuBarPrivate::get(menuBar);
QCOMPARE(menuBar->requestNative(), requestNative);
if (requestNative && nativeMenuBarSupported)
QCOMPARE(menuBarPrivate->useNativeMenuBar(), native);
if (native && nativeMenuBarSupported)
QVERIFY(menuBarPrivate->nativeHandle());
QQmlComponent component(&engine);
@ -795,7 +804,7 @@ void tst_qquickmenubar::addRemove()
void tst_qquickmenubar::addRemoveInlineMenus_data()
{
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("not native") << false;
QTest::newRow("native") << true;
}
@ -806,10 +815,10 @@ void tst_qquickmenubar::addRemoveInlineMenus()
// is an inline child from QML (fileMenu). Since it's owned by
// JavaScript, it should be deleted by the gc when appropriate, and
// not upon a call to removeMenu.
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl("menus.qml"));
auto window = qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0));
@ -840,7 +849,7 @@ void tst_qquickmenubar::addRemoveInlineMenus()
void tst_qquickmenubar::addRemoveMenuFromQml_data()
{
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("not native") << false;
QTest::newRow("native") << true;
}
@ -850,10 +859,10 @@ void tst_qquickmenubar::addRemoveMenuFromQml()
// Create a menu dynamically from QML, and add it to
// the menubar. Remove it again. Check that the
// garbage collector will then destruct it.
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl("menus.qml"));
auto window = qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0));
@ -890,17 +899,17 @@ void tst_qquickmenubar::addRemoveMenuFromQml()
void tst_qquickmenubar::insert_data()
{
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("not native") << false;
QTest::newRow("native") << true;
}
void tst_qquickmenubar::insert()
{
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl("menus.qml"));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
@ -932,8 +941,9 @@ void tst_qquickmenubar::removeMenuThatIsOpen()
// Check that if we remove a menu that is open, it ends
// up being hidden / closed. This is mostly important for
// non-native menubars.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", false }});
engine.load(testFileUrl("menus.qml"));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
@ -952,7 +962,7 @@ void tst_qquickmenubar::removeMenuThatIsOpen()
void tst_qquickmenubar::addRemoveExistingMenus_data()
{
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("not native") << false;
QTest::newRow("native") << true;
}
@ -961,10 +971,10 @@ void tst_qquickmenubar::addRemoveExistingMenus()
{
// Check that you get warnings if trying to add menus that
// are already in the menubar, or remove menus that are not.
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl("menus.qml"));
auto window = qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0));
@ -987,6 +997,8 @@ void tst_qquickmenubar::addRemoveExistingMenus()
void tst_qquickmenubar::checkHighlightWhenMenuDismissed()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Mouse highlight not functional on offscreen/minimal platforms");
@ -1045,6 +1057,8 @@ void tst_qquickmenubar::checkHighlightWhenMenuDismissed()
void tst_qquickmenubar::hoverAfterClosingWithEscape()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
if ((QGuiApplication::platformName() == QLatin1String("offscreen"))
|| (QGuiApplication::platformName() == QLatin1String("minimal")))
QSKIP("Mouse highlight not functional on offscreen/minimal platforms");
@ -1077,47 +1091,12 @@ void tst_qquickmenubar::hoverAfterClosingWithEscape()
QVERIFY(!secondMenu->isVisible());
}
void tst_qquickmenubar::requestNative_data()
{
QTest::addColumn<QString>("testUrl");
QTest::addColumn<bool>("requestNative");
QTest::newRow("menuitems, not native") << QStringLiteral("menubaritems.qml") << false;
QTest::newRow("menuitems, native") << QStringLiteral("menubaritems.qml") << true;
QTest::newRow("menus, not native") << QStringLiteral("menus.qml") << false;
QTest::newRow("menus, native") << QStringLiteral("menus.qml") << true;
}
void tst_qquickmenubar::requestNative()
{
QFETCH(QString, testUrl);
QFETCH(bool, requestNative);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl(testUrl));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
QVERIFY(window);
QQuickMenuBar *menuBar = window->property("menuBar").value<QQuickMenuBar *>();
QVERIFY(menuBar);
QCOMPARE(menuBar->requestNative(), requestNative);
QQuickMenu *fileMenu = window->property("fileMenu").value<QQuickMenu *>();
QVERIFY(fileMenu);
QVERIFY(!fileMenu->requestNative());
QQuickMenuPrivate *fileMenuPriv = QQuickMenuPrivate::get(fileMenu);
QCOMPARE(fileMenuPriv->useNativeMenu(), requestNative);
}
void tst_qquickmenubar::AA_DontUseNativeMenuBar()
{
// Check that we end up with a non-native menu bar when
// AA_DontUseNativeMenuBar is set, even if requestNative is true.
// Check that we end up with a non-native menu bar when AA_DontUseNativeMenuBar is set.
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", true }});
engine.load(testFileUrl("menus.qml"));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
@ -1138,7 +1117,7 @@ void tst_qquickmenubar::AA_DontUseNativeMenuBar()
void tst_qquickmenubar::containerItems_data()
{
QTest::addColumn<QString>("testUrl");
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("menuitems, not native") << QStringLiteral("menubaritems.qml") << false;
QTest::newRow("menuitems, native") << QStringLiteral("menubaritems.qml") << true;
QTest::newRow("menus, not native") << QStringLiteral("menus.qml") << false;
@ -1153,17 +1132,18 @@ void tst_qquickmenubar::containerItems()
// of accessing those MenuBarItems and menus in the MenuBar
// API, so check that all end up in sync.
QFETCH(QString, testUrl);
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl(testUrl));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
QVERIFY(window);
QQuickMenuBar *menuBar = window->property("menuBar").value<QQuickMenuBar *>();
QVERIFY(menuBar);
QCOMPARE(menuBar->requestNative(), requestNative);
auto *menuBarPrivate = QQuickMenuBarPrivate::get(menuBar);
QCOMPARE(menuBarPrivate->useNativeMenuBar(), native);
QCOMPARE(menuBar->count(), 4);
for (int i = 0; i < menuBar->count(); ++i) {
@ -1178,7 +1158,7 @@ void tst_qquickmenubar::containerItems()
QCOMPARE(menuBarItem->menu(), menu);
// Test the "contentData" list property API
auto cd = QQuickMenuBarPrivate::get(menuBar)->contentData();
auto cd = menuBarPrivate->contentData();
QCOMPARE(cd.count(&cd), menuBar->count());
auto cdItem = static_cast<QQuickItem *>(cd.at(&cd, i));
QVERIFY(cdItem);
@ -1197,7 +1177,7 @@ void tst_qquickmenubar::containerItems()
void tst_qquickmenubar::mixedContainerItems_data()
{
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("not native") << false;
QTest::newRow("native") << true;
}
@ -1207,10 +1187,10 @@ void tst_qquickmenubar::mixedContainerItems()
// The application is allowed to add items other
// than MenuBarItems and Menus as children. But those
// should just be ignored by the MenuBar (and the Container).
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl("mixed.qml"));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
@ -1262,8 +1242,8 @@ void tst_qquickmenubar::mixedContainerItems()
void tst_qquickmenubar::applicationWindow_data()
{
QTest::addColumn<bool>("initialRequestNative");
QTest::addColumn<bool>("initialVisible");
QTest::addColumn<bool>("initiallyNative");
QTest::addColumn<bool>("initiallyVisible");
QTest::newRow("initially not native, visible") << false << true;
QTest::newRow("initially not native, hidden") << false << false;
QTest::newRow("initially native, visible") << true << true;
@ -1273,13 +1253,14 @@ void tst_qquickmenubar::applicationWindow_data()
void tst_qquickmenubar::applicationWindow()
{
// Check that ApplicationWindow adds or removes the non-native
// menubar in response to toggling MenuBar.requestNative and
// menubar in response to toggling Qt::AA_DontUseNativeMenuBar and
// MenuBar.visible.
QFETCH(bool, initialRequestNative);
QFETCH(bool, initialVisible);
QFETCH(bool, initiallyNative);
QFETCH(bool, initiallyVisible);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !initiallyNative);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", initialRequestNative }, { "visible", initialVisible }});
engine.setInitialProperties({{ "visible", initiallyVisible }});
engine.load(testFileUrl("menus.qml"));
QPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
@ -1290,13 +1271,11 @@ void tst_qquickmenubar::applicationWindow()
QQuickItem *contents = window->property("contents").value<QQuickItem *>();
QVERIFY(contents);
for (const bool visible : {initialVisible, !initialVisible, initialVisible})
for (const bool requestNative : {initialRequestNative, !initialRequestNative, initialRequestNative}) {
menuBar->setRequestNative(requestNative);
for (const bool visible : {initiallyVisible, !initiallyVisible, initiallyVisible}) {
menuBar->setVisible(visible);
const bool nativeMenuBarVisible = bool(menuBarPrivate->nativeHandle());
QCOMPARE(nativeMenuBarVisible, nativeMenuBarSupported && requestNative && visible);
QCOMPARE(nativeMenuBarVisible, nativeMenuBarSupported && initiallyNative && visible);
if (!visible) {
QVERIFY(!menuBar->isVisible());
@ -1315,7 +1294,7 @@ void tst_qquickmenubar::applicationWindow()
void tst_qquickmenubar::menubarAsHeader_data()
{
QTest::addColumn<bool>("requestNative");
QTest::addColumn<bool>("native");
QTest::newRow("not native") << false;
QTest::newRow("native") << true;
}
@ -1325,10 +1304,10 @@ void tst_qquickmenubar::menubarAsHeader()
// ApplicationWindow.menuBar was added in Qt 5.10. Before that
// the menuBar was supposed to be assigned to ApplicationWindow.header.
// For backwards compatibility, check that you can still do that.
QFETCH(bool, requestNative);
QFETCH(bool, native);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, !native);
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "requestNative", requestNative }});
engine.load(testFileUrl("menubarAsHeader.qml"));
QScopedPointer<QQuickApplicationWindow> window(qobject_cast<QQuickApplicationWindow *>(engine.rootObjects().value(0)));
@ -1339,7 +1318,7 @@ void tst_qquickmenubar::menubarAsHeader()
QQuickItem *contents = window->property("contents").value<QQuickItem *>();
QVERIFY(contents);
QVERIFY(menuBar->count() > 0);
QCOMPARE(menuBarPrivate->nativeHandle() != nullptr, nativeMenuBarSupported && requestNative);
QCOMPARE(menuBarPrivate->nativeHandle() != nullptr, nativeMenuBarSupported && native);
if (menuBarPrivate->nativeHandle()) {
// Using native menubar

View File

@ -121,6 +121,7 @@ tst_QQuickPopup::tst_QQuickPopup()
void tst_QQuickPopup::initTestCase()
{
QQmlDataTest::initTestCase();
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
qputenv("QML_NO_TOUCH_COMPRESSION", "1");
}

View File

@ -2,4 +2,18 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQuickTest/quicktest.h>
QUICK_TEST_MAIN(tst_qquickuniversalstyle)
class Setup : public QObject
{
Q_OBJECT
public slots:
void applicationAvailable()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
}
};
QUICK_TEST_MAIN_WITH_SETUP(tst_qquickuniversalstyle, Setup)
#include "tst_qquickuniversalstyle.moc"

View File

@ -39,6 +39,9 @@ static QMap<QString, QStringPair> findSnippets(const QDir &inputDir, const QDir
void tst_Snippets::initTestCase()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar);
qInfo() << "Snippets are taken from" << QQC2_SNIPPETS_PATH;
QDir snippetsDir(QQC2_SNIPPETS_PATH);

View File

@ -1,32 +1,44 @@
# Copyright (C) 2023 The Qt Company Ltd.
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(menus LANGUAGES C CXX ASM)
find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST)
endif()
cmake_minimum_required(VERSION 3.16)
project(menus VERSION 0.1 LANGUAGES CXX)
qt_internal_add_manual_test(menus
GUI
set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Quick QuickControls2)
qt_standard_project_setup(REQUIRES 6.8)
qt_add_executable(appmenus
main.cpp
)
qt_add_qml_module(appmenus
URI Menus
VERSION 1.0
QML_FILES
Main.qml
SOURCES
cppsettings.cpp
cppsettings.h
main.cpp
LIBRARIES
Qt::CorePrivate
Qt::Gui
Qt::Qml
Qt::Quick
Qt::QuickControls2
)
# Resources:
set(qml_resource_files
"main.qml"
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(appmenus PROPERTIES
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appmenus
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
qt_internal_add_resource(menus "qml"
PREFIX
"/"
FILES
${qml_resource_files}
target_link_libraries(appmenus
PRIVATE
Qt6::Quick
Qt6::QuickControls2
)

View File

@ -1,4 +1,4 @@
// Copyright (C) 2023 The Qt Company Ltd.
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtCore
@ -28,13 +28,11 @@ ApplicationWindow {
}
menuBar: MenuBar {
requestNative: requestNativeMenuBarSwitch.checked
visible: menuBarVisibleSwitch.checked
Menu {
id: fileMenu
objectName: "file"
requestNative: true
title: qsTr("&File")
ContextAction { text: qsTr("&New...") }
ContextMenuItem { text: "menuItem" }
@ -135,9 +133,10 @@ ApplicationWindow {
anchors.fill: parent
Switch {
id: requestNativeAction
text: qsTr("Request native")
checked: true
text: qsTr("Don't use native menu windows")
checked: CppSettings.dontUseNativeMenuWindows
onClicked: CppSettings.dontUseNativeMenuWindows = checked
}
Row {
@ -186,9 +185,10 @@ ApplicationWindow {
Row {
Switch {
id: requestNativeMenuBarSwitch
text: qsTr("Request native")
checked: true
text: qsTr("Don't use native menu bar")
checked: CppSettings.dontUseNativeMenuBar
onClicked: CppSettings.dontUseNativeMenuBar = checked
}
Switch {
id: menuBarVisibleSwitch
@ -245,7 +245,6 @@ ApplicationWindow {
Menu {
id: backgroundContextMenu
objectName: "backgroundContextMenu"
requestNative: requestNativeAction.checked
function appendAction() {
let action = actionComponent.createObject(null, { text: qsTr("Extra context menu item") })
@ -331,7 +330,6 @@ ApplicationWindow {
Menu {
id: editContextMenu
objectName: "editContextMenu"
requestNative: requestNativeAction.checked
ContextAction {
text: qsTr("Cut")

View File

@ -1,4 +1,4 @@
// Copyright (C) 2020 The Qt Company Ltd.
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick.Controls

View File

@ -0,0 +1,46 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "cppsettings.h"
#include <QCoreApplication>
CppSettings::CppSettings(QObject *parent) :
QObject(parent),
mSettings("QtProject", "menus")
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, dontUseNativeMenuWindows());
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar());
}
bool CppSettings::dontUseNativeMenuWindows() const
{
return mSettings.value("dontUseNativeMenuWindows").toBool();
}
void CppSettings::setDontUseNativeMenuWindows(bool dontUseNativeMenuWindows)
{
const bool oldValue = this->dontUseNativeMenuWindows();
if (dontUseNativeMenuWindows == oldValue)
return;
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows, dontUseNativeMenuWindows);
mSettings.setValue("dontUseNativeMenuWindows", dontUseNativeMenuWindows);
emit dontUseNativeMenuWindowsChanged();
}
bool CppSettings::dontUseNativeMenuBar() const
{
return mSettings.value("dontUseNativeMenuBar").toBool();
}
void CppSettings::setDontUseNativeMenuBar(bool dontUseNativeMenuBar)
{
const bool oldValue = this->dontUseNativeMenuBar();
if (dontUseNativeMenuBar == oldValue)
return;
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar);
mSettings.setValue("dontUseNativeMenuBar", dontUseNativeMenuBar);
emit dontUseNativeMenuBarChanged();
}

View File

@ -0,0 +1,38 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef CPPSETTINGS_H
#define CPPSETTINGS_H
#include <QObject>
#include <QQmlEngine>
#include <QSettings>
class CppSettings : public QObject
{
Q_OBJECT
Q_PROPERTY(bool dontUseNativeMenuWindows READ dontUseNativeMenuWindows WRITE setDontUseNativeMenuWindows
NOTIFY dontUseNativeMenuWindowsChanged FINAL)
Q_PROPERTY(bool dontUseNativeMenuBar READ dontUseNativeMenuBar WRITE setDontUseNativeMenuBar
NOTIFY dontUseNativeMenuBarChanged FINAL)
QML_ELEMENT
QML_SINGLETON
public:
explicit CppSettings(QObject *parent = nullptr);
bool dontUseNativeMenuWindows() const;
void setDontUseNativeMenuWindows(bool dontUseNativeMenuWindows);
bool dontUseNativeMenuBar() const;
void setDontUseNativeMenuBar(bool dontUseNativeMenuBar);
signals:
void dontUseNativeMenuWindowsChanged();
void dontUseNativeMenuBarChanged();
private:
QSettings mSettings;
};
#endif // CPPSETTINGS_H

View File

@ -7,17 +7,20 @@
int main(int argc, char *argv[])
{
QGuiApplication::setApplicationName("menus");
QGuiApplication::setOrganizationName("QtProject");
QGuiApplication::setApplicationName("menus");
QGuiApplication app(argc, argv);
// app.setAttribute(Qt::AA_DontUseNativeMenuBar, true);
qputenv("QT_QUICK_CONTROLS_USE_NATIVE_MENUS", "1");
QQmlApplicationEngine engine;
engine.setInitialProperties({{ "currentStyle", QQuickStyle::name() }});
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreationFailed,
&app,
[]() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.loadFromModule("Menus", "Main");
return app.exec();
}

View File

@ -1,55 +0,0 @@
<RCC>
<qresource prefix="/">
<file>ControlContainer.qml</file>
<file>controls/Button.qml</file>
<file>controls/CheckBox.qml</file>
<file>controls/RadioButton.qml</file>
<file>controls/CheckDelegate.qml</file>
<file>controls/ComboBox.qml</file>
<file>controls/DelayButton.qml</file>
<file>controls/Dial.qml</file>
<file>controls/Frame.qml</file>
<file>controls/GroupBox.qml</file>
<file>controls/ItemDelegate.qml</file>
<file>controls/Page.qml</file>
<file>controls/PageIndicator.qml</file>
<file>controls/Pane.qml</file>
<file>controls/ProgressBar.qml</file>
<file>controls/RadioDelegate.qml</file>
<file>controls/RangeSlider.qml</file>
<file>controls/RoundButton.qml</file>
<file>controls/ScrollBar.qml</file>
<file>controls/ScrollIndicator.qml</file>
<file>controls/Slider.qml</file>
<file>controls/SpinBox.qml</file>
<file>controls/SwipeDelegate.qml</file>
<file>controls/Switch.qml</file>
<file>controls/SwitchDelegate.qml</file>
<file>controls/TabBar.qml</file>
<file>controls/TextArea.qml</file>
<file>controls/TextField.qml</file>
<file>SettingsDialog.qml</file>
<file>ColorEditor.qml</file>
<file>controls/ToolBar.qml</file>
<file>controls/Dialog.qml</file>
<file>controls/Menu.qml</file>
<file>ExampleContainer.qml</file>
<file>controls/Label.qml</file>
<file>controls/ToolTip.qml</file>
<file>controls/Tumbler.qml</file>
<file>controls/BusyIndicator.qml</file>
<file>testbench.qml</file>
<file>controls/MenuBar.qml</file>
<file>controls/SplitView.qml</file>
<file>+Imagine/ApplicationWindow.qml</file>
<file>ApplicationWindow.qml</file>
<file>+Imagine/ToolBar.qml</file>
<file>ToolBar.qml</file>
<file>+Imagine/Menu.qml</file>
<file>Menu.qml</file>
<file>+Imagine/Dialog.qml</file>
<file>Dialog.qml</file>
<file>ContentPane.qml</file>
<file>+Imagine/ContentPane.qml</file>
</qresource>
</RCC>