mafulechka
18 березня 2020 р. 16:01

Користувальницькі оформлення вікон на стороні клієнта Qt 5.15

Це свіжі новини про нову функцію в Qt 5.15, яка дуже порадувала.

Зазвичай оформлення вікон були досить нудною річчю. Заголовок, обмежити, мінімізувати, максимізувати, змінити розмір, вийти... і все.


Однак, останнім часом програми все частіше і частіше включають інтерфейс користувача (application specific UI) і оформлення тем. Пара скріншотів для пояснення, про що тут йдеться:

MacOS займається цим досить давно.

... і Chrome, і будь-який інший веб-браузер.

Вбудовування меню в оформлення може заощадити багато місця на екрані.

... або це може бути важливим для брендингу або дизайну.
На жаль, подібні речі раніше не були можливі з Qt.
Однак можна було прибрати оформлення з вікна, тобто:

  1. Window {
  2. flags: Qt.FramelessWindowHint
  3. }

Але це тільки залишило вас із вікном без оформлення. Таким чином, це не могло бути переміщене або змінене. Якщо б ви спробували реалізувати рух вікна або змінити розмір, схопивши мишу і вручну встановивши розмір і положення вікна, ви швидко виявили б, що це не дуже добре. Менеджери вікон зазвичай мають дуже специфічну поведінку при переміщенні або зміні розмірів вікон. Звичайні дії: перетягування вгору, щоб максимізувати, перетягування ліворуч/праворуч на плитку, прив'язка до інших вікон або панелі завдань, зміна розмірів двох вікон одночасно, якщо вони розташовані поруч один з одним і так далі.

Заради справедливості, раніше був наданий один помічник - QSizeGrip. Він дозволяє змінити розмір будь-якого заданого кута вікна, але працює тільки з кутами, а не з межами вікна, і доступний тільки для програм віджетів.

У Qt 5.15 було додано два нових методи QWindow: startSystemMove і startSystemResize. Ці методи просять віконний менеджер вступити у володіння та розпочати власну операцію зміни розміру чи переміщення. Це означає, що прив'язка, розбиття на тайли і так далі повинні працювати просто чарівним чином, а реалізація заголовка в QML стає майже однорядковою:

  1. DragHandler {
  2. onActiveChanged: if (active) window.startSystemMove();
  3. target: null
  4. }

Помістивши цей фрагмент коду елемент QtQuick, всі тригер операції перетягування викличуть операцію переміщення власного вікна.
startSystemResize працює аналогічно, за винятком того, що він приймає аргумент Qt::Edges, який є бітовим полем меж вікна, які ви захопили, тобто для нижнього правого кута, ви б викликали:

  1. startSystemResize(Qt.RightEdge | Qt.BottomEdge)

Це також дуже зручно, так як ви можете легко мати один обробник для всіх чотирьох меж вікна і просто створити аргумент кордону наступним чином:

  1. DragHandler {
  2. id: resizeHandler
  3. grabPermissions: TapHandler.TakeOverForbidden
  4. target: null
  5. onActiveChanged: if (active) {
  6. const p = resizeHandler.centroid.position;
  7. let e = 0;
  8. if (p.x < border) e |= Qt.LeftEdge;
  9. if (p.x >= width - border) e |= Qt.RightEdge;
  10. if (p.y < border) e |= Qt.TopEdge;
  11. if (p.y >= height - border) e |= Qt.BottomEdge;
  12. window.startSystemResize(e);
  13. }
  14. }

Якщо ви хочете отримати повний приклад того, як це можна використовувати, було зроблено макет браузера з використанням нового API.

Зверніть увагу, що, хоча це кроссплатформенний API, не всі платформи підтримують його. startSystemMove в даний час підтримується в Wayland, X11, macOS і Windows, а startSystemResize, з іншого боку, підтримується в Wayland, X11 і Windows, але не в macOS.

Щоб упоратися з цим, обидва методи повертають логічне значення, яке вказує, чи була операція підтримана чи ні. Це означає, що якщо ви хочете реалізувати зміну розміру і в macOS, вам доведеться перевірити значення startSystemResize, що повертається, і постаратися зробити все можливе, щоб реалізувати запасний варіант у разі збою, тобто:

  1. if (!window.startSystemResize(edges)) {
  2. // your fallback code for setting window.width/height manually
  3. }

Подальша робота в Qt буде полягати в тому, щоб надати запасні варіанти або абстракції, які зробили б цю роботу за вас, але, принаймні, ніщо не повинно завадити вам зробити це самостійно.

Інша область покращення - узгодження з віконним менеджером у тому, чи слід використовувати оформлення на стороні клієнта або сервері. Деякі програми можуть захотіти підтримувати обидва режими і дозволити віконному менеджеру вирішувати, але це неможливо. Після встановлення FramelessWindowHint оформлення на стороні сервера не буде.

Третя область – тінь вікна. Принаймні на Wayland тінь має бути намальована як частина оформлення вікна. І хоча можна малювати тіні за допомогою QtQuick, в даний час немає способу повідомити плагін QPA, яка частина поверхні є тінню, а яка - віконною рамкою, що означає, що, якщо ви спробуєте намалювати тіні, менеджер вікон в даний час буде розглянути тіньову частину вікна, і це зіпсує мозаїку та прив'язку з іншими вікнами. На інших платформах диспетчер вікон зазвичай малює тіні навіть для вікон, оформлених на стороні клієнта, тому цю проблему складно вирішити.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
  • Останні коментарі
  • Evgenii Legotckoi
    16 квітня 2025 р. 17:08
    Благодарю за отзыв. И вам желаю всяческих успехов!
  • IscanderChe
    12 квітня 2025 р. 17:12
    Добрый день. Спасибо Вам за этот проект и отдельно за ответы на форуме, которые мне очень помогли в некоммерческих пет-проектах. Профессиональным программистом я так и не стал, но узнал мно…
  • AK
    01 квітня 2025 р. 11:41
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    09 березня 2025 р. 21:02
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    09 березня 2025 р. 16:14
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…