Адаптивный дизайн приложений на Gtk
Основные рабочие среды из мира GNU/Linux — это GNOME и KDE Plasma. До недавнего времени их можно было видеть только на десктопах, но сейчас эти окружения можно заметить и на мобильных девайсах. Эти девайсы не слишком распространены. Десктопный Linux встречается гораздо чаще.
Для того чтобы внедриться в мир адаптивных интерфейсов, в сообществе KDE был создан целый фреймворк под названием Kirigami. О нем можно прочитать в этой статье.
Разработчики из сообщества GNOME Foundation пошли похожим путем, создав библиотеку виджетов libadwaita в дополнение к Gtk4. До нее там тоже были некоторые способы создания адаптивных интерфейсов. Например, при помощи библиотеки libhandy. Но речь пойдет не о них.
В этой статье я хочу рассказать о том, что привнесла в создание адаптивных интерфейсов libadwaita. Для примера я приведу код из нескольких приложений, среди которых есть приложения собственной разработки.
AdwBreakpoint
Breakpoint дословно можно перевести как «точка разрыва», «точка остановки» или «точка прерывания». Без этого компонента не будут работать большинство адаптивных макетов. Разработчик должен указать критический размер окна, при достижении которого интерфейс приложения должен измениться, подстроившись под новый размер. То, как интерфейс должен измениться, например, каким образом виджеты должны перестроиться, указывается при помощи специальных установщиков (setter).
В моем приложении Zodiac интерфейс описан при помощи XML, соответственно, и Breakpoint был встроен в программу при помощи вот этого кода:
<child>
<object class="AdwBreakpoint">
<condition>max-width: 500sp</condition>
<setter object="result_page" property="orientation">vertical</setter>
</object>
</child>
В другом моем приложении Notepad для создания интерфейса использовался язык Vala. Ввиду того, что возникла идея поменять местами список заметок и текстовую область к объекту breakpoint были подключены сигналы apply и unapply:
var breakpoint = new Adw.Breakpoint(new Adw.BreakpointCondition.length(Adw.BreakpointConditionLengthType.MAX_WIDTH, 550, Adw.LengthUnit.SP));
breakpoint.add_setters(hbox, "orientation", Gtk.Orientation.VERTICAL, hbox, "homogeneous", true, hbox, "margin-start", 5, hbox, "margin-end", 5);
breakpoint.apply.connect(()=>{
hbox.reorder_child_after(scroll, scroll_for_text);
});
breakpoint.unapply.connect(()=>{
hbox.reorder_child_after(scroll_for_text, scroll);
});
add_breakpoint(breakpoint);
Объект scroll из класса GtkScrolledWindow содержит контейнер GtkListBox со списком заметок, а объект scroll_for_text из того же класса — обычный GtkTextView для ввода и вывода содержимого заметок. При помощи метода reorder_child_after объекты поданные ему на вход меняются местами. Мне показалось, что вследствие этого приложение в портретной ориентации выглядит лучше и им будет удобнее пользоваться.
При использовании AdwBreakpoint окно не будет иметь минимального размера, поэтому важно не забыть передать окну свойства width-request и height-request. В противном случае готовьтесь увидеть в консоли вашей IDE множество предупреждений.
Макеты для адаптивного дизайна
Далее рассмотрим некоторые адаптивные макеты из libadwaita отдельно. Всем макетам, описанным в этом разделе, кроме Clamp, необходим Breakpoint для изменения их вида.
Clamp
Одним из таких макетов является Clamp. Он позволяет указать необходимые отступы для дочернего элемента и будет их придерживаться, если есть достаточно места. В противном случае отступы будут удалены. Этот макет прекрасно подходит для встраивания GtkListBox. В своих приложениях я часто его использую. Например, он использован в вышеупомянутом Zodiac и еще в другом моем приложении под названием Desktop Files Creator:
Clamp отлично подойдет, если, например, нужно отцентровать по вертикали и/или горизонтали какой-либо виджет или контейнер с группой виджетов. В качестве примера можно привести еще одно мое приложение под названием Forgetpass:
В этом приложении в Clamp упаковывается вертикальный GtkBox, в котором содержатся GtkListBox с двумя AdwEntryRow для ввода названия сайта и ключевого слова, кнопка генерации пароля и еще один GtkBox, но уже с горизонтальной ориентацией. В нем содержится поле для вывода готового пароля и кнопка для копирования пароля в буфер обмена.
ViewSwitcher
Еще один интересный макет — это ViewSwitcher. Он обычно используется вместе с ViewSwitcherBar и предоставляет удобный адаптивный интерфейс для переключения представлений в виде набора вкладок расположенного в заголовке окна. Вот так эта штука выглядит:
Когда ширина окна уменьшается до определенного значения, то панель со вкладками перемещается вниз:
Подобные макеты отлично подходят для приложений вроде музыкальных плееров. И ведь действительно: для каждой отдельной вкладки можно поставить в соответствие свою страницу со списком исполнителей, альбомов, треков и прочего.
NavigationSplitView
Следующий макет называется NavigationSplitView. Он представляет из себя две области, где может располагаться какое-нибудь содержимое. Одна область — это боковая панель (Sidebar), в которой можно разместить, например, список, а в другой области (Content) можно показывать содержимое каждого пункта из этого списка:
При достижении определенного значения ширины вся эта конструкция может схлопываться и показывать только Sidebar:
Или Content:
Такая вещь, как NavigationSplitView, отлично бы подошла для моего Notepad. Сейчас в нем используется обычный контейнер GtkBox с ориентацией, зависящей от ширины окна. Левая часть контейнера отведена для списка заметок, а правая — для содержимого заметок. Если заменить GtkBox на NavigationSplitView, то в Sidebar можно поместить список заметок, а в Content — их содержимое. Сделать это можно, например, вот так:
var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 5);
box.append(entry_search);
box.append(scroll);
var sidebar_toolbar = new Adw.ToolbarView();
sidebar_toolbar.set_top_bar_style(Adw.ToolbarStyle.RAISED);
sidebar_toolbar.add_top_bar(sidebar_headerbar);
sidebar_toolbar.set_content(box);
var content_toolbar = new Adw.ToolbarView();
content_toolbar.set_top_bar_style(Adw.ToolbarStyle.RAISED);
content_toolbar.add_top_bar(content_headerbar);
content_toolbar.set_content(scroll_for_text);
var sidebar = new Adw.NavigationPage(sidebar_toolbar, "");
var content = new Adw.NavigationPage(content_toolbar, "");
var split_view = new Adw.NavigationSplitView();
split_view.set_sidebar(sidebar);
split_view.set_content(content);
Придется разделить существующий хидербар на две части, создав таким образом два хидербара. Один будет содержать кнопки для добавления, удаления и поиска заметок и располагаться в Sidebar. Другой будет расположен в Content и содержать кнопки для сохранения заметок и главное меню. Планирую уже следующую версию приложения выпустить с данным макетом. Пока что все это находится на стадии тестирования. В репозитории приложения создана отдельная ветка next для следующей версии.
OverlaySplitView
Следующий макет — это OverlaySplitView. Он очень похож на NavigationSplitView, но отличается от него тем, что боковая панель в свернутом виде показывается над основной. Вот так выглядит макет в развернутом виде:
А так в свернутом:
Такой макет тоже бы отлично подошел для приложений наподобие Notepad. Вообще, макеты, похожие на OverlaySplitView, хорошо подходят для таких приложений, как списки задач или покупок, утилиты для настройки чего-либо, программы для составления распорядка дня, расписаний уроков и тому подобного.
Подробнее об этих и некоторых других макетах можно прочитать на этом сайте. Кроме описаний, на сайте приводятся и примеры кода. Библиотека libadwaita постоянно развивается и пополняется новыми компонентами с каждым обновлением. Можно ожидать, что в новых версиях библиотеки разработчики придумают что-нибудь новое для создания адаптивных интерфейсов.
Vapad
Это простой текстовый редактор, написанный на Vala. Я не являюсь создателем этого приложения, но принимал некоторое участие в его разработке. Хочу рассказать, как здесь организован адаптивный дизайн. Исходный код приложения находится здесь.
Разработчик решил создать автоматически скрывающуюся панель, на которой будут размещаться некоторые элементы из хидербара. Панель расположена снизу и появляется, когда окно уменьшается до определенного значения по ширине. Вот, как это выглядит в коде:
<child>
<object class="AdwBreakpoint">
<condition>max-width: 500sp</condition>
<setter object="open_button" property="visible">False</setter>
<setter object="new_tab_button" property="visible">False</setter>
<setter object="save_button" property="visible">False</setter>
<setter object="tab_bar" property="visible">False</setter>
<setter object="footer" property="visible">True</setter>
</object>
</child>
В качестве нижней панели (footer) используется GtkHeaderBar. В эту панель добавляются практически те же самые компоненты, что скрываются в заголовке при помощи Breakpoint. Но есть и отличия. Об этом чуть позже.
При достижении ширины окна вышеуказанного значения нижняя панель исчезает, а в заголовке снова появляются исчезнувшие ранее элементы. Разработчик почему-то не озаботился предоставлением скриншотов своего приложения в репозитории. Я исправлю это упущение. Вот так выглядит данное приложение в десктопном варианте:
А так в мобильном:
Как видно, элементы и их расположение на нижней и верхней панелях в разных вариантах несколько отличаются. Вместо кнопки открытия новой вкладки появилась кнопка обзора всех вкладок. Из этого режима можно открыть новую вкладку или открыть/закрыть существующие. Приложение на данный момент еще находится в стадии разработки. Есть парочка нерешенных задач. Увидеть их можно в соответствующем разделе репозитория.
Clocks
Приведу еще один пример приложения. Clocks — это стандартное приложение для среды рабочего стола GNOME. Оно представляет собой обычные часы с будильником, таймером и парочкой других наворотов. Ознакомиться с ним подробнее можно здесь, а исходный код посмотреть вот здесь.
Код для Breakpoint в этом приложении выглядит следующим образом:
<child>
<object class="AdwBreakpoint">
<condition>max-width: 600sp</condition>
<setter object="switcher_bar" property="reveal">True</setter>
<setter object="header_bar" property="title-widget"/>
</object>
</child>
Объект switcher_bar принадлежит классу AdwViewSwitcherBar и служит для переключения представлений. В десктопном режиме этот объект располагается в заголовке окна (header_bar). При уменьшении ширины окна до значения 600sp switcher_bar показывается в нижней части окна, а header_bar в свою очередь наделяется свойством title-widget, и в нем ничего кроме названия приложения и пары виджетов в начале и конце теперь не отображается.
Clocks, как и большинство приложений GNOME, отличается своей минималистичностью. В нем практически нет ничего лишнего. А с учетом наличия адаптивного интерфейса им можно пользоваться на любом устройстве, где установлена соответствующая операционная система.
Мне нравится то, в каком направлении развивается Gtk. Сейчас, когда один человек может владеть устройствами с самыми разными размерами экрана, начиная от телефона и заканчивая телевизорами, важно, чтобы любимыми приложениями было удобно пользоваться на любом из них.
Автор статьи@KAlexAl
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.