This is fresh news about a new feature in Qt 5.15 that is very exciting.
Traditionally, window treatments have been a pretty boring thing. Title, Limit, Minimize, Maximize, Resize, Exit... and that's it.
However, more and more applications have recently included an application specific UI and themes. A couple of screenshots to explain what is being said here:
MacOS has been doing this for quite some time.
... and Chrome, and any other web browser.
Embedding menus in your design can save a lot of screen space.
...or it might be important for branding or design.
Unfortunately, things like this were not previously possible with Qt.
However, it was possible to remove the decoration from the window, i.e.:
Window { flags: Qt.FramelessWindowHint }
But that just left you with an undecorated window. So it couldn't be moved or changed. If you then tried to move the window or resize it by grabbing the mouse and manually setting the size and position of the window, you would quickly find that it didn't work very well. Window managers tend to have very specific behavior when moving or resizing windows. Common actions are dragging up to maximize, dragging left/right on a tile, snapping to other windows or the taskbar, resizing two windows at the same time if they are next to each other, and so on.
To be fair, one helper was previously provided - QSizeGrip. It allows you to resize any given window corner, but only works on corners, not window borders, and is only available for widget applications.
Qt 5.15 added two new methods to QWindow: startSystemMove and startSystemResize. These methods ask the window manager to take over and start its own resize or move operation. This means that snapping, tiling, and so on should work like magic, and the implementation of the header in QML becomes almost a one-liner:
DragHandler { onActiveChanged: if (active) window.startSystemMove(); target: null }
By placing this code snippet in a QtQuick element, all drag operation triggers will call the native window's move operation.
startSystemResize works similarly, except it takes a Qt::Edges argument, which is a bitfield of the window borders you captured, i.e. for the bottom right corner, you would call:
startSystemResize(Qt.RightEdge | Qt.BottomEdge)
It's also very handy, as you can easily have one handler for all four window borders and just create the borders argument like this:
DragHandler { id: resizeHandler grabPermissions: TapHandler.TakeOverForbidden target: null onActiveChanged: if (active) { const p = resizeHandler.centroid.position; let e = 0; if (p.x < border) e |= Qt.LeftEdge; if (p.x >= width - border) e |= Qt.RightEdge; if (p.y < border) e |= Qt.TopEdge; if (p.y >= height - border) e |= Qt.BottomEdge; window.startSystemResize(e); } }
If you'd like a complete example of how this can be used, a web browser mockup has been made using the new API.
Please note that while this is a cross-platform API, not all platforms support it. startSystemMove is currently supported on Wayland, X11, macOS, and Windows, while startSystemResize, on the other hand, is supported on Wayland, X11, and Windows, but not on macOS.
To deal with this, both methods return a boolean value that indicates whether the operation was supported or not. This means that if you want to implement resizing on macOS as well, you will have to check the return value of startSystemResize and try your best to implement a fallback in case of failure, i.e.:
if (!window.startSystemResize(edges)) { // your fallback code for setting window.width/height manually }
Further work in Qt will be to provide fallbacks or abstractions that do the work for you, but at least nothing should stop you from doing it yourself.
Another area of improvement is agreeing with the window manager on whether to use client-side or server-side styling. Some applications may want to support both modes and let the window manager decide, but this is currently not possible. Once FramelessWindowHint is set, there will be no server-side styling.
The third area is the window shadow. At least on Wayland, the shadow must be drawn as part of the window decoration. And while it is possible to draw shadows with QtQuick, there is currently no way to tell the QPA plugin which part of the surface is the shadow and which part is the window frame, which means that if you try to draw shadows, the window manager will currently consider the shadow part. windows, and this will mess up the tiling and snapping with other windows. On other platforms, the window manager usually draws shadows, even for client-side-decorated windows, so this is a difficult problem to solve.