Мастерство создания виджетов в EWW: от основ до системной панели в Hyprland

Комплексный курс по разработке интерфейсов для Linux с использованием движка ElKowar's Wacky Widgets. Вы научитесь проектировать динамические виджеты, работать с синтаксисом Yuck и интегрировать панель управления с системными процессами Arch Linux.

1. Основы EWW и архитектура проекта: структура файлов и жизненный цикл виджета

Основы EWW и архитектура проекта: структура файлов и жизненный цикл виджета

Представьте, что вы решили построить приборную панель для футуристичного автомобиля, но вместо физических кнопок и стрелок у вас есть только чистый лист кода и графический сервер Wayland. В мире Linux-кастомизации ElKowar’s Wacky Widgets (EWW) занимает уникальную нишу: это не просто «рисовалка» окон, а полноценный декларативный движок, который превращает ваши идеи в интерактивные элементы интерфейса. Если обычные панели вроде Waybar или Polybar предлагают жесткую структуру, где вы лишь выбираете модули, то EWW дает вам «лего-конструктор», где вы сами определяете, как данные превращаются в пиксели.

Многие новички в Hyprland пытаются сразу копировать чужие конфиги из репозиториев на GitHub, обнаруживая там сотни строк непонятного кода. Ошибка кроется в попытке объять всё сразу. Чтобы приручить EWW, нужно сначала понять его «скелет» — то, как файлы общаются друг с другом и как виджет проходит путь от строчки в текстовом редакторе до появления на вашем мониторе.

Философия декларативного интерфейса

Прежде чем открыть терминал, необходимо осознать фундаментальный принцип EWW: он декларативен. В традиционном программировании вы пишете инструкции: «создай окно, нарисуй в нем текст, если нажали кнопку — измени текст». В EWW вы описываете конечное состояние: «это окно содержит текст, который всегда равен значению переменной ».

Этот подход радикально меняет архитектуру проекта. Вы не управляете отрисовкой напрямую. Вместо этого вы создаете структуру данных и визуальное дерево, а движок EWW берет на себя тяжелую работу по обновлению интерфейса, когда данные меняются. Это роднит EWW с современными веб-фреймворками вроде React или Vue, но адаптированными под специфику системного окружения Linux.

Для пользователя Hyprland это означает невероятную гибкость. Вы можете создать виджет, который появляется только на определенном мониторе, реагирует на смену воркспейса или меняет свой размер в зависимости от того, сколько ядер процессора сейчас загружено. Но за эту гибкость приходится платить строгой организацией файлов.

Анатомия проекта: eww.yuck и eww.scss

Проект EWW в своей базовой форме состоит из двух критически важных файлов. Если вы используете EndeavourOS, скорее всего, ваша конфигурация будет жить в директории ~/.config/eww/.

Главный файл конфигурации: eww.yuck

Файл eww.yuck — это сердце вашего интерфейса. Здесь используется язык Yuck (Yet another Useless Configuration Kanguage), который по синтаксису напоминает Lisp. Основная особенность Lisp-подобных языков — обилие круглых скобок. Каждая открывающая скобка начинает определение элемента или вызов функции, а закрывающая — завершает его.

