mafulechka
mafulechkaMarch 18, 2020, 6:01 a.m.

Custom client side window decorations in Qt 5.15

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.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

Comments

Only authorized users can post comments.
Please, Log in or Sign up
d
  • dsfs
  • April 26, 2024, 11:56 a.m.

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
d
  • dsfs
  • April 26, 2024, 11:45 a.m.

C++ - Test 002. Constants

  • Result:50points,
  • Rating points-4
d
  • dsfs
  • April 26, 2024, 11:35 a.m.

C++ - Test 001. The first program and data types

  • Result:73points,
  • Rating points1
Last comments
k
kmssrFeb. 9, 2024, 2:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 6:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 4:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 19, 2023, 5:01 a.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
Evgenii Legotckoi
Evgenii LegotckoiMay 2, 2024, 9:07 p.m.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderCheApril 30, 2024, 11:22 a.m.
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
G
GarApril 22, 2024, 12:46 p.m.
Clipboard Как скопировать окно целиком в clipb?
Павел Дорофеев
Павел ДорофеевApril 14, 2024, 9:35 a.m.
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrexApril 4, 2024, 11:47 a.m.
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Follow us in social networks