Это свежие новости о новой функции в Qt 5.15, которая очень порадовала.
Традиционно оформление окон были довольно скучной вещью. Заголовок, ограничить, минимизировать, максимизировать, изменить размер, выйти... и всё.
Однако, в последнее время приложения всё чаще и чаще включают Пользовательский интерфейс приложения (application specific UI) и оформление тем. Пара скриншотов для объяснения, о чем здесь говорится:
MacOS занимается этим довольно давно.
... и Chrome, и любой другой веб-браузер.
Встраивание меню в оформление может сэкономить много места на экране.
... или это может быть важно для брендинга или дизайна.
К сожалению, подобные вещи ранее не были возможны с Qt.
Однако можно было убрать оформление с окна, т. е.:
Window { flags: Qt.FramelessWindowHint }
Но это только оставило вас с окном без оформлея. Таким образом, это не могло быть перемещено или изменено. Если бы вы затем попытались реализовать движение окна или изменить размер, схватив мышь и вручную установив размер и положение окна, вы быстро обнаружили бы, что это не очень-то хорошо. Менеджеры окон, как правило, имеют очень специфическое поведение при перемещении или изменении размеров окон. Обычные действия: перетаскивание вверх, чтобы максимизировать, перетаскивание влево/вправо на плитку, привязка к другим окнам или панели задач, изменение размеров двух окон одновременно, если они расположены рядом друг с другом и так далее.
Справедливости ради, ранее был предоставлен один помощник - QSizeGrip. Он позволяет изменить размер любого заданного угла окна, но работает только с углами, а не с границами окна, и доступен только для приложений виджетов.
В Qt 5.15 было добавлено два новых метода в QWindow: startSystemMove и startSystemResize. Эти методы просят оконный менеджер вступить во владение и начать собственную операцию изменения размера или перемещения. Это означает, что привязка, разбиение на тайлы и так далее, должны работать просто волшебным образом, а реализация заголовка в QML становится почти однострочной:
DragHandler { onActiveChanged: if (active) window.startSystemMove(); target: null }
Поместив этот фрагмент кода в элемент QtQuick, все триггер операции перетаскивания вызовут операцию перемещения собственного окна.
startSystemResize работает аналогично, за исключением того, что он принимает аргумент Qt::Edges, который является битовым полем границ окна, которые вы захватили, то есть для нижнего правого угла, вы бы вызвали:
startSystemResize(Qt.RightEdge | Qt.BottomEdge)
Это также очень удобно, так как вы можете легко иметь один обработчик для всех четырех границ окна и просто создать аргумент границ следующим образом:
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); } }
Если вы хотите получить полный пример того, как это можно использовать, был сделан макет веб-браузера с использованием нового API.
Обратите внимание, что, хотя это кроссплатформенный API, не все платформы поддерживают его. startSystemMove в настоящее время поддерживается в Wayland, X11, macOS и Windows, в то время как startSystemResize, с другой стороны, поддерживается в Wayland, X11 и Windows, но не в macOS.
Чтобы справиться с этим, оба метода возвращают логическое значение, которое указывает, была ли операция поддержана или нет. Это означает, что если вы хотите реализовать изменение размера и в macOS, вам придется проверить возвращаемое значение startSystemResize и постараться сделать все возможное, чтобы реализовать запасной вариант в случае сбоя, т. е.:
if (!window.startSystemResize(edges)) { // your fallback code for setting window.width/height manually }
Дальнейшая работа в Qt будет заключаться в том, чтобы предоставить запасные варианты или абстракции, которые бы сделали эту работу за вас, но, по крайней мере, ничто не должно помешать вам сделать это самостоятельно.
Другая область улучшения - согласование с оконным менеджером в том, следует ли использовать оформление на стороне клиента или на стороне сервера. Некоторые приложения могут захотеть поддерживать оба режима и позволить оконному менеджеру решать, но в настоящее время это невозможно. После установки FramelessWindowHint оформления на стороне сервера не будет.
Третья область - тень окна. По крайней мере, на Wayland тень должна быть нарисована, как часть оформления окна. И хотя можно рисовать тени с помощью QtQuick, в настоящее время нет способа сообщить плагину QPA, какая часть поверхности является тенью, а какая - оконной рамкой, что означает, что, если вы попытаетесь нарисовать тени, менеджер окон в настоящее время будет рассмотрите теневую часть окна, и это испортит мозаику и привязку с другими окнами. На других платформах диспетчер окон обычно рисует тени, даже для окон, оформленных на стороне клиента, поэтому эту проблему сложно решить.