Skip to content

Skia-поверхности вместо зашитых сплитов и оверлеев (черновик для обсуждения)

Статус: Draft — зафиксировать общее понимание; при стабилизации можно сжать в ADR или раздел в 0017.
Дата: 2026-04-16
Связь: ADR 0017 (мультиоконность, MfdHostWindow, полный MfdShellView), 0021 (зоны внимания), 0022 (визуальная поверхность в MFD — ортогонально теме Skia, но та же идея «инструмент в контуре», не отдельный хак).


Зачем этот документ

Переход на Skia в стеке Avalonia задумывался не только как смена бэкенда отрисовки, а как способ:

  1. Уйти от зашитых сплитов в XAML (Grid, GridSplitter, фиксированные колонки под каждый новый инструмент) — они быстро становятся продуктовым контрактом: каждая новая сущность тянет ещё одну «полку» в главном окне.
  2. Отдать композицию и отрисовку слою, где раскладку проще менять (режимы, пресеты, второй монитор, кокпит) без разрастания дерева панелей.
  3. Перестать опираться на оверлеи как на основной способ показать постоянный UI — и перейти к нормальным Skia-поверхностям: узлы в дереве композиции с предсказуемым измерением, вводом, фокусом и клипом.

Цель документа — выровняться по терминам и зафиксировать направление до того, как оно разъедется по разным ADR и коду.


Термины

Термин Что имеем в виду
Зашитый сплит Раскладка в XAML, где новый инструмент = новая колонка/строка или обязательный GridSplitter в главном хроме, без участия модели презентации / зон внимания.
Оверлей Слой «поверх» основного дерева (popup, полупрозрачная панель, отдельный OverlayLayer, плавающий прямоугольник), удобный для прототипа, но с отдельными правилами z-order, hit-testing и фокуса.
Skia-поверхность (первая класса) Регион, который живёт в явном месте композиции: участвует в layout, получает pointer/keyboard как соседние зоны, рисуется через Skia в своих границах. Не замена Window, а нормальный участок UI, а не «налепка». Типичная реализация в дереве Avalonia — SkiaHost.
SkiaHost Контрол Avalonia (Control), который внутри своих границ хостит отрисовку Skia (measure/arrange, клип, ввод — по правилам дерева). Ставится в слот кабины / разметку как обычный узел. Не путать с MfdHostWindow — это отдельное окно вторичного контура; SkiaHostобёртка-контрол под канву внутри окна.
Вторичный контур MfdShellView: переключаемые страницы SecondaryShellPage (чат, терминал, сборка, обозреватель решения, …). Зона внимания Mfd — якорь презентации; страница — не то же самое, что зона (0021).

SkiaHost в коде (когда вводить тип)

Имя фиксируем в репозитории не только «чтобы не путаться», хотя дизамбиг важен: в проекте уже много *Host* (MfdHostWindow, host surface, каналы) — отдельный тип SkiaHost делает явным контрол Avalonia с канвой Skia внутри окна, а не второе TopLevel и не абстрактный «хост поверхности» в смысле CDS.

Инженерно тип имеет смысл как единая точка для общего поведения: инвалидация перерисовки, DPI / layout rounding, при необходимости маршрут ввода — чтобы не плодить копипасту «ещё один кастомный Control со Skia» в каждом слоте; удобный grep SkiaHost для ревью.

Когда заводить в коде: когда начинаете стабильно монтировать Skia в слотах кабины (не обязательно отдельный PR «только имя» до первого реального использования). Пока Skia только в превью/экспериментах и размазан по контролам — документ термина достаточен; при консолидации — имя типа в коде совпадает с этим документом.


Принятые ориентиры

  1. Презентация задаёт топологию, а не набор сплитов вручную: строка presentation / zone_screen_layout и грамматика якорей (0017). На главном окне при схемах вроде (P+F)(M) в фокусе P и лобовое; содержимое M (включая вторичный контур) не дублируется отдельной «полкой» только ради одного инструмента.

  2. Обозреватель решениястраница вторичного контура (SecondaryShellPage.SolutionExplorer), а не отдельная колонка со сплитом «дерево + shell» в MainWindow. То же дерево доступно в колонке Mfd и в MfdHostWindow, пока открыта эта страница — без второго независимого дубля MFD-контента в main (0017 п. 8).

  3. Оверлей — не зло сам по себе; зло — постоянный UI «налепкой». Оверлеи в прототипе доказали работоспособность; то, что теперь путает и сбивает (постоянные панели, карты, инструменты), убираем в пользу полноценных поверхностей в зоне (в т.ч. Skia). Где оверлей по смыслу уместен — оставляем: тосты, подсказки, превью перетаскивания, модалка на весь экран, иной временный и контекстный слой — иначе пришлось бы дублировать ту же семантику в дереве ради запрета на «поверх». Итог: не «ноль оверлеев», а не подменять ими зоны презентации.

  4. Skia — способ отрисовки и гибкой композиции внутри выделенных регионов (в т.ч. кокпит, отдельные инструменты), а не оправдание очередного глобального слоя поверх всего окна.

  5. Слабая связность и роль Avalonia. Рефакторинг и вынос логики из «прилипшего к вью» кода нацелены на то, чтобы оркестрация, правила презентации, команды, состояние workspace жили во view model и сервисах с явными границами. Avalonia остаётся слоем хоста и тяжёлых контролов: окна, ввод и фокус, жизненный цикл визуала, встраивание дорогих виджетов (редактор, докинг, списки и т.п.) — всё, ради чего не имеет смысла писать свой UI-стек с нуля. Это не утверждение «половина экрана не на Avalonia»: типично весь chrome всё ещё дерево Avalonia; речь о узкой ответственности — не размазывать семантику кокпита по XAML/code-behind. Skia-поверхности при этом живут внутри хоста (измерение, клип, pointer — через тот же слой, пока мы так решили).

  6. Avalonia XPF — не «платная сетка для 50/50». Коммерческий Avalonia XPF — это продукт совместимости с WPF (перенос существующих WPF-приложений на движок Avalonia), а не замена stock-layout для зелёного приложения на Avalonia. Мотивация «пропорции и сплиты боль без отдельного продукта» ведёт к Skia / своему композитору / модели зон, а не к покупке XPF как способа упростить Grid.

