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.
Commenting for this post has ended.
Splendid! I see ComboBox and ToolTip using a real popup lurking around the corner ;-)
Can we hope for an OS WebView based Qt WebView on desktop platforms (without QtWebEngine)?
The goal is indeed for these kinds of controls (combo box drop downs, tooltips, etc) to be top level windows down the line.
The work to implement a Qt WebView backend using WebView2 on windows is tracked by QTBUG-75747. I don't know if there's an alternative for Linux, so there we likely need QtWebEngine for the foreseeable future. If you're in a hurry the new WindowContainer API should give you the building block to embed a WebView2 manually.
The second window in the initial app with the dialog at the top does not minimize correctly in my view on Windows, it certainly does not minimize with the same behavior as the primary window (Commenting out the flags property) . The secondary window gets minimized as a tiny title bar and does not seem to expand to the original size when clicked. For me the work around has been to create a second window in C++.Also I wish the QML dialogs were not embedded into its parent component. This might be proper behavior on a embedded device but I think it should be a normal independent window on a desktop (having tried it on Linux). Qt/QML is getting better with every release. Thank you Qt engineers for making such a great opensource product
Thanks for the support David!
The
dialog
window in the initial example will be a transient child of the main window, as a result of the automatic transient parent logic for Qt Quick windows. One of the effects of a window being a transient child is that it may have a different representation in the system task switcher UI. If you prefer thedialog
window to be an independent top level window you can override the transient parent by setting it tonull
.For QML dialogs (and related controls such as Popup) we're working on making them top level windows on relevant platforms. Stay tuned! :)
This may have a big impact on the roadmap of Qt Application Manager framework!
I was wondering if this can be extended to the WebAssembly version of Qt. Could we "embed" an HTML/javascript/CSS document provided by the browser? The typical application I am looking for is embedding a WebRTC application inside a QML Window for instance. Any thoughts ?
The plan is definitely to enable this for Qt for WebAssembly. It requires support for foreign windows on WASM, which is tracked by QTBUG-116185. The foreign window
WId
would likely be anemscripten::val
representing a HTML element. With a bit of plumbing on the application side you should then be able to embed your<div>
or<iframe>
.I think I have been waiting for this for 10 years. This means that we can finally render a QML interface on top of a window that's being managed by a game engine like Ogre or Panda, right ? It's something that I managed to do with QtWidget, which was a great way to create game editors... but I had never been able to do it with QML, which would've been perfect for in-game interfaces. That's great news. Goodbye Awesomium.
Actually, the use-case you are describing (embedding Qt UIs into other non-Qt UIs) is the opposite of what this new Qt Quick feature is about (embedding non-Qt UIs into Qt Quick UIs). They are definitely related though, and some of the work for this new feature has likely improved the other use-case as well. To embed a Qt Quick UI in a non-Qt UI you can use QWindow::fromWinId to wrap the non-Qt window, and then use setParent on the
QQuickWindow
to reparent it into the foreign UI.Very Nice! I was trying out a simple use case to have a movable window inside main window. here is my code snippet. ``` import QtQuick import QtQuick.Controls
Window { id: mainWindow width: 640 height: 480 visible: true
} ```
If you detach the widow and drag it using the MouseArea the window moves fine, but if I do the same when it is being inside I see weird flickering because of wrong position while dragging. How do I fix it?
Please file a bug report and CC me, thanks!
Provide the code snippets is python so that we know the intended type of usage
The snippets in this blog post are QML, showcasing the QML APIs for window containment. If you're referring to the windowembedding manual test, that's just a minimal C++ wrapper. You'll find matching Python APIs in Qt for Python to e.g. create a foreign window.