В eww.yuck решаются три задачи:

  • Определение окон (Windows): Вы указываете физические параметры (размер, положение на экране, слой отрисовки).
  • Описание виджетов (Widgets): Вы создаете шаблоны элементов (например, «кнопка с иконкой и текстом»).
  • Управление данными (Variables): Вы объявляете переменные, которые будут хранить системную информацию.
  • Пример структуры, которую мы будем детально изучать позже:

    Стилизация: eww.scss

    Если Yuck отвечает за структуру («что именно мы видим»), то eww.scss отвечает за эстетику («как это выглядит»). EWW использует GTK для отрисовки, поэтому стилизация происходит через диалект CSS — SCSS (Sassy CSS).

    SCSS позволяет использовать переменные для цветов, вложенные селекторы и миксины. Это крайне удобно для создания тем оформления. Например, вы можете один раз определить переменную $bg-color: #1e1e2e; и использовать её во всех частях интерфейса. При смене темы вам достаточно будет изменить одну строчку.

    Важно понимать: EWW автоматически компилирует SCSS в обычный CSS при запуске. Если вы допустите ошибку в синтаксисе стилей, виджет может либо не запуститься, либо выглядеть как «голый» текст без отступов и цветов.

    Иерархия папок и дополнительные ресурсы

    Хотя для работы достаточно двух файлов, серьезный проект быстро разрастается. Профессиональная структура папок в ~/.config/eww/ обычно выглядит так:

    * eww.yuck — входная точка, импортирующая другие файлы. * eww.scss — главный файл стилей. * scripts/ — директория для Bash, Python или Lua скриптов, которые поставляют данные (например, скрипт для получения текущего трека в Spotify). * modules/ — отдельные .yuck файлы для разных частей панели (например, clock.yuck, battery.yuck), которые подключаются через (include "./modules/file.yuck"). * assets/ — иконки, изображения и шрифты.

    Такое разделение позволяет избежать «файла-монстра» на 2000 строк, в котором невозможно ничего найти. В рамках нашего курса мы будем придерживаться модульного подхода, так как это упрощает отладку.

    Жизненный цикл виджета: от кода до пикселя

    Понимание того, как EWW обрабатывает вашу конфигурацию, поможет вам избежать 90% типичных ошибок. Процесс жизни виджета можно разделить на четыре этапа: Парсинг, Инициализация переменных, Отрисовка и Цикл обновления.

    1. Парсинг и построение дерева

    Когда вы выполняете команду eww open <window_name>, демон EWW (процесс, работающий в фоне) считывает файлы конфигурации. Он проверяет синтаксис Yuck. Если вы забыли закрыть скобку, процесс прервется здесь.

    На этом этапе строится внутреннее дерево виджетов. Если ваше окно содержит box, внутри которого лежат три button, EWW создает иерархическую структуру, где свойства родителя (например, ориентация контейнера) влияют на дочерние элементы.

    2. Инициализация переменных

    После построения структуры EWW «оживляет» переменные. * Статические переменные (defvar) просто записываются в память. * Опрашиваемые переменные (defpoll) запускают свои скрипты первый раз, чтобы получить начальное значение. * Слушающие переменные (deflisten) открывают постоянный поток данных из внешнего процесса.

    Это критический момент: если ваш скрипт получения заряда батареи зависнет на старте, виджет может отобразиться с пустым полем или вообще не отрисоваться.

    3. Отрисовка через GTK и Wayland

    EWW не рисует пиксели сам — он говорит библиотеке GTK3, какие элементы нужно создать. GTK, в свою очередь, общается с композитором Hyprland через протоколы Wayland (например, layer-shell).

    Здесь вступают в силу параметры геометрии. Атрибут :stacking "fg" (foreground) заставляет Hyprland поместить окно виджета над всеми остальными окнами. Если вы укажете :exclusive true, Hyprland «подвинет» другие окна, чтобы виджет их не перекрывал (так работают классические панели задач).

    4. Цикл обновления (The Update Loop)

    Это самая интересная часть. EWW постоянно следит за состоянием переменных. Как только значение переменной меняется (например, defpoll раз в секунду обновил время), EWW находит в дереве виджетов все элементы, которые зависят от этой переменной.

    Важно отметить: перерисовывается не всё окно целиком, а только те части, которые изменились. Это делает EWW крайне эффективным с точки зрения потребления ресурсов процессора. Однако, если вы создадите 100 переменных, которые обновляются каждую миллисекунду, вы заметите нагрузку на систему. Баланс между частотой обновления и производительностью — это искусство, которое мы освоим позже.

    Взаимодействие с Hyprland: специфика Wayland

    Поскольку вы работаете в Hyprland, важно понимать роль протокола layer-shell. В отличие от старых систем на X11, в Wayland окно не может просто сказать: «я хочу быть здесь». Оно должно объявить свою роль.

    Виджеты EWW обычно регистрируются как слои (layers). Существует несколько уровней: * Background: под всеми окнами и обоями. * Bottom: над обоями, но под окнами. * Top: над обычными окнами. * Overlay: поверх всего, включая заблокированный экран (используется редко).

    Для системной панели мы будем использовать слой top или bottom. Также Hyprland позволяет управлять «прозрачностью для кликов». Вы можете сделать так, чтобы виджет был виден, но клики проходили сквозь него на рабочий стол, или наоборот — чтобы виджет перехватывал весь ввод.

    Подготовка рабочего пространства

    Чтобы начать работу эффективно, проверьте текущее состояние вашего EWW. Откройте терминал и выполните: eww daemon

    Эта команда запускает фоновый процесс. EWW работает по клиент-серверной модели. Демон держит в памяти состояние всех виджетов, а клиент (команда eww) посылает ему приказы: открыть окно, закрыть, обновить переменную.

    Если вы измените eww.yuck, вам не нужно перезапускать демон вручную в большинстве случаев — EWW умеет подхватывать изменения «на лету» (hot reload). Однако при серьезных ошибках в синтаксисе демон может упасть, и его придется перезапустить.

    Логирование — ваш лучший друг

    При разработке виджетов вы неизбежно столкнетесь с тем, что окно «просто не открывается». В 99% случаев причина написана в логах. Чтобы видеть их в реальном времени, держите открытым терминал с командой: eww logs

    Там будут отображаться ошибки парсинга Yuck, ошибки выполнения скриптов в defpoll и жалобы GTK на неправильный CSS. Без присмотра за логами разработка в EWW превращается в гадание на кофейной гуще.

    Структура данных и типы в Yuck

    Хотя Yuck кажется простым, у него есть своя типизация. Понимание типов данных поможет правильно передавать информацию из скриптов в виджеты.

  • Строки (Strings): Текст в двойных кавычках "Привет". Почти все данные из скриптов приходят в виде строк.
  • Числа (Numbers): Могут использоваться в математических выражениях внутри Yuck.
  • Булевы значения (Booleans): true или false. Используются для условий (например, показывать виджет или нет).
  • Списки и Массивы (Arrays/JSON): EWW отлично понимает JSON. Если ваш скрипт выводит [1, 2, 3], вы можете итерироваться по этому списку, создавая элементы динамически.
  • Пример того, как EWW интерпретирует данные: если вы получаете громкость звука как строку "50%", вы можете просто вывести её в label. Но если вы хотите использовать это значение для длины полоски прогресса (scale), вам может понадобиться очистить строку от символа %, чтобы превратить её в чистое число.

    Почему именно EWW?

    В экосистеме Arch Linux существует множество инструментов для создания панелей. Почему мы выбираем именно этот путь?

    * Минимализм ресурсов: Написанный на Rust, EWW потребляет ничтожно мало оперативной памяти по сравнению с решениями на Electron или даже Python (если не злоупотреблять тяжелыми скриптами). * Полный контроль: Вы не ограничены сеткой или предустановленными модулями. Хотите часы в форме круга посередине экрана? Пожалуйста. Хотите, чтобы при наведении на иконку сети выплывал график трафика за последние 24 часа? Это реализуемо. * Интеграция: Благодаря возможности выполнять любые системные команды, EWW становится центром управления вашим дистрибутивом. Он может управлять яркостью через brightnessctl, звуком через wpctl или переключать воркспейсы в Hyprland через hyprctl.

    Мы заложили фундамент. Мы знаем, где лежат файлы, как они связаны между собой и какой путь проходит код, прежде чем стать кнопкой на экране. В следующей части мы перейдем от теории к практике и напишем наше первое описание окна, разобрав синтаксис Yuck до мельчайших деталей. Подготовьте ваш любимый текстовый редактор и убедитесь, что eww daemon запущен — мы начинаем строить.

    10. Сборка финальной панели управления: интеграция компонентов в единую систему

    Сборка финальной панели управления: интеграция компонентов в единую систему

    Когда вы смотрите на идеально настроенный рабочий стол в Hyprland, вы видите не просто набор разрозненных цифр и иконок. Вы видите симфонию данных, где каждое изменение громкости, переключение воркспейса или скачок нагрузки на процессор мгновенно отражается в интерфейсе. До этого момента мы изучали отдельные инструменты: как рисовать блоки, как опрашивать систему и как слушать потоки событий. Теперь настало время превратить эти запчасти в единый механизм — полноценную системную панель.

    Интеграция — это не просто копирование кода в один файл. Это решение архитектурных конфликтов, управление производительностью и создание визуальной иерархии, которая делает интерфейс интуитивно понятным.

    Архитектура монолита против модульности

    Первый вопрос, который встает перед разработчиком при сборке финальной системы: стоит ли держать всё в одном гигантском файле eww.yuck? Для маленького виджета часов это допустимо, но для полноценной панели управления, включающей системный трей, индикаторы ресурсов, управление плеером и воркспейсы, такой подход превратит поддержку кода в кошмар.

    EWW поддерживает директиву include, которая позволяет разделять логику. Правильная структура проекта для финальной сборки выглядит так:

  • eww.yuck — главный файл, который только подключает модули и определяет финальные окна (defwindow).
  • modules/ — папка с отдельными файлами для каждого логического блока (workspaces.yuck, system.yuck, clock.yuck).
  • scripts/ — все bash/python скрипты, которые питают defpoll и deflisten.
  • scss/ — разделенные стили (colors.scss, widgets.scss, layout.scss).
  • При интеграции важно соблюдать иерархию имен. Если в модуле system.yuck вы назвали виджет metric, а в модуле audio.yuck тоже использовали metric, EWW выдаст ошибку при парсинге. Используйте префиксы: system-metric, audio-metric.

    Главный контейнер и стратегия Layout-дизайна

    Основа любой панели — это правильное распределение пространства. В большинстве современных систем используется схема «Лево — Центр — Право». Чтобы реализовать это в EWW, мы используем корневой виджет box с атрибутом :space-evenly false.

    Почему false? Если оставить значение по умолчанию (true), EWW заставит левый, центральный и правый блоки занимать ровно по 33% ширины. Это выглядит плохо: часы в центре будут иметь огромные пустые поля, а системные индикаторы справа могут не влезть.

    Правильная стратегия интеграции выглядит так:

  • Создаем внешний box с ориентацией horizontal.
  • Внутри него создаем три дочерних box для левой, центральной и правой частей.
  • Используем атрибут :halign со значениями "start", "center" и "end" соответственно.
  • Для левого и правого блоков устанавливаем :hexpand true, чтобы они «расталкивали» центральный блок ровно по середине экрана.
  • В этой схеме center-modules всегда будет строго по центру монитора, независимо от того, сколько иконок добавилось в системный трей справа. Это критически важно для визуального баланса в Hyprland.

    Синхронизация состояний: когда данные пересекаются

    При сборке финальной панели вы неизбежно столкнетесь с тем, что разные виджеты хотят знать об одном и том же. Например, кнопка «Mute» в аудио-виджете должна менять цвет, а виджет уведомлений должен знать, включен ли режим «Не беспокоить».

    Плохая практика — запускать два разных defpoll, которые делают одно и то же. Это лишняя нагрузка на CPU. Хорошая практика — создание «единого источника истины» (Single Source of Truth). Если у вас есть сложный скрипт на Python или Bash, который собирает информацию о системе, пусть он выводит один большой JSON-объект через deflisten.

    Представьте переменную (deflisten global_state :initial "{}" "scripts/sys_monitor.sh"). Внутри этого JSON могут быть поля:

  • global_state.cpu.usage
  • global_state.audio.volume
  • global_state.network.ssid
  • Интегрируя компоненты, вы просто передаете части этого объекта в соответствующие виджеты. Это гарантирует, что данные на всей панели обновляются одновременно. Нет ничего хуже, чем когда часы в одном углу панели спешат на секунду относительно часов в другом меню из-за разных интервалов опроса.

    Визуальная целостность через SCSS

    На этапе сборки часто выясняется, что виджеты, написанные в разное время, выглядят как «лоскутное одеяло». Один имеет скругление углов 10px, другой — 12px, у одного шрифт чуть светлее, у другого — темнее.

    Для интеграции необходимо внедрить систему дизайн-токенов в SCSS. Вместо того чтобы писать background: #282c34 в каждом виджете, мы создаем файл _variables.scss:

    При интеграции виджетов используйте миксины для типичных элементов. Например, «капсула» (скругленный контейнер для модуля) должна быть одинаковой везде:

    Управление системными событиями через шину DBus

    При интеграции управления яркостью, громкостью или Bluetooth, прямое выполнение команд вроде amixer или brightnessctl — это базовый уровень. Для «мастерской» панели лучше использовать DBus.

    Почему это важно для интеграции? DBus позволяет не только отдавать команды, но и получать подтверждения или узнавать об изменениях, сделанных другими программами. Если вы измените громкость кнопками на клавиатуре, ваша панель в EWW должна мгновенно обновиться. Это достигается через deflisten, который слушает сигналы DBus.

    Например, для интеграции управления плеером (Spotify, VLC) используйте playerctl --follow metadata. Это превратит ваш виджет в живой пульт управления, который меняет обложку альбома и название трека без единой секунды задержки.

    Финальная проверка: логика взаимодействия

    Перед тем как считать сборку завершенной, проверьте «граничные случаи»:

  • Что произойдет при отключении монитора? Если ваше окно привязано к конкретному ID монитора, оно может исчезнуть. Используйте относительную адресацию или скрипты для пересоздания окон при изменении конфигурации мониторов.
  • Как панель ведет себя в полноэкранном режиме? Параметр :exclusive true заставляет приложения «уважать» место панели, но в играх это может мешать. Настройте горячую клавишу в Hyprland для временного отключения эксклюзивности или скрытия панели.
  • Обработка ошибок: Если скрипт в defpoll вернул ошибку, виджет не должен «разваливаться». Используйте значения по умолчанию в интерполяции: ${data.value ?: "N/A"}.
  • Сборка финальной панели — это процесс итеративного улучшения. Вы начнете с простой полоски, но по мере интеграции новых функций поймете, где не хватает отступов, а где данные обновляются слишком медленно. Главное преимущество EWW здесь в том, что вам не нужно перекомпилировать код. Каждое сохранение eww.yuck мгновенно обновляет интерфейс, позволяя вам видеть результат интеграции в реальном времени.

    Ваша панель теперь — это не просто украшение, а полноценный дашборд, который экономит время и делает работу в Hyprland максимально эффективной. Вы прошли путь от создания первой надписи "Hello World" до сложной системы, взаимодействующей с ядром Linux и протоколами Wayland.

    2. Синтаксис Yuck и декларативное описание первого окна

    Синтаксис Yuck и декларативное описание первого окна

    Почему опытные разработчики интерфейсов для Linux часто выбирают инструменты, которые на первый взгляд кажутся сложными? Представьте, что вместо того чтобы вручную прописывать каждый пиксель перемещения кнопки, вы просто описываете состояние системы: «Если музыка играет, покажи обложку альбома; если нет — покажи часы». В EWW за эту магию отвечает Yuck — язык, который может показаться странным из-за обилия скобок, но именно он позволяет строить интерфейсы, которые не «ломаются» при изменении размеров экрана или обновлении данных.

    Философия S-выражений и почему Yuck выглядит именно так

    Если вы когда-нибудь видели код на языке Lisp или Clojure, синтаксис Yuck покажется вам родным. Для всех остальных обилие круглых скобок может вызвать легкое замешательство. Однако за этим стоит строгая логика. Yuck использует так называемые S-выражения (Symbolic Expressions).

    В обычном HTML мы пишем <tag attribute="value">Content</tag>. В Yuck та же структура выглядит так: (tag :attribute "value" "Content").

    Такой подход выбран не случайно. S-выражения позволяют интерпретатору EWW мгновенно строить дерево виджетов. Каждая открывающая скобка — это начало нового узла в иерархии, а закрывающая — его завершение. Это исключает двусмысленность: вы всегда точно знаете, какой виджет вложен в какой.

    Основные правила синтаксиса Yuck:

  • Ключевые слова (атрибуты) всегда начинаются с двоеточия, например :orientation или :text.
  • Строки всегда заключаются в двойные кавычки ".
  • Комментарии начинаются с точки с запятой ;. Все, что идет после нее до конца строки, игнорируется демоном EWW.
  • Вложенность определяется исключительно скобками, а не отступами (хотя отступы критически важны для чтения кода человеком).
  • Анатомия файла eww.yuck

    Любой проект на EWW начинается с файла eww.yuck. Это «головной мозг» вашего интерфейса. Здесь происходит три ключевых действия:

  • Объявление окон (defwindow).
  • Определение пользовательских виджетов (defwidget).
  • Создание переменных (defpoll, deflisten, defvar).
  • Важно понимать разницу между окном и виджетом. Окно — это сущность, с которой взаимодействует ваш оконный менеджер (в нашем случае Hyprland). Оно имеет координаты, размер и слой. Виджет — это содержимое окна. Вы не можете просто «открыть виджет», вы открываете окно, внутри которого отрисовывается дерево виджетов.

    Рассмотрим минимально жизнеспособный пример конфигурации:

    В этом блоке мы создали окно с именем my-first-window. Разберем параметры подробнее:

  • :monitor 0 — указывает, на каком мониторе появится окно. Если у вас один монитор, это всегда 0.
  • :geometry — сложный атрибут, принимающий функцию geometry. Здесь мы задаем положение и размер.
  • :anchor "top center" — привязка окна. В данном случае оно будет «приклеено» к верхней части экрана по центру.
  • :stacking "fg" — определяет слой. fg (foreground) означает, что окно будет поверх обычных приложений.
  • :exclusive true — важнейший параметр для Hyprland. Он заставляет оконный менеджер «подвинуться», резервируя место под ваше окно, чтобы другие окна не перекрывали его (аналог поведения классических панелей задач).
  • Декларативность против императивности

    Главная сложность для новичков — понять декларативную природу Yuck. В императивном программировании (например, в Python или JavaScript) вы говорите системе: «Когда я нажму кнопку, измени текст метки на "Нажато"».

    В Yuck вы описываете состояние. Вы говорите: «Текст этой метки ВСЕГДА равен значению переменной status». А затем в другом месте вы просто меняете значение status. EWW сам увидит изменение и перерисует нужную часть интерфейса.

    Это избавляет от целого класса ошибок, связанных с тем, что интерфейс «забыл» обновиться. Если данные изменились в источнике, они изменятся и на экране. Это и есть декларативный подход: описание того, что должно быть на экране, а не того, как это туда поместить.

    Работа с компонентом geometry

    Геометрия в EWW — это не просто набор цифр. Это гибкая система, которая должна учитывать разные разрешения экранов. В Hyprland это особенно актуально, так как вы можете динамически менять мониторы.

    При использовании (geometry ...) вы можете указывать значения в пикселях (px) или процентах (%).

  • Использование процентов позволяет создавать адаптивные интерфейсы. Например, :width "100%" растянет вашу панель на всю ширину экрана.
  • Смещение :x и :y работает относительно точки привязки (anchor). Если анкор стоит в top right, то положительное значение :x сдвинет окно влево (внутрь экрана).
  • Пример сложной геометрии для боковой панели:

    Это создаст панель у левого края, отступив 20 пикселей от границы, высотой почти во весь экран.

    Создание собственных виджетов через defwidget

    Писать весь код внутри defwindow — плохая практика. Как только ваш интерфейс станет сложнее «Hello World», файл превратится в нечитаемую кашу. Для переиспользования кода и структурирования используется defwidget.

    Представьте, что вы хотите создать индикатор ресурса (например, CPU или RAM), который состоит из иконки и текста. Вместо того чтобы копировать этот код дважды, вы создаете шаблон:

    Здесь metric — это имя вашего виджета, а [label value] — это аргументы, которые он принимает. Теперь внутри окна вы можете вызвать его сколько угодно раз:

    Это делает ваш код модульным. Если вы решите изменить стиль всех метрик (например, поменять box на вертикальный), вам нужно будет изменить код только в одном месте — в определении defwidget.

    Типизация и выражения внутри Yuck

    Несмотря на простоту, Yuck поддерживает базовые логические операции и интерполяцию строк. Это позволяет делать виджеты «умными» прямо в конфигурации.

    Интерполяция строк выполняется с помощью фигурных скобок и кавычки: {cpu_usage}%")

    Внутри этих скобок можно использовать тернарные операторы для условного отображения: (label :text {battery_level < 20 ? "Low Battery!" : "Battery OK"})

    Важно: Yuck — это не полноценный язык программирования. Если вам нужна сложная математика или обработка массивов, это лучше вынести в отдельный bash или python скрипт, а результат передать в EWW. Yuck должен отвечать только за отображение.

    Взаимодействие с Hyprland: специфические атрибуты

    Работая в EndeavourOS с Hyprland, вы должны учитывать, как Wayland обрабатывает окна. EWW предоставляет несколько специфических параметров для defwindow, которые критичны для корректной работы:

  • :focusable: Если вы создаете панель поиска или виджет с вводом текста, установите true. Для обычных информационных панелей лучше оставить false, чтобы окно не «перехватывало» фокус с ваших рабочих приложений.
  • :stacking:
  • - "bg" (background) — под всеми окнами (удобно для виджетов рабочего стола). - "fg" (foreground) — обычное окно. - "overlay" — поверх всего, даже поверх полноэкранных окон (идеально для уведомлений или системных меню).
  • :namespace: Позволяет задать имя окна для протокола layer-shell. Это полезно, если вы хотите написать специфические правила в hyprland.conf (например, размытие фона за виджетом).
  • Пример окна с размытием в Hyprland: В eww.yuck:

    В hyprland.conf:

    Отладка синтаксических ошибок

    Поскольку Yuck чувствителен к закрывающим скобкам, ошибки неизбежны. Главный инструмент разработчика — это вывод демона. Если вы написали код и окно не открывается, выполните в терминале: eww logs

    EWW довольно подробно описывает, где именно он встретил проблему. Например: Error: Unexpected token ')' at line 24, column 5 Это прямой сигнал к тому, что вы либо забыли закрыть скобку выше, либо поставили лишнюю.

    Еще один полезный прием: использование команды eww inspect. Она открывает графическое окно, позволяющее увидеть дерево виджетов «вживую», проверить их текущие размеры и значения переменных. Это аналог DevTools в браузере.

    Структура SCSS и связь с Yuck

    Хотя мы подробно разберем стилизацию позже, важно понимать, как Yuck связывается с CSS. У каждого виджета есть атрибут :class. (label :class "clock-label" :text "12:00")

    В файле eww.scss вы обращаетесь к этому классу:

    EWW автоматически подхватывает изменения в SCSS файле без перезагрузки демона. Это позволяет настраивать внешний вид буквально «на лету». Если вы допустили ошибку в синтаксисе SCSS, EWW также сообщит об этом в логах, но при этом продолжит работу со старыми стилями, не «роняя» всю панель.

    Поток данных: от переменной к экрану

    Чтобы ваше окно не было статичной картинкой, оно должно получать данные. В Yuck это реализуется через переменные. На данном этапе нам важно понять, где они объявляются.

    В этом примере defpoll каждые 1 секунду выполняет системную команду date. Результат записывается в переменную time. Поскольку в label мы указали :text time, EWW автоматически обновляет текст каждую секунду.

    Это фундаментальный принцип:

  • Скрипт/команда дает данные.
  • Переменная хранит данные.
  • Виджет отображает переменную.
  • Особенности работы с кавычками и экранированием

    Одной из частых проблем в Yuck является передача сложных строк в системные команды. Если вам нужно выполнить команду, содержащую кавычки, внутри defpoll, используйте обратную косую черту или одинарные кавычки.

    Пример: "echo \"Hello\"" или "echo 'Hello'"

    Если вы используете интерполяцию ${}, помните, что внутри нее находится выражение на языке Yuck. Если вы хотите вывести просто знак доллара, его нужно экранировать или выносить за пределы интерполяции.

    Резюме структуры первого окна

    Подводя итог, создание вашего первого окна в EWW выглядит как сборка конструктора:

  • Вы определяете окно (defwindow), задавая ему «прописку» на экране через geometry.
  • Вы определяете структуру внутри окна, используя контейнеры (например, box).
  • Вы наполняете структуру контентом (виджеты label, button и т.д.).
  • Вы связываете контент с динамикой через переменные.
  • Вы придаете всему этому вид через классы в SCSS.
  • Такая жесткая сепарация (структура в Yuck, стиль в SCSS, логика в скриптах) позволяет создавать профессиональные интерфейсы, которые легко поддерживать. В следующих главах мы перейдем от общих концепций к детальному изучению каждого «кирпичика» — базовых виджетов, которые превратят ваш пустой прямоугольник в функциональную панель управления.

    3. Базовые виджеты: работа с текстом (label), контейнерами (box) и кнопками (button)

    Базовые виджеты: работа с текстом (label), контейнерами (box) и кнопками (button)

    Если вы когда-нибудь пытались собрать интерфейс на чистом GTK или Qt, вы знаете, что за простым отображением текста часто скрываются сотни строк инициализации. В EWW всё иначе: здесь интерфейс собирается как конструктор LEGO. Однако, чтобы собрать не просто «кучу деталей», а функциональную панель для Hyprland, нужно понимать анатомию трех китов, на которых держится любой виджет: label (текст), box (контейнер) и button (интерактивность). Именно эти элементы составляют 90% любого современного интерфейса, от системного трея до сложного дашборда управления умным домом.

    Контейнер box: фундамент и логика размещения

    Виджет box — это самый важный элемент в иерархии EWW. В мире веб-разработки его ближайшим аналогом является Flexbox. По умолчанию в EWW ни один виджет не может существовать сам по себе внутри окна, если их больше одного. Вам всегда нужен «родитель», который скажет элементам, как им встать: в ряд или в колонку.

    Основная задача box — управление пространством. Без него ваши кнопки и текстовые поля будут накладываться друг на друга или вести себя непредсказуемо. Рассмотрим ключевые атрибуты, которые определяют поведение контейнера.

    Ориентация и выравнивание

    Атрибут :orientation принимает два значения: "horizontal" (по умолчанию) и "vertical". Это база, определяющая направление главной оси. Однако магия начинается при использовании :space-evenly и :spacing.

  • :space-evenly: Если установлено значение true, EWW распределит свободное место так, чтобы все дочерние элементы имели одинаковый размер и равные отступы между собой. Это полезно для создания кнопок в панели задач. Если false, элементы будут занимать ровно столько места, сколько им нужно (согласно их контенту).
  • :spacing: Определяет фиксированное расстояние в пикселях между элементами внутри контейнера.
  • :halign и :valign: Горизонтальное и вертикальное выравнивание. Они принимают значения "fill", "center", "start", "end".
  • Представьте, что вы создаете блок системной информации. Вам нужно, чтобы иконка и текст стояли рядом, но при этом весь блок находился строго по центру отведенного ему места.

    В этом примере box выступает в роли невидимой оболочки. Если мы изменим :orientation на "vertical", проценты окажутся под иконкой. Важно понимать, что box может содержать в себе другие box. Это называется вложенностью, и именно так создаются сложные интерфейсы. Например, общая панель — это горизонтальный box, внутри которого лежат три других box: левый (меню), центральный (часы) и правый (системный трей).

    Управление размером через hexpand и vexpand

    Часто новички сталкиваются с проблемой: виджет «схлопывается» или, наоборот, не хочет растягиваться на всю ширину окна. Для решения этой задачи используются логические атрибуты :hexpand и :vexpand.

    Если вы установите :hexpand true, виджет попытается занять всё доступное пространство по горизонтали, которое «отдает» ему родительский контейнер. Если таких виджетов несколько, они разделят свободное место поровну. Это критично при создании разделителей или когда вы хотите, чтобы поле ввода текста занимало всю оставшуюся часть окна, отодвигая кнопки к краям.

    Виджет label: больше чем просто текст

    label — это основной способ вывода информации. На первый взгляд он кажется примитивным, но в EWW он обладает мощными инструментами форматирования и оптимизации.

    Интерполяция и динамика

    Главная особенность label в том, что его содержимое почти никогда не бывает статичным. Мы используем синтаксис {cpu_usage}%") yuck (label :markup "<span foreground='#ff5555'>Внимание:</span> Система перегрета") yuck (button :onclick "hyprctl dispatch exec kitty" (label :text "Открыть терминал")) yuck (defwidget volume-control [] (box :orientation "horizontal" :class "volume-box" :spacing 8 :space-evenly false (button :onclick "pamixer -t" :class "volume-button" (label :text "󰕾")) (label :text "{color};" :class "info-icon") (label :text value :class "info-text")))

    ;; Использование: (info-block :icon "" :value "24%" :color "#88c0d0") (info-block :icon "" :value "2.4GB" :color "#a3be8c") yuck (defwidget bar-layout [] (box :orientation "h" :class "main-bar" (box :halign "start" :space-evenly false (button :onclick "rofi -show drun" "󰣇") (workspaces-widget))

    (box :halign "center" (time-widget))

    (box :halign "end" :space-evenly false (system-tray) (power-menu-button)))) yuck ;; eww.yuck (button :class "power-btn" :onclick "shutdown now" "⏻") scss // eww.scss .power-btn { background-color: #bf616a; color: white; border-radius: 8px; padding: 4px 12px; &:hover { background-color: #d08770; } } `

    Такое разделение ответственности делает ваш проект поддерживаемым. Логика (что происходит при клике) остается в Yuck, а эстетика (как это выглядит) уходит в SCSS.

    Иерархия и наследование свойств

    Важно помнить, что некоторые свойства в EWW наследуются. Если вы зададите определенный шрифт для внешнего box, все label внутри него унаследуют этот шрифт, если у них не определен свой собственный. Это позволяет избегать дублирования кода.

    Однако такие атрибуты, как :halign или :hexpand, не наследуются. Они относятся к конкретному экземпляру виджета и определяют, как он ведет себя по отношению к своему родителю. Если вы хотите, чтобы все кнопки в ряду растягивались, вам придется прописать :hexpand true для каждой или использовать :space-evenly true у родительского box.

    Изучение label, box и button — это как изучение букв алфавита. По отдельности они просты, но их комбинации позволяют создавать бесконечно сложные и красивые интерфейсы. Понимая, как box управляет пространством, как label форматирует данные и как button` инициирует действия, вы закладываете фундамент для создания полноценной системной панели, которая будет выглядеть и работать именно так, как нужно вам.

    4. Геометрия и позиционирование окон в окружении Hyprland

    Геометрия и позиционирование окон в окружении Hyprland

    Почему ваше идеально сверстанное окно в EWW иногда перекрывает важные элементы интерфейса, а в других случаях — внезапно «схлопывается» или улетает за границы монитора? В среде Wayland, и особенно в динамическом тайлинговом менеджере Hyprland, управление окнами подчиняется строгому протоколу layer-shell. Здесь окно — это не просто прямоугольник с координатами, а активный участник композиции рабочего стола, который должен уметь договариваться с другими окнами за свободные пиксели.

    Математика экранного пространства: блок geometry

    В EWW описание физического воплощения виджета начинается с блока :geometry. На первый взгляд он кажется простым набором координат, но в Wayland-окружении его интерпретация зависит от выбранного «якоря» (anchor).

    Рассмотрим структуру блока:

    Здесь мы видим четыре ключевых параметра: `, , и . Важно понимать, что значения могут быть как абсолютными (в пикселях, px), так и относительными (в процентах, %).

    Относительные и абсолютные величины

    Использование процентов — это залог адаптивности. Если вы планируете делиться своей конфигурацией или используете несколько мониторов с разным разрешением, жесткая привязка к пикселям создаст проблемы.

  • Проценты (%): Рассчитываются относительно текущего разрешения монитора, указанного в :monitor. Если разрешение экрана , то :width "50%" превратится в пикселей.
  • Пиксели (px): Фиксированный размер. Идеален для высоты панелей или ширины боковых меню, где контент имеет предсказуемый размер.
  • Магия параметра anchor

    Атрибут :anchor определяет «точку отсчета» для координат и . Если вы указываете :anchor "top right", то точка будет находиться в верхнем правом углу экрана. При этом положительное значение будет сдвигать окно влево (внутрь экрана), а вниз.

    Доступные значения для анкоров: top, bottom, left, right, center. Их можно комбинировать, например:

  • "top left" — классика для статус-баров.
  • "center center" — для модальных окон или дашбордов по центру.
  • "bottom center" — для док-панелей в стиле macOS.
  • > Важный нюанс: В EWW координаты и всегда отсчитываются от края, указанного в anchor, по направлению к центру экрана. Если у вас стоит "bottom right", то отодвинет виджет от правой границы на 20 пикселей влево.

    Взаимодействие с Hyprland через Layer Shell

    Hyprland использует протокол layer-shell, который делит экранное пространство на слои. В отличие от X11, где окна просто лежат друг над другом, слои в Wayland определяют логику взаимодействия. В EWW за это отвечает параметр :stacking.

    Уровни наслоения (Stacking)

  • fg (foreground): Окно отрисовывается на верхнем слое. Оно будет перекрывать обычные окна приложений. Это стандарт для панелей и виджетов, которые должны быть всегда на виду.
  • bg (background): Окно уходит под все остальные окна. Идеально для создания анимированных обоев или виджетов, которые «встроены» в рабочий стол.
  • overlay: Самый верхний слой. Используется для уведомлений или меню, которые должны перекрывать даже другие системные панели.
  • bottom: Слой выше фонового, но ниже обычных окон.
  • Эксклюзивные зоны и резервирование места

    Параметр :exclusive — это то, что отличает «просто окно» от «системной панели».

  • Если :exclusive true, Hyprland вычисляет размер вашего виджета и «откусывает» это пространство от рабочей области. Окна приложений при раскрытии на весь экран не будут заходить под вашу панель или перекрывать её. Они будут упираться в её границу.
  • Если :exclusive false (по умолчанию), виджет будет просто «парить» над или под окнами. Это полезно для плавающих виджетов мониторинга, которые не должны мешать расположению рабочих окон.
  • Рассмотрим расчет рабочей области (Work Area) в Hyprland при наличии панели: Пусть разрешение монитора . Если сверху прикреплена панель с :height "40px" и :exclusive true, то доступная высота для окон приложений станет:

    При этом Hyprland автоматически применит отступы (gaps), если они настроены в hyprland.conf.

    Тонкая настройка геометрии: отступы и выравнивание

    Внутри самого окна (в блоке defwidget) мы управляем расположением элементов с помощью атрибутов контейнера box. Часто возникает путаница между тем, как позиционируется само окно на экране, и тем, как элементы располагаются внутри него.

    Центрирование внутри окна

    Представим, что вы создаете центральный виджет для даты и времени. Ваше окно занимает 100% ширины экрана, но текст должен быть строго посередине.

    Здесь критически важны атрибуты :halign (horizontal alignment) и :valign (vertical alignment).

  • start: прижать к левому краю (для горизонтального бокса).
  • end: прижать к правому краю.
  • center: выровнять по центру.
  • fill: растянуть элемент на всё доступное пространство.
  • Если вы хотите, чтобы центральный блок был идеально отцентрирован относительно экрана, а не относительно пустого места между левым и правым блоками, используйте структуру с тремя равными долями через :space-evenly true.

    Работа с несколькими мониторами

    Hyprland нумерует мониторы начиная с 0, но эти индексы могут меняться при переподключении кабелей. В параметре :monitor в EWW можно указывать:

  • Индекс (например, 0, 1).
  • Имя выхода (например, "DP-1", "HDMI-A-1"). Имя можно узнать через команду hyprctl monitors.
  • Динамическое позиционирование: Если вы хотите, чтобы одна и та же конфигурация панели открывалась на том мониторе, где сейчас находится курсор, в EWW нет прямого параметра «активный монитор» в defwindow. Однако это решается через внешние скрипты и передачу переменной в команду открытия: eww open bar --arg monitor_id=0 (при условии использования динамических аргументов, что мы затронем в будущих главах).

    Проблемы "схлопывания" и фиксированные размеры

    Частая ошибка новичков: окно создано, но оно выглядит как маленькая точка или узкая полоска, хотя в :geometry указано :width "200px".

    Это происходит потому, что EWW — это GTK-приложение под капотом. Размер окна часто определяется его содержимым (content-driven sizing). Если внутри окна пустой box, GTK может попытаться сжать его до минимально возможного размера.

    Чтобы гарантировать размер:

  • Используйте :width и :height в :geometry.
  • Внутри виджетов используйте атрибуты :hexpand и :vexpand.
  • Задавайте min-width и min-height в SCSS-файле для соответствующих классов.
  • Пример принудительного расширения:

    Атрибут :hexpand true заставляет виджет «толкать» границы своего родительского контейнера, пока тот не заполнит всё доступное место, выделенное в :geometry.

    Специфические настройки Hyprland для EWW

    Поскольку мы работаем в Hyprland, мы можем управлять поведением окон EWW прямо из основного конфига hyprland.conf. Это дополняет возможности Yuck.

    Размытие (Blur) и прозрачность

    Чтобы ваша панель имела эффект матового стекла, недостаточно просто задать прозрачный фон в CSS. Нужно сообщить Hyprland, что это окно подлежит обработке шейдером размытия. Для этого используется namespace.

    В eww.yuck:

    В hyprland.conf:

    Здесь ignorealpha говорит Hyprland не размывать области, которые прозрачны более чем на 50%. Это позволяет сохранить четкость текста на размытом фоне.

    Анимации появления

    Hyprland позволяет анимировать слои. Вы можете сделать так, чтобы ваша панель плавно выезжала сверху при запуске:

    Это создает бесшовный опыт взаимодействия, превращая набор виджетов в полноценную часть операционной системы.

    Позиционирование всплывающих окон (Popups)

    Создание всплывающего меню громкости или календаря требует иного подхода к геометрии. Такое окно не должно быть :exclusive true, иначе при его открытии все остальные окна на экране будут дергаться и менять размер.

    Типичная конфигурация для всплывающего окна:

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

    > Лайфхак: Чтобы идеально вычислить координаты для всплывающего окна, нужно учитывать ширину самого окна. Если ваша панель имеет отступ от края экрана в 10 пикселей, и вы хотите, чтобы правый край календаря совпадал с правым краем панели, убедитесь, что :anchor у обоих окон совпадает (например, "top right").

    Взаимодействие геометрии и CSS

    Иногда параметры :geometry в Yuck конфликтуют с правилами в SCSS. Важно помнить приоритеты:

  • GTK Constraints: Если контент (например, очень длинная строка текста в label) не влезает в указанную ширину :width "100px", окно расширится вопреки настройкам геометрии, чтобы вместить текст.
  • SCSS margin: Отступы в CSS (margin) увеличивают фактическое занимаемое место окна в layer-shell. Если у вас панель высотой 40px и margin: 5px, Hyprland зарезервирует под неё 50px (40 + 5 сверху + 5 снизу), если :exclusive включен.
  • SCSS padding: Влияет только на внутреннее расположение элементов и не меняет внешние границы окна, если размеры жестко заданы.
  • Математически это выглядит так. Полная высота, занимаемая окном в системе ():

    Если вы хотите создать «парящую» панель (которая не касается краев монитора), лучше всего использовать margin в CSS в сочетании с :exclusive true. Тогда окна приложений будут аккуратно останавливаться, не доходя до вашей панели те самые 5-10 пикселей, создавая эффект чистоты и порядка.

    Особенности работы с мониторами разной плотности (HiDPI)

    Wayland и Hyprland отлично справляются с масштабированием, но это накладывает отпечаток на расчеты в пикселях. Если у вас стоит масштаб (scale) 2.0 в настройках монитора, то указанные в EWW :height "40px" на самом деле будут занимать 80 физических пикселей матрицы.

    Однако для вас, как для разработчика виджетов, это прозрачно: вы продолжаете оперировать «логическими пикселями». Единственная проблема может возникнуть с растровыми иконками (PNG) — они могут выглядеть размытыми. Поэтому для элементов интерфейса в EWW настоятельно рекомендуется использовать шрифтовые иконки (Nerd Fonts, Font Awesome) или SVG, которые масштабируются математически без потери качества.

    Сценарии сложного позиционирования

    Сценарий 1: Боковая панель (Side Bar)

    Для вертикальной панели настройки будут следующими:
  • :geometry с :width "50px", :height "100%".
  • :anchor "left center".
  • :orientation "v" для корневого бокса.
  • Hyprland при этом будет сдвигать окна вправо, освобождая всю левую сторону экрана.

    Сценарий 2: Игровой оверлей (HUD)

    Если вам нужен индикатор FPS или загрузки CPU, который висит поверх игр:
  • :stacking "overlay".
  • :exclusive false.
  • :focusable false (чтобы клики проходили сквозь виджет или не забирали фокус у игры).
  • Координаты в процентах, например :x "2%" :y "2%"`, чтобы на любом разрешении индикатор был в углу.
  • Правильное понимание геометрии в EWW избавляет от 90% багов верстки. Помните, что окно в Hyprland — это живой объект, и его финальный вид — это результат компромисса между вашим Yuck-файлом, CSS-стилями и правилами Layer Shell самого оконного менеджера.

    5. Стилизация интерфейса: применение SCSS для кастомизации внешнего вида

    Стилизация интерфейса: применение SCSS для кастомизации внешнего вида

    Почему один виджет выглядит как системная утилита из 90-х, а другой — как футуристичный интерфейс из научно-фантастического фильма? Разница кроется не в логике работы или количестве строк кода в eww.yuck, а в глубине проработки стилей. В мире EWW (ElKowar's Wacky Widgets) за визуальную эстетику отвечает SCSS — мощный препроцессор, который превращает сухие правила CSS в гибкую систему дизайна.

    Если вы когда-нибудь пытались стилизовать сложные интерфейсы на чистом CSS, вы знаете, как быстро файл превращается в нечитаемое полотно. SCSS решает эту проблему, привнося в мир верстки логику программирования: переменные, вложенность и математические операции. В контексте Hyprland и Wayland, где важна каждая деталь — от тени под панелью до плавности скругления углов — владение SCSS становится критическим навыком.

    Мост между структурой и визуалом

    Прежде чем погрузиться в синтаксис, важно понять, как именно EWW связывает декларативное описание виджета с его внешним видом. В файле eww.yuck мы определяем иерархию компонентов, используя атрибут :class. Это единственный и основной способ сообщить движку, какие стили применить к конкретному элементу.

    В отличие от веб-разработки, где часто используются идентификаторы (ID), в EWW работа строится преимущественно на классах. Это продиктовано философией переиспользуемости: мы создаем один defwidget (например, для кнопки индикатора) и применяем к нему класс, который может наследоваться или изменяться в зависимости от контекста.

    Когда вы запускаете EWW, демон считывает файл eww.scss, компилирует его в стандартный CSS и применяет к GTK-объектам, из которых состоят виджеты. Важно помнить, что EWW использует GTK3 под капотом, а значит, мы ограничены подмножеством CSS, которое поддерживает эта библиотека. Здесь не работают некоторые современные свойства вроде grid (в привычном понимании CSS Grid Layout) или сложные 3D-трансформации, но база — цвета, отступы, шрифты и тени — доступна в полном объеме.

    Архитектура SCSS: переменные и палитра

    Первое правило хорошего дизайна — консистентность. Цвета не должны браться «из головы» для каждого отдельного виджета. В SCSS мы начинаем с создания глобальной палитры. Это позволяет изменить облик всей панели, поменяв всего одну строку кода.

    В начале вашего eww.scss всегда должен располагаться блок переменных. В SCSS переменная объявляется через символ bg-color: #1e1e2e; accent-color: #89b4fa; warning-color: #fab387;

    // Параметры геометрии padding-small: 5px; wallpaper-primary; } yuck (defwidget sys-tray [] (box :class "tray-container" (label :class "tray-icon" :text "󰂚") (button :class "tray-button" :onclick "notify-send 'Hello'" "Click me") ) ) scss .tray-container { background-color: border-radius; padding: accent-color; font-size: 1.2rem; margin-right: fg-color;

    &:hover { background-color: rgba(accent-color; } } } scss .icon { font-family: "JetBrainsMono Nerd Font"; font-size: 16px; } scss .panel-bg { background-color: rgba(30, 30, 46, 0.8); // 80% непрозрачности } scss side-module-width: side-module-width; } scss .button { background-color: accent-color, 10%); }

    &:active { background-color: darken(color) { background-color: rgba(color; border: 1px solid accent-color); } .status-offline { @include status-indicator(accent-color, border-radius; } yuck (defwidget battery-icon [capacity] (label :class {capacity < 20 ? "bat-low" : "bat-normal"} :text {capacity < 20 ? "󰂃" : "󰁹"}) ) scss .bat-normal { color: critical-color; animation: blink 1s infinite; // Да, GTK поддерживает простые анимации! } scss @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } }

    .recording-dot { background-color: vol-bg: #313244; bg-color; padding: 4px 12px; border-radius: 20px;

    .volume-icon { font-size: 18px; color: vol-bg; border-radius: 5px; min-width: 80px; min-height: 8px;

    highlight { background-color: $vol-accent; border-radius: 5px; } }

    // Скрываем стандартный ползунок (slider), если хотим плоский дизайн slider { min-height: 0; min-width: 0; background-color: transparent; } } } scss @import "variables"; @import "mixins"; @import "common"; @import "modules/bar"; @import "modules/dashboard"; `

    Нижнее подчеркивание в названии файла (например, _variables.scss`) — это стандарт SCSS, указывающий, что этот файл является «частичным» (partial) и его не нужно компилировать в отдельный CSS-файл, он предназначен только для импорта.

    Создание визуального стиля в EWW — это итеративный процесс. Начиная с простой палитры и базовых отступов, вы постепенно перейдете к сложным миксинам и анимациям. Главное помнить, что SCSS — это инструмент для реализации вашего дизайна, а не ограничение. Благодаря глубокой интеграции с GTK и поддержке современных возможностей препроцессора, вы можете добиться того, чтобы ваш Linux-десктоп выглядел именно так, как вы его себе представляете.

    6. Динамические данные: использование defpoll для периодического обновления информации

    Динамические данные: использование defpoll для периодического обновления информации

    Почему статические интерфейсы в Linux практически бесполезны? Представьте панель управления, которая показывает время вашего входа в систему, но не меняет его ни на секунду, или индикатор заряда батареи, застывший на 100% в то время, как ноутбук уже сигнализирует о разрядке. В мире системных виджетов данные — это живой поток, а не застывший текст. В EWW основным инструментом превращения статичного макета в живой информационный центр является механизм defpoll.

    Природа реактивности в EWW

    До этого момента мы работали с константами и аргументами, которые передавались в виджеты при их создании. Но EWW спроектирован как реактивный движок. Это означает, что виджет не просто "рисуется" один раз — он "подписывается" на изменения переменных. Как только значение переменной меняется, все связанные с ней компоненты (label, прогресс-бары, иконки) автоматически перерисовываются, чтобы отразить актуальное состояние.

    defpoll — это мост между миром командной строки Linux и декларативным миром Yuck. Он позволяет выполнять любую Shell-команду через заданные промежутки времени и сохранять результат её вывода в переменную.

    Синтаксис defpoll выглядит следующим образом:

    Рассмотрим каждый атрибут подробно:

  • имя_переменной: идентификатор, по которому вы будете обращаться к данным в Yuck через синтаксис 3/3/1}'")
  • yuck (defpoll weather :interval "1h" "scripts/get_weather.sh") yuck (label :text "{weather.temp}°C") yuck (defvar show_details false)

    (defpoll network_detailed :interval "1s" :run-while show_details "scripts/get_network_stats.sh") bash #!/bin/bash bat="/sys/class/power_supply/BAT0" capacity=bat/capacity") status=bat/status")

    if [ "capacity\", \"icon\": \"{battery_data.icon} {ram_usage}%") yuck (defpoll disk_used :interval "1m" "df / | tail -1 | awk '{print 2}'")

    (label :text "Диск: ${round(disk_used / disk_total * 100, 0)}%") yuck (defpoll player_active :interval "2s" "playerctl status 2>/dev/null || echo 'Stopped'") yuck (box :visible {player_active != "Stopped"} (button :onclick "playerctl previous" "󰒮") (button :onclick "playerctl play-pause" "󰐊") (button :onclick "playerctl next" "󰒭")) `

    Использование defpoll` превращает ваш конфигурационный файл из простого описания кнопок в логическую схему управления. Вы определяете, откуда брать данные и как часто, а EWW берет на себя всю рутину по обновлению интерфейса, обеспечивая плавность и актуальность вашей панели в Hyprland.

    7. Интерактивность: обработка событий нажатия и выполнение системных скриптов

    Интерактивность: обработка событий нажатия и выполнение системных скриптов

    Представьте, что вы создали идеальную панель: она переливается градиентами, показывает точное время и загрузку процессора. Но когда вы кликаете по иконке громкости, ничего не происходит. Виджет остается «вещью в себе», красивой, но бесполезной картинкой. В мире Linux-десктопов интерфейс ценен лишь тогда, когда он становится пультом управления. Нажать на кнопку и увидеть, как мгновенно открывается терминал, переключается музыкальный трек или меняется яркость монитора — именно в этот момент статический конфиг превращается в живой инструмент.

    В этой главе мы перейдем от пассивного созерцания данных к активному управлению системой. Мы разберем, как EWW связывает абстрактные клики мышью с реальными бинарными файлами в вашем /usr/bin, как передавать аргументы в скрипты прямо из интерфейса и как заставить виджет реагировать на действия пользователя мгновенно, не дожидаясь следующего цикла обновления.

    Механика событий в EWW: от виджета к ядру системы

    В основе интерактивности EWW лежит событийная модель GTK, адаптированная под декларативный синтаксис Yuck. Когда пользователь нажимает кнопку, движок не пытается самостоятельно решить, что делать. Вместо этого он делегирует задачу внешней среде — оболочке Shell.

    Это фундаментальное архитектурное решение. EWW не содержит в себе кода для регулировки звука или управления окнами Hyprland. Он лишь предоставляет «крючки» (hooks), за которые вы можете зацепить любую команду.

    Основными атрибутами для обработки взаимодействий являются: * :onclick — срабатывает при нажатии левой кнопкой мыши (ЛКМ). * :onrightclick — срабатывает при нажатии правой кнопкой мыши (ПКМ). * :onmiddleclick — срабатывает при нажатии на колесо мыши. * :onhover — выполняется, когда курсор входит в границы виджета. * :onhoverlost — выполняется, когда курсор покидает границы виджета.

    Любое из этих событий ожидает строку, которая будет интерпретирована как Shell-команда. Это может быть как простая утилита (например, notify-send), так и путь к вашему сложному Bash-скрипту.

    Анатомия команды: выполнение системных вызовов

    Рассмотрим простейший пример — кнопку закрытия окна или выхода из системы. В самом примитивном виде это выглядит так:

    Здесь строка "hyprctl dispatch exit" передается системному интерпретатору. Однако в реальных проектах команды редко бывают такими короткими. Часто нам нужно выполнить последовательность действий или обработать логику «если-то».

    Использование кавычек и экранирование

    Поскольку Yuck сам использует двойные кавычки для обозначения строк, написание сложных команд внутри атрибута может стать проблемой. Если вам нужно передать строку внутри команды, используйте одинарные кавычки или обратный слэш:

    Если же логика разрастается, золотое правило разработки на EWW гласит: выносите логику в отдельные скрипты. Вместо того чтобы городить конструкцию из пяти pipe и awk внутри файла .yuck, создайте файл scripts/volume.sh.

    Пути к скриптам

    EWW ищет файлы относительно своей рабочей директории (обычно ~/.config/eww). Чтобы ваши виджеты были переносимыми, лучше всего использовать встроенную переменную EWW_CONFIG_DIR.

    В этом примере переменная 1%

    Мгновенно обновляем переменную в EWW, чтобы интерфейс не ждал poll

    eww update current_volume=2 * 100}') lisp (button :onclick "./scripts/volume_set.sh +5" "UP") lisp (defvar menu_open false)

    (button :onclick "eww update menu_open=X и {id}" id)) lisp (button :class "kill-btn" :onclick "hyprctl dispatch killactive" "󰅙") bash eww logs

    Работа с ползунками (Scale) и вводом данных

    Интерактивность — это не только клики, но и плавное изменение значений. Виджет scale (ползунок) требует особого подхода, так как он генерирует событие при каждом движении.

    Атрибут :onchange в виджете scale передает новое значение в виде специальной переменной {}.

    Здесь {} — это плейсхолдер, который EWW автоматически заменяет на текущее числовое значение ползунка в момент перемещения. Это позволяет делать невероятно плавные регуляторы яркости и громкости.

    Нюанс «петли обратной связи»

    Если вы используете defpoll для чтения громкости и scale с :onchange для её установки, может возникнуть конфликт: вы тянете ползунок, а defpoll в этот же момент сбрасывает его назад к старому значению, так как система еще не успела применить изменения.

    Решение:

  • Используйте переменную defvar для временного хранения значения ползунка.
  • Обновляйте системный параметр только в :onchange.
  • Делайте eww update этой переменной сразу в команде :onchange.
  • Безопасность и производительность

    Поскольку :onclick выполняет произвольный Shell-код, будьте осторожны при передаче в него внешних данных. Хотя в контексте личного конфига риск невелик, хорошим тоном считается фильтрация входных данных, если они приходят из сети или ненадверенных источников.

    С точки зрения производительности: * Не запускайте тяжелые вычисления прямо в :onclick. Если нужно что-то долго считать, запустите скрипт в фоне (my_script.sh &), чтобы не блокировать UI-поток EWW. * Избегайте бесконечных циклов в скриптах, вызываемых из событий. * Помните, что каждое нажатие — это порождение нового процесса в системе (fork/exec). Для 10 нажатий в секунду это незаметно, но для автоматизированных действий лучше использовать deflisten (об этом в будущих главах).

    Практический кейс: Создание интерактивной панели управления (Dashboard)

    Давайте объединим знания и набросаем логику для кнопки управления Wi-Fi. Нам нужно:

  • Отображать текущее состояние (вкл/выкл).
  • Менять состояние при клике.
  • Менять иконку и цвет.
  • Скрипт ~/.config/eww/scripts/wifi.sh:

    Конфигурация eww.yuck:

    В этой связке мы видим идеальный цикл интерактивности: * defpoll обеспечивает актуальность данных, если вы включите Wi-Fi через терминал или другое меню. * button обеспечивает мгновенное действие. * Скрипт внутри себя вызывает eww update`, устраняя задержку интерфейса. * Динамические классы в SCSS меняют визуальное состояние кнопки.

    Интерактивность в EWW — это мост между визуальной эстетикой и системной мощью Linux. Понимая, как работают события и как правильно организовать связь со скриптами, вы перестаете быть просто пользователем интерфейса и становитесь его архитектором. В следующей главе мы углубимся в работу со сложными структурами данных и научимся генерировать виджеты динамически, используя циклы, что критически важно для создания списков задач, плееров и системных треев.

    8. Сложные виджеты: использование списков и циклов для генерации элементов

    Сложные виджеты: использование списков и циклов для генерации элементов

    Представьте, что вы создаете индикатор рабочих столов (воркспейсов) для Hyprland. В системе может быть три воркспейса, а может быть десять. Писать вручную десять одинаковых блоков (button ...) в коде Yuck — это не только утомительно, но и архитектурно неверно. Любое изменение в дизайне кнопки придется копировать десять раз. В программировании интерфейсов этот тупик разрешается итерацией: мы берем массив данных и «прокатываем» через него шаблон виджета. В EWW за эту магию отвечает конструкция for, которая превращает статичную конфигурацию в гибкую систему, способную адаптироваться к любому количеству внешних данных.

    Природа списков в Yuck и JSON-структуры

    Прежде чем переходить к циклам, необходимо понять, с какими данными работает EWW. В отличие от классических языков программирования, Yuck оперирует данными, которые чаще всего приходят извне в формате JSON. Это критически важно, так как стандартный вывод Shell-команд обычно представляет собой неструктурированный текст, который цикл for не сможет обработать напрямую.

    Чтобы цикл заработал, переменная должна содержать массив. В формате JSON это выглядит как [item1, item2, item3] или массив объектов [{"id": 1, "name": "workspace1"}, {"id": 2, "name": "workspace2"}].

    Когда вы используете defpoll для получения списка чего-либо (например, списка открытых окон или доступных Wi-Fi сетей), ваша задача — убедиться, что на выходе получается валидный JSON-массив. В экосистеме Arch Linux и Hyprland для этого идеально подходит утилита gojq (или стандартный jq), которая преобразует текст в структурированные данные.

    Анатомия цикла for

    Синтаксис цикла в Yuck может показаться непривычным для тех, кто привык к Python или JavaScript, так как он следует логике Lisp-подобных S-выражений.

    Проблема «пустого» состояния и сортировки

    При работе с циклами часто возникают граничные случаи. Что если массив пуст? Цикл for просто ничего не отрисует. Однако, если ваш контейнер box имеет фиксированные отступы или фон, пустой контейнер может выглядеть странно. В таких случаях полезно комбинировать for с тернарными операторами или атрибутом :visible.

    Второй нюанс — порядок. JSON-вывод системных утилит не всегда гарантирует сортировку. Если вы не добавите | sort в ваш скрипт, кнопки воркспейсов на панели могут прыгать (например, [1, 3, 2]), что разрушает пользовательский опыт. Всегда подготавливайте данные на стороне скрипта, прежде чем отдавать их в Yuck.

    Работа со сложными объектами в циклах

    Циклы становятся по-настоящему мощными, когда каждый элемент массива — это не просто строка или число, а объект с набором свойств. Рассмотрим создание виджета для мониторинга ресурсов, где мы хотим отобразить загрузку по каждому ядру процессора отдельно.

    Предположим, наш скрипт выдает такой JSON: [{"core": 0, "usage": 12}, {"core": 1, "usage": 45}, {"core": 2, "usage": 8}]

    В Yuck мы обращаемся к полям объекта через точечную нотацию:

    json [ {"name": "Firefox", "icon": "🌐", "exec": "firefox"}, {"name": "Terminal", "icon": "💻", "exec": "alacritty"}, {"name": "Files", "icon": "📁", "exec": "thunar"} ] yuck (defvar app_list '[{"name": "Firefox", "icon": "🌐", "exec": "firefox"}]') ;; Заглушка

    (defwidget app_launcher [] (box :orientation "v" :space-evenly false :class "launcher-window" (for app in app_list (button :class "app-item" :onclick "scripts/launch.sh AnBmn \times m{ws}" (label :text ws))) scss .ws-btn { background: #333; &.active { background: #0077ff; border-bottom: 2px solid white; } } `` Этот паттерн — основа создания профессиональных интерфейсов. Мы не меняем структуру виджета, мы лишь «подсвечиваем» нужные элементы, опираяся на логику сравнения внутри цикла.

    Отладка циклов: когда что-то пошло не так

    Самая частая ошибка при работе с for — несоответствие структуры данных и ожиданий Yuck. Если ваш defpoll вернул строку вместо массива, EWW выдаст ошибку парсинга в логах.

    Используйте команду eww state, чтобы увидеть текущее состояние ваших переменных. Если в поле переменной вы видите "[]" (строка с квадратными скобками), а не [] (массив), значит, ваш скрипт выводит данные некорректно.

    Еще одна ловушка — использование ключевых слов Yuck в качестве имен переменных итерации. Никогда не называйте переменную в цикле box, label или for. Используйте понятные имена: item, entry, ws, app.

    Взаимодействие с внешними скриптами обработки

    Поскольку Yuck — это декларативный язык, он плохо справляется со сложной логикой фильтрации. Если вам нужно отобразить «только те окна, которые не являются свернутыми и имеют заголовок длиннее 5 символов», не пытайтесь написать это внутри Yuck через тернарные операторы.

    Правильный путь:

  • Скрипт на Python/Bash собирает данные.
  • Скрипт фильтрует, сортирует и обрезает массив.
  • Скрипт упаковывает результат в чистый JSON.
  • EWW получает готовый массив и просто «размножает» виджеты.
  • Этот принцип разделения ответственности (Separation of Concerns) делает вашу конфигурацию EWW чистой, а отладку — быстрой. Вы всегда можете запустить скрипт в терминале и увидеть ровно те данные, которые получит цикл for.

    Заключение

    Использование списков и циклов переводит создание виджетов из разряда «рисования кнопок» в разряд «проектирования систем». Вместо того чтобы описывать каждый элемент интерфейса, вы описываете правила, по которым этот интерфейс строится на основе данных. Это позволяет создавать динамические панели задач, индикаторы воркспейсов, меню уведомлений и системные мониторы, которые ведут себя предсказуемо и эффективно. Освоение for` — это последний шаг перед тем, как ваши виджеты станут по-настоящему профессиональными и адаптивными.

    9. Продвинутая логика: работа с потоковыми данными через переменные deflisten

    Продвинутая логика: работа с потоковыми данными через переменные deflisten

    Почему ваш виджет громкости обновляется с задержкой в одну секунду, хотя вы крутите колесо мыши прямо сейчас? В мире интерфейсов задержка в 1000 миллисекунд кажется вечностью. До этого момента мы использовали defpoll — механизм, который напоминает настойчивого ребенка, каждые пять секунд спрашивающего: «Мы уже приехали?». Это работает для часов или прогноза погоды, но для динамического окружения Hyprland, где окна открываются мгновенно, а воркспейсы сменяются десятками в минуту, опрос (polling) становится либо слишком медленным, либо слишком ресурсозатратным.

    Решение кроется в переходе от модели «спрашивай» к модели «слушай». Вместо того чтобы заставлять EWW постоянно стучаться в двери системы, мы научим его держать дверь открытой и реагировать на входящий поток данных в тот самый миг, когда они возникают. Для этого в Yuck существует deflisten — мощнейший инструмент для создания по-настоящему реактивных интерфейсов.

    Природа реактивности: Poll vs Listen

    Чтобы понять, зачем нам deflisten, нужно осознать физику процесса defpoll. Когда вы задаете интервал в 1 секунду, демон EWW порождает новый процесс оболочки (shell), выполняет команду, ждет ответа, парсит его и убивает процесс. Если команд много, система начинает «икать» от постоянного создания и уничтожения сотен мелких процессов.

    deflisten работает иначе. Он запускает скрипт один раз при старте демона. Этот скрипт не завершается; он переходит в режим ожидания или бесконечного цикла, выдавая новую строку данных в стандартный вывод (stdout) только тогда, когда происходит событие. EWW «подвешивается» на этот поток и мгновенно обновляет переменную, как только в потоке появляется новая строка.

    Это дает три фундаментальных преимущества:

  • Нулевая задержка. Изменение в системе (например, переключение окна) доходит до виджета за миллисекунды.
  • Экономия ресурсов. Нет нужды постоянно перезапускать тяжелые утилиты вроде hyprctl или playerctl.
  • Событийная модель. Мы обрабатываем только реальные изменения, а не перерисовываем одно и то же значение каждую секунду.
  • Анатомия deflisten и требования к скриптам

    Синтаксис deflisten на первый взгляд прост, но он накладывает жесткие требования на то, как должен вести себя внешний скрипт.

    Ключевой момент: команда внутри deflisten должна быть блокирующей и построчной. Если вы запустите обычный ls, скрипт отработает один раз, выведет список файлов и завершится. EWW увидит завершение процесса и попытается запустить его снова (re-spawn), что превратит deflisten в очень плохой и неконтролируемый defpoll.

    Чтобы deflisten работал корректно, скрипт должен:

  • Иметь бесконечный цикл или использовать утилиты с флагом --follow или -f.
  • Выводить данные строго по одной строке за раз. Каждая новая строка — это новое состояние переменной.
  • Очищать буфер вывода. В некоторых языках программирования (например, Python) вывод буферизуется, и EWW не увидит данных, пока буфер не заполнится. В Shell-скриптах это решается автоматическим сбросом при переводе строки, но иногда требуется принудительный stdbuf -oL.
  • Практика: Слушаем события Hyprland

    Hyprland предоставляет идеальный интерфейс для deflisten — Unix-сокет событий. Если вы выполните в терминале socat -U - UNIX-CONNECT:/tmp/hypr/HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do # Если событие связано с изменением окна (activewindow) if [[ {window_title}" :limit-width 40))) lisp (deflisten music_info :initial "Silence" "playerctl --follow metadata --format '{{ title }} - {{ artist }}' || echo 'No Media'") lisp (deflisten network_data :initial '{"ssid": "Off", "signal": 0, "status": "down"}' "scripts/network_monitor.sh")

    (defwidget network_w [] (box :class "net-box {network_data.ssid}") (label :text "(date +%s%N) if (( current_time - last_update > 100000000 )); then echo "current_time fi done bash #!/bin/bash

    generate_json() { # Получаем список воркспейсов и активный ID local workspaces=(hyprctl activeworkspace -j | jq '.id')

    # Формируем единый JSON echo "{\"all\": active}" }

    Начальный вывод

    generate_json

    Слушаем сокет на предмет событий воркспейсов

    socat -U - UNIX-CONNECT:/tmp/hypr/line == workspace ]] || [[ line == destroyworkspace ]] || [[ {workspace_data.all}" (button :onclick "hyprctl dispatch workspace {ws.id == workspace_data.active ? 'active' : 'inactive'}" (label :text "${ws.id}")))))
    `

    В этой реализации мы получаем идеальный баланс:

  • Эффективность. Скрипт просыпается только тогда, когда Hyprland сообщает о манипуляциях с воркспейсами.
  • Информативность. Мы передаем структуру данных, которая позволяет кнопкам менять свой CSS-класс (active/inactive) на лету.
  • Интерактивность. Кнопки используют hyprctl, что в свою очередь вызовет новое событие в сокете, которое поймает наш deflisten, замкнув цикл обновления.
  • Граничные случаи и отладка

    При работе с deflisten новички часто сталкиваются с тем, что виджет просто пуст. Вот чек-лист для проверки:

  • Проверка в терминале. Запустите ваш скрипт вручную. Выводит ли он данные? Продолжает ли он работать после вывода первой строки? Если он завершается — это не подходит для deflisten.
  • Формат JSON. Если вы используете JSON, прогоните вывод скрипта через jq. Любая лишняя запятая или пропущенная кавычка «сломает» парсер EWW, и переменная не обновится.
  • Пути. Всегда используйте полные пути к скриптам или убедитесь, что они находятся в папке, которая видна демону EWW (обычно это подпапки внутри ~/.config/eww/).
  • Логи. Используйте eww logs. Там будут отображаться ошибки выполнения скриптов из deflisten, включая сообщения об ошибках из стандартного потока ошибок (stderr).
  • Использование deflisten` переводит ваши виджеты из разряда «статичных информеров» в разряд «живых компонентов системы». Это фундамент для создания профессиональных панелей, которые по отзывчивости не уступают нативным решениям вроде Waybar или системным панелям GNOME/KDE.

    В следующей главе мы объединим всё изученное: геометрию, стили, опросы и потоки данных, чтобы собрать воедино нашу финальную панель управления, которая станет полноценным центром вашей работы в Hyprland.