MOffice

Своя оболочка MOffice без Qt: анализ, решение, прототип

2026-10-04, сессия B «Платформа». Поручение владельца: «Нам нужно сделать такую же оболочку и использовать свою оболочку» — то есть оболочку с тем же видом и поведением, что у прототипа на Qt 6.8 (B3), но на собственном коде, без Qt. Решение записано в библию (§4, §6, §12, решение № 39) и в docs/13-owner-decisions.md §1. Код — ui/meridian-ui (набор компонентов) и ui/meridian-write (окно Write); снимки и сравнение — страница «Своя оболочка».

1. Почему не Qt

Довод Суть
Коммерческая Qt, скорее всего, недоступна По открытым источникам Qt Group с марта 2022 не продаёт коммерческие лицензии и поддержку в России (docs/legal/qt-quote-request.md). Ждать ответа и строить на этом план — риск для всей оболочки
LGPL противоречит решению № 35 «Полностью свой код под своей лицензией», класс 2 (LGPL) не используем. У LGPL обязательства: только динамическая линковка, право пользователя заменить библиотеки Qt, выкладка исходников Qt, текст LGPL в поставке, сложности с магазинами приложений
Зависимость от вендора Лицензионные условия, цены (≈ 3 600–3 950 $ за разработчика в год) и санкционная политика — вне нашего контроля
Два языка и мост C++/QML + Rust через C ABI: две сборочные системы (CMake + cargo), две команды, отладка на стыке
Размер и поставка Qt — десятки мегабайт библиотек рядом с программой; своя оболочка — один файл ≈ 14 МБ (macOS arm64, release) со шрифтами и ядром

2. Что рассматривали

Вариант Лицензия Почему нет / да
Qt 6 коммерческая платная вероятный отказ продаж в РФ; дорого; вендор
Qt 6 LGPLv3 класс 2 противоречит решению № 35, ограничения поставки
Slint (Rust) GPLv3 / «royalty-free» с условиями / коммерческая не класс 0–1; условия бесплатной лицензии меняются вендором
GTK 4 LGPL класс 2; на Windows и macOS чужой вид
Flutter BSD-3 второй язык (Dart) и свой движок на C++; FFI с ядром; вид Material/Cupertino — переделывать всё
Electron / Tauri (WebView) MIT / Apache веб-движок: память, задержка ввода, разный WebView на ОС; офисный пакет с тяжёлым холстом — не наш путь (библия §4)
egui MIT/Apache немедленный режим близок к нашему, но вид и ввод под инструменты, ленту и MDS всё равно писать самим; плюс зависимость от чужой архитектуры
iced, Xilem/Masonry, GPUI MIT / Apache iced — архитектура Elm, своя модель виджетов; Xilem — ранняя стадия; GPUI привязан к Zed и неполон на Windows
Свой набор meridian-ui на «кирпичах» класса 0–1 наш код + MIT/Apache выбрано: тот же растр и текст, что в ядре; полный контроль над видом MDS; один язык; одна сборка

3. Что взяли и что написали сами

