Qt Blog

Popups and Menus in Qt Quick 6.8

Written by Oliver Eftevaag | Oct 3, 2024

With the release of Qt 6.8.0, it's a good time to go into more details on the new features brought to popups and menus in Qt Quick Controls.

For developers familiar with Qt Quick, it is important to understand that the current popup implementation involves creating a unique item that encompasses all of the popup's content. This item, known internally as QQuickPopupItem, is derived from the Page class and, when displayed, becomes a visual child of the Overlay. The Overlay itself is a child of the window with a high z-index value of 1000001, ensuring it appears above other elements.

This overlay works in conjunction with opened popups to determine whether a pointer event should be blocked (if the popup is modal) or if a popup should close (based on the closePolicy). The benefit of implementing popups this way is that all the logic is maintained within a single QML scene, without relying on external factors. This approach is particularly effective for mobile and embedded systems, which Qt Quick was originally designed to excel in. However, what about those who want to develop robust desktop applications using Qt Quick?

Introducing Popup Windows

If you want to open a popup in Qt Widgets, you typically want to give a widget the Qt::Popup window flag.
The window flag will determine whether a widget gets rendered inside it's parent widget, or if it should create a new top-level window on the windowing system level, to draw itself inside of.

Up until now, the only way to achieve the same kind of top-level popup windows in Qt Quick, were to create custom components based on Window. The fact that you can't use our dedicated Popup type, to open top-level popup windows, were indeed one of my most surprising realizations when I first started working on Qt Quick. It also means that derived types like Dialog, ToolTips and Menus have the same limitations. Including our non-native dialogs in the Qt Quick Dialogs module, which uses the Qt Quick Controls Dialog type under the hood.

I was directly involved with creating the Qt Quick Dialogs module for Qt 6.2. One of our goals has been to reduce the relevance of the Qt.labs.platform module, by adding equivalent features in Qt Quick Controls. But I've been reluctant with the idea of removing the various dialogs from Qt.labs.platform, due to the fact that I think the Qt Widgets dialogs offer a better user experience on desktop systems. With the main reason being that the dialogs aren't top-level windows, like the dialogs in Qt Widgets. This felt so natural to me, that I even highlighted it in the last paragraph of my previous blog post.

In Qt 6.8, it's finally possible to have Popup, Dialogs, ToolTips, and Menus, inside top-level windows. This can be controlled by the new popupType property. By default, you get the old behavior. But if you want a popup to appear inside its own dedicated window, you can now set popupType: Popup.Window like this:

Dialog {
visible: true
anchors.centerIn: parent
popupType: Popup.Window
focus: true
title: qsTr("Unsaved changes!")

Label {
text: qsTr("Do you want to save, before quitting?")
}

footer: DialogButtonBox {
standardButtons: DialogButtonBox.Yes | DialogButtonBox.No
}
}

Here is the difference between using Popup.Window and Popup.Item as the popupType.

Qt Quick Dialogs is already taking advantage of this new feature. Below, you can see how the FileDialog looks in Qt 6.7 (the first image), and in Qt 6.8 (the last image).

Note that on some embedded platforms, you might end up with the old behavior even with Popup.Window, if the platform doesn't allow you to have multiple windows opened at the same time.

Menus and MenuBars

In addition to adding the new popupType, we've also made Qt Quick applications look and feel more native on primarily Windows and macOS, by adding support for native Menus and MenuBars.

Windows

We've made improvements to the Windows style, as well as the new FluentWinUI3 style, to make the menus look and feel similar to menus found in various native Windows 11 apps, like Notepad and Paint. Below is a GIF example of a Menu, with the popupType set to Popup.Window.

Windows also supports native WIN32 context menus, which can be used when setting the popupType to Popup.Native. However, they might appear somewhat outdated, and Popup.Window can be used instead, for a more consistent look on modern platforms.

macOS

On macOS, we've finally added support for native menu bars! You will never have to choose between the QtQuick.Controls and Qt.labs.platform menu bars ever again, as the QtQuick.Controls MenuBar now create a native MenuBar by default on macOS. Below is a GIF of the Text Editor example, which no longer imports the Qt.labs.platform module.

If you want to disable the native MenuBar, for whatever reason, it can be disabled by setting the Qt::AA_DontUseNativeMenuBar application attribute.

Menus that are inside a native MenuBar, will also be native. If a Menu isn't inside a MenuBar, it's still possible to have it be native, by setting the popupType to be Popup.Native. This can be useful, if you wish to have native context menus in your application. The native status of the MenuBar takes precedence over the popupType in determining whether a Menu becomes native. Consequently, you cannot have native Menus within non-native MenuBars, even if the popupType is set to Popup.Native.

Conclusion

The new documentation for Popup explains the difference between the various popup types, in case there's any more confusion.

When I went to the KDE Akademy last year, this exact topic was brought up, and I am very happy that we've finally been able to come up with a solution. It's also a feature that many other people have asked for over the years.

Should you encounter any bugs or issues while using the features mentioned here, kindly report them at https://bugreports.qt.io/. We want desktop application developers to prefer using the popup types Popup.Window or Popup.Native, so it is crucial for us to address any identified bugs promptly.

Unfortunately, we did not manage to resolve the handling of the closePolicy before the release of 6.8.0. We are presently evaluating different solutions, which can be tracked through this patch on Gerrit, and should be fixed before 6.8.1.