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

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

Це свіжі новини про нову функцію в 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, яка частина поверхні є тінню, а яка - віконною рамкою, що означає, що, якщо ви спробуєте намалювати тіні, менеджер вікон в даний час буде розглянути тіньову частину вікна, і це зіпсує мозаїку та прив'язку з іншими вікнами. На інших платформах диспетчер вікон зазвичай малює тіні навіть для вікон, оформлених на стороні клієнта, тому цю проблему складно вирішити.

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up