Слой Берём (класс 0–1) Пишем сами
Окно, события ОС, HiDPI, Wayland/X11/Win32/Cocoa winit 0.30 (Apache-2.0; без Adwaita-рамки — она тянула заброшенный ttf-parser) перетаскивание за свой заголовок, двойной щелчок — развернуть, отступ под кнопки окна macOS
Вывод кадра в окно softbuffer 0.4 (MIT/Apache) — без GPU преобразование кадра, кэш масок
Растр tiny-skia 0.12 (BSD-3) — тот же, что в ядре слой рисования paint.rs: обрезка без масок для прямоугольников, построчное копирование страниц, тени
Текст harfrust (MIT) + skrifa (MIT/Apache) — те же, что в ядре (с 2026-10-04 — свой meridian-font, §8) text.rs: комплект шрифтов, подстановка недостающих символов, синтетический курсив, кэши, усечение «…», перенос
Шрифты Inter 4.1 (OFL-1.1) — токен fontFamily.ui; Carlito и Liberation — из ядра —
Иконки — svg.rs: свой разбор подмножества SVG (контуры с дугами по SVG 1.1 F.6, круги, прямоугольники)
Токены spec/design-tokens.json генератор scripts/gen_shell_assets.py выдаёт и QML, и Rust (tokens.rs, icons_gen.rs) — один источник истины
Ввод — ui.rs: немедленный режим, попадание по прошлому кадру, верхний элемент, щелчок/двойной щелчок, модальные слои, обрезка
Компоненты MDS — widgets.rs: копии shell/mds/components/*.qml — кнопки 4 видов, кнопки и переключатели ленты, поле со списком, карточка стиля, вкладка документа, боковая панель, ползунок масштаба
Окно Write — app.rs, ribbon.rs: заголовок, лента 8 вкладок с адаптивным сворачиванием групп (перенос RibbonPage.relayout()), холст, панели, строка состояния, палитра команд, backstage, списки
Ядро C ABI meridian-ffi v0.2 — тот же контракт, что у Qt-оболочки kernel.rs: безопасная обёртка и фоновый рендер страниц (2–4 потока, кэш растров)
Диалог «Открыть» rfd 0.15 (MIT; на Linux — портал XDG по D-Bus, без GTK) —

4. Результат прототипа (2026-10-04)

Показатель Значение
Сходство с Qt-оболочкой (SSIM по 6 снимкам 1440×900 и 1024×700, 1.0 — до пикселя) светлая 0.985, тёмная 0.983, палитра 0.968, backstage 0.996, узкое окно 0.984, новый документ 0.982
Время кадра 2880×1800 (Retina 2×), CPU M5 Max, release 8,7 мс медиана, 10,3 мс 95-й процентиль (было 27 мс до оптимизаций: обрезка без масок, построчное копирование страниц, тени полосками, без лишней заливки кадра)
Размер программы (macOS arm64, release) ≈ 14 МБ одним файлом — интерфейс, шрифты, ядро; Qt рядом не нужен
Объём кода ≈ 7 200 строк Rust (из них 520 — сгенерированные токены и иконки); Qt-версия — ≈ 3 400 строк QML/C++ поверх Qt
Тесты 39: 19 модульных meridian-ui (цвет, геометрия, текст, разбор всех 71 иконки, рисование и обрезка, ввод, размеры компонентов), 8 модульных meridian-write (ядро, рендер в фоне, лента, поиск палитры), 12 сквозных (щелчки и клавиши как у пользователя: тема, палитра, вкладки, масштаб, навигация по реальному docx, сворачивание ленты, списки, backstage, узкое окно, панели)
Настоящее окно macOS: тема из системы, Retina 2×, фоновый рендер страниц, перетаскивание, --quit-after/--dump-frame для дымовой проверки
Linux x86_64 сборка за 2 мин в контейнере rust на sagr, 39 тестов, clippy -D warnings чистый; зависимости программы — только libc/libm/libgcc_s (X11/Wayland подгружаются при запуске); кадр совпадает с macOS arm64 на 99,92 % пикселей
Лицензии cargo deny: advisories, bans, licenses, sources — ok; THIRD_PARTY.md/NOTICE/SBOM перегенерированы (Inter — в third_party.toml)

Найдено и исправлено по ходу (тестами): результат щелчка показывался кадром позже — теперь после события сразу строится ещё один кадр; маска обрезки копировалась на каждый примитив (5 МБ) — теперь по указателю и только для фигур на краю.

5. Чего ещё нет (честно) и оценка

Область Что нужно (своим кодом, решение № 40) Оценка, чел.-мес.
Доступность macOS — сделано 2026-10-04: дерево доступности из подписей, ролей (кнопка, вкладка, строка списка), рамок и порядка фокуса (группы: заголовок → лента → панели → документ → строка состояния; модальный слой — только он) → объекты NSAccessibilityElement под представлением, нажатие экранным чтецом → щелчок; Linux — сделано 2026-10-05: AT-SPI 2 через свой клиент D-Bus (Embed в реестр, Accessible/Component/Action/Application/Cache, сигналы состава, подписей и фокуса), проверено клиентом pyatspi под Xvfb; дальше — UI Automation (Windows), текст документа и проверка с VoiceOver/NVDA/Orca 1–1,5
Ввод текста в документ буфер обмена — сделано 2026-10-04 (NSPasteboard, Win32 Clipboard, выделение CLIPBOARD X11 с ответами другим программам; вставка в палитру и путей в диалог «Открыть», копирование пути); IME на macOS — сделано 2026-10-04 (NSTextInputClient: набор через interpretKeyEvents:, черновик setMarkedText: → Event::ImePreedit, окно кандидатов у поля ввода, «мёртвые» клавиши; в палитре); XIM на Linux — сделано (XOpenIM/XCreateIC, XFilterEvent, Xutf8LookupString: составные символы, «мёртвые» клавиши, IME через XMODIFIERS); IMM32/TSF (Windows, сейчас — подтверждённый текст через WM_CHAR), курсор и выделение — вместе с моделью правки ядра (фаза 1) 0,8–1,3
GPU-рендер свой путь через Metal / Direct3D / Vulkan для 4K/5K и анимаций — по замерам; CPU-путь (meridian-raster, 7,4 мс на 2880×1800) остаётся основным 1,5–2
Подсказки, анимации, фокус с клавиатуры фокус с клавиатуры и подсказки клавиш Alt — сделано 2026-10-04: Tab/Shift+Tab по всем кнопкам и вкладкам, Enter и пробел нажимают, рамка по токенам borderWidth.focus/focusOffset, модальные слои замыкают фокус; одиночный Alt (F10 на Windows) — коды у вкладок и команд по подписям, двухбуквенные при нехватке букв, любая раскладка; всплывающие подсказки — сделано: 600 мс, название и сочетание клавиш, человеческие подписи кнопок-иконок, перерисовка по таймеру; стрелки — сделано: при видимой рамке фокус идёт к ближайшему элементу в ту сторону, сначала в том же ряду (вкладки, команды ленты, списки); анимации — сделано: anim.rs (cubic-bezier и длительности из токенов, переходы по элементу, смена цели с текущего значения), полоска вкладок ленты и появление палитры, «уменьшить движение» ОС (macOS, Windows), кадры по 16 мс только пока идёт переход —
Своя рамка окна на Windows/Linux изменение размера за края — сделано 2026-10-04: X11 — полоса у края 6 px (углы 12), курсоры сторон, _NET_WM_MOVERESIZE, проверено под Openbox; Windows — рамка системы сохранена (WM_NCCALCSIZE), верх — HTTOP; свои кнопки окна (свернуть, развернуть/восстановить, закрыть) на Windows и Linux — сделано, проверено под Openbox; дальше — меню привязок Snap (Windows 11) при наведении на «Развернуть» 0,1
Меню macOS, печать, перетаскивание файлов внутрь, несколько окон меню macOS — сделано 2026-10-04 своим кодом в meridian-platform (декларативное описание, свои и стандартные пункты, Event::Menu, проверено на настоящем окне); печать — сделано 2026-10-04: PDF/A-2b ядра → macOS — системное окно печати (PDFKit, лист у окна), Linux — CUPS (lp), Windows — команда «печать» для PDF; раздел backstage «Экспорт» (PDF рядом с документом, копировать путь); перетаскивание файлов — уже есть; дальше — несколько окон 1–1,5
Шрифты для редких письменностей в интерфейсе сделано 2026-10-04: подстановка из системных шрифтов своим кодом — папки ОС один раз, файлы лениво по подсказкам письменности (CJK, хангыль, армянский, грузинский, тайский, иврит, арабский, деванагари), в коллекциях — обычное начертание, кэш и лимиты; дальше — шейпинг сложных письменностей в интерфейсе через meridian-shape сессии A 0,2
Windows-проверка и CI-матрица трёх ОС Linux — свой раннер на sagr (сделано 2026-10-04, just ci на каждый push); Windows и macOS — нужны машины 0,5
Итого до продуктового паритета с Qt ≈ 8–11 (против лицензий Qt ≈ 30–145 тыс. $ в год и обязательств LGPL)

6. План

  1. Фаза 0 (сейчас) — прототип ui/ = вид и поведение Qt-прототипа; Qt-оболочка shell/ остаётся эталоном для сравнения снимков (SSIM) до паритета и не распространяется.
  2. Фаза 1 — разработка Write идёт на своей оболочке; доступность, IME и буфер обмена — первыми (вместе с моделью правки); GPU-рендер — по замерам на 4K.
  3. После паритета — shell/ (Qt) удаляется из репозитория, решение № 36 закрывается; запрос в Qt Sales не нужен.

7. Риски и меры

Риск Мера
Доступность хуже, чем у Qt свой мост к API доступности с фазы 1 (порядок фокуса уже есть); тесты с экранными чтецами в критериях готовности
Ввод сложных письменностей (IME) свой IME в meridian-platform + шейпер ядра meridian-shape; корпус ru/en/ar/he/hi уже есть у A3
Производительность на слабых ПК CPU-путь уже 7,4 мс на 2880×1800; GPU-путь — фаза 1; замер на эталонном ноутбуке в CI
Объём своего кода компоненты — копии готовой дизайн-системы MDS, поведение проверяется снимками и сквозными тестами

8. «Весь код свой» в оболочке (решение № 40)

Владелец подтвердил 2026-10-04: и в оболочке чужие библиотеки заменяются своим кодом («переписать всё»). Порядок и состояние:

Что было Чем заменяем Состояние
rfd (системный диалог «Открыть», на Linux — ashpd/zbus) свой диалог CM-01 в интерфейсе (ui/meridian-write/src/filedialog.rs): места, путь, список, сведения, фильтр; одинаков на всех ОС сделано 2026-10-04; −41 сторонний крейт
tiny-skia (растр) свой kernel/crates/meridian-raster (зона B, общий с ядром — шаг 6 плана ядра): пути, сглаженная заливка non-zero/even-odd (16 подстрок × 1/256 px), обводка со стыками и концами, маски, картинки с аффинным преобразованием, source-over, запись PNG на своём DEFLATE meridian-archive сделано 2026-10-04: кадры оболочки совпадают с tiny-skia — SSIM 0,9999, кадр 2880×1800 7,2 мс (было 8,7); детерминизм — только целые и базовые операции f32. Ядро (meridian-render) переводит сессия A
winit + softbuffer (окно, ввод, вывод кадра) свой ui/meridian-platform без зависимостей: macOS — AppKit и CoreGraphics через objc runtime (своё представление MeridianView, CGImage, libdispatch); Linux — X11 через подгружаемый Xlib (EWMH-перетаскивание и разворот, XPutImage, раскладки с кириллицей, Xft.dpi, будильник на сокетах и poll); Windows — Win32 и GDI (свой заголовок через WM_NCCALCSIZE, SetDIBitsToDevice, DPI на монитор, тема из реестра, PostMessage) сделано 2026-10-04: macOS — настоящее окно снято системой (screencapture -l); Linux — Xvfb на sagr: окно, щелчки xdotool (вкладка «Вид» → «Тёмная»), палитра Ctrl+Shift+P, набор латиницей и кириллицей («тёмн» с «ё»), Enter; дымовой выход; Windows — сборка и clippy -D warnings чистые (кросс-проверка), запуск — на машине с Windows (пока нет); −55 сторонних крейтов
harfrust + skrifa (текст) meridian-font сессии A (OpenType: cmap, hmtx, kern, контуры glyf/CFF; шейпер — шаг 8 плана ядра) сделано 2026-10-04: ui/meridian-ui зависит только от своих крейтов (meridian-raster, meridian-font); кадры совпадают с harfrust/skrifa — SSIM 0,9962–0,9995 (светлая 0,9995, палитра 0,9985, «Открыть» 0,9990, оглавление 0,9962), кадр 2880×1800 8,0 мс. С парами GPOS от A (f8312e2) кернинг Inter совпадает с HarfBuzz: SSIM 0,9999–1,0000, кадр 7,4 мс; harfrust ещё приходит в сборку через раскладку ядра (meridian-layout, зона A)
Шрифт Inter (данные) остаётся данными (OFL) — решение владельца по данным —