Зафиксированное разделение: Skia, Avalonia, view model

Продуктовая позиция команды (не перечитывать как «весь UI не Avalonia» — речь об ответственности):

Слой Роль
Skia Границы регионов, отрисовка, зоны (доли, кокпит, кастомная графика) — там, где источник правды не зашитый XAML-сплит. На уровне дерева — обычно через SkiaHost.
Avalonia Редактор, дерево, терминал и прочие тяжёлые контролы. Это виджеты, которые монтируются во вторичный контур как страница (SecondaryShellPage и аналоги в MfdShellView) и показываются там, куда укажет оркестрация (в колонке Mfd главного окна или в MfdHostWindow — тот же набор страниц, тот же хост). Они не являются «логикой внутри MainWindowViewModel» в смысле реализации редактирования текста/дерева в VM.
View model (в т.ч. MainWindowViewModel) Навигация и хром: какая страница вторичного контура активна, видимость зон Mfd/Pfd, команды, согласованность с мультиоконностью — не внутренности редактора или дерева как предметная логика в VM.

Семантика кабины для агента/тестов агрегируется в CDS (CockpitSurfaceSnapshotBuildercds-contract-v0.md); сейчас снимок строится из VM, целевой язык — CDS. Как сблизить и не плодить второй источник правды — см. подраздел «Источник правды сегодня и как сблизить с CDS» там же.


Зафиксировано здесь (не ждём отдельного ADR без новой развилки)

  • Граница оверлей vs поверхность в зоне — см. п. 3: постоянный инструментарий → в зоне (Skia / слот кабины); временное и контекстное → оверлей, где уместен.
  • Рендер-пайплайн (SkiaHost vs отдельные surface hosts / прочие оптимизации): жёсткой взаимоисключающей развилки на весь продукт сейчас нет. Дефолт: SkiaHost в слоте в дереве Avalonia; цепочка смысла — ADR 0036 (канал → CDS → композитор → поверхность). Отдельные surface hosts и иные оптимизации — последующая эволюция по метрикам/требованиям, не вторая параллельная «архитектура», пока не появится несовместимое требование для класса сценариев. Отдельный ADR под пайплайн — только тогда; до тех пор достаточно architecture-migration.md + эта заметка (при необходимости — дополнение к 0036, а не дублирующий ADR).

Остаётся открытым (вне этого документа или по фичам)

  • Пофичево (semantic map, подтверждения агента, палитра): оверлей vs зона в конкретном UX — по миграции и 0017 п. 6, не по принципу «только оверлей / только Skia».
  • Кокпит и host-surface: сопоставить правила размещения инструментов (CockpitInstrumentPlacementRules) с принципом «не размножать сплиты в main» после эволюции shell/страниц.

История правок

Дата Изменение
2026-04-16 Первый черновик: сплиты, оверлеи, Skia-поверхности, связь с вторичным контуром и 0017.
2026-04-16 П. 5–6: граница Avalonia (хост + тяжёлые контролы), слабая связность; уточнение про XPF vs Skia.
2026-04-16 Подраздел «Зафиксированное разделение»: Skia (границы/зоны/отрисовка), Avalonia (редактор/дерево как страницы shell), VM — навигация, не логика виджетов.
2026-04-16 Связка VM ↔ CDS: ссылка на cds-contract-v0.md (источник правды / улучшение).
2026-04-16 П. 3 и открытые вопросы: постоянный UI из оверлеев → поверхности в зоне; оверлей оставляем где уместен (тосты, DnD, модалки).
2026-04-16 Термин SkiaHost: контрол Avalonia, хостящий Skia; дизамбиг с MfdHostWindow.
2026-04-16 Подраздел «SkiaHost в коде»: зачем тип в репозитории (дизамбиг + общая логика + grep), когда вводить.
2026-04-16 Раздел «открытые вопросы» перестроен: зафиксировано (оверлеи, рендер-пайплайн без отдельного ADR до жёсткой развилки); открыто — пофичево + CockpitInstrumentPlacementRules.