Window embedding in Qt Quick
January 30, 2024 by Tor Arne Vestbø | Comments
Qt 6.7 comes with some new and exciting APIs for window management in Qt Quick. In this blog post we'll look at the changes, and what use-case they open up.
Top level windows
Window management in Qt Quick is provided by the Window
type, added way back in Qt 5.0. The type as it is today enables you to create and manage top level windows, for example:
import QtQuick
Window {
id: mainWindow
width: 300; height: 300
visible: true
color: "darkseagreen"
MouseArea {
anchors.fill: parent
onClicked: dialog.show();
}
Window {
id: dialog
flags: Qt.Dialog
color: "thistle"
}
}
Which will give a dialog window centered on top of the main window:
The centering comes as a result of the dialog having the main window as it's transient parent, associating the dialog to the main window. For QWindow
this transient relationship needs to be explicitly managed via QWindow::setTransientParent()
, but in Qt Quick we automatically enable this relationship for Windows
declared as a children of another Window
in the QML document.
This already gives the building blocks for creating top level dialogs, popup menus, tooltips, etc.
Child windows
A use-case that's not currently handled by the Window
type is child windows. Child windows live inside the top level window (or another child window), and follow the position of its parent.
On the surface this sounds exactly like any old Item in a Qt Quick scene, and normally these primitives would be sufficient, but as we'll see soon there are cases where a dedicated child window has its uses.
On the Qt GUI level child windows are managed by calling QWindow::setParent()
, so in tech preview in Qt 6.7 we have introduced a new parent
property to Window
, that allows setting the visual parent of window.
import QtQuick
Window {
id: mainWindow
width: 300; height: 300
visible: true
color: "darkseagreen"
Window {
id: childWindow
color: "thistle"
parent: mainWindow
visible: true
}
}
Which will make the window a visual child of the main window:
Unlike the transientParent
property, the parent
property needs to be set explicitly, and is not enabled automatically for windows declared as children of other windows in the QML document. There is also no built in logic for centering, matching the behavior of QWindow::setParent()
.
Windows with Item parents
However, a Window
can also have an Item
as its visual parent:
import QtQuick
Window {
id: mainWindow
width: 300; height: 300
visible: true
color: "darkseagreen"
Item {
id: item
anchors.centerIn: parent
width: childWindow.width
height: childWindow.height
Window {
id: childWindow
color: "thistle"
parent: item
visible: true
}
}
}
Which results in the window's position being relative to the item:
Z ordering
We've also added a new z property to Window
, matching the existing property for Item, which allows controlling the stacking order between sibling child windows:
import QtQuick
Window {
id: mainWindow
width: 300; height: 300
visible: true
color: "darkseagreen"
Window {
color: "thistle"
parent: mainWindow
visible: true
z: 1
}
Window {
x: 50; y: 50
color: "darkgrey"
parent: mainWindow
visible: true
}
}
Normally the stacking order of sibling windows will follow the document order, just like for items, but in this case we've made the first window stack above the second:
But why?
Now, why would a child window be useful?
One such use-case is per-window surface properties. Qt's rendering pipeline today is not color managed, meaning we don't pick up the color space of every asset (color, image, video), and color match these to a target color space. What we do support is setting the target color space of the window, via QSurfaceFormat::setColorSpace()
. If you know that your source assets are in a given color space, or do your own color matching, you can set the target color space of the window to e.g. QColorSpace::AdobeRgb
. Qt will do its best to not get in the way and deliver the pixels to the OS without touching them, letting the OS do the job of color matching the window to the final target color space of the display (if the platform supports it).
There's no QML API at the moment to cover this use-case, but with a small C++ helper to set the Window's surface format before making it visible, we can do something like this on macOS:
The outer window is the default color space of the screen (Display P3 in this case), while the inner window is explicitly set to Adobe RGB. The effect is subtle, but important for color accurate rendering.
A related case is high dynamic range (HDR), which allows video and image content to take full advantage of the brightness of modern displays to e.g. represent specular highlights in more detail. Rendering HDR content requires changes to the rendering pipeline, which on some platforms may cause shifts in the brightness level of the standard dynamic range (SDR) color values. You typically don't want the rest of your application UI to be affected by playing a HDR video, so one way to mitigate this is to use a dedicated HDR-enabled child window for the HDR content. In addition, HDR formats also often specify a color space, so using a dedicated window solves this issue as well, as described earlier.
We are working on HDR support in Qt, so stay tuned for more details on this use-case.
Embedding non-Qt Quick windows
Being able to turn Qt Quick Windows into child windows is of course great, but has limited uses for most applications. The observant reader may have noticed that supporting parenting a Window
to an Item
means that we have the ability to control the position of a child window based on an item. Wouldn't it be nice to do that with any window?
And that's exactly what we've enabled, via the new WindowContainer
type. This type is tech preview in 6.7 and mirrors the functionality provided in Qt Widgets by QWidget::
createWindowContainer()
, but for Qt Quick:
import QtQuick
Window {
id: mainWindow
width: 300; height: 300
visible: true
color: "darkseagreen"
WindowContainer {
window: openGLWindow
anchors.centerIn: parent
}
}
The contained window can be any QWindow
. In this example it's the OpenGL window from Qt's OpenGL Window Example, exposed to QML via a simple context property, resulting in:
Important Item properties like position, size, z-order, visibility, and clip, have been plumbed from the item to the window, but just like for QWidget::
createWindowContainer()
there may sharp corners, so please let us know of any issues you find!
The success of the embedding also depends on how "well behaved" the embedded window is as a child window. For example, we know that Qt Widgets has some quirks when embedded into something that's not a top level QWidget
(on account of never being written for this use-case), and this is something we're hoping to improve down the line.
Foreign windows
Finally, if we can embed any QWindow
, that means we can also embed so-called foreign windows. In Qt we use this term for QWindows
that represent native platform windows created outside of Qt, either manually using native code or via some other framework. As long as the underlying native platform window type is the same type as Qt expects (NSView
, UIView
, HWND
, xcb_window_t
, etc), you can use QWindow::fromWinId()
to create a foreign QWindow
representing the native window, which can then be embedded the same way as above.
This allows you to for example embed a native map view, video player, or web view:
And not only that. As these embedded foreign windows are "just another window", we can have other windows stacked on top (or bottom) of them, such as the Qt logo in the examples above. As long as the platform supports child windows with alpha channels (and most do), these can be semi transparent as well.
This closes the loop from earlier on why being able to parent a Window
to another item, in this case the window container, is a useful feature.
Feedback welcome
As mentioned, this feature is tech preview in the upcoming Qt 6.7 release, but can already be tried out with the current beta release, so please test and let us know of any issues you find, or if your use-case is not covered by the existing APIs.
For a quick start you can try out the windowembedding manual test, which showcases various features of the Qt Quick window embedding.
Happy embedding!
Blog Topics:
Comments
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.8 Now!
Download the latest release here: www.qt.io/download.
Qt 6.8 release focuses on technology trends like spatial computing & XR, complex data visualization in 2D & 3D, and ARM-based development for desktop.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.