Microsoft рекомендует современный WinUI 3 в качестве официального фундамента для будущих Windows‑приложений. Однако, стандартный стек C++/WinRT + XAML встречает разработчика чудовищным временем компиляции, миксиновым адом вместо нативного наследования, тоннами шаблонной рутины и плохочитаемым кодом.

В этой статье мы разберем архитектуру wxl — тонкой надстройки, которая позволяет описывать GUI на чистом C++23 декларативно (как во Flutter или SwiftUI). Вы узнаете, как сжать заголовочные файлы проекции в 50 раз, сократить время пересборки с полуминуты до 4 секунд, победить накладные расходы QueryInterface с помощью ленивого кэширования интерфейсов на базе STA‑потока и превратить написание нативного GUI в чистое удовольствие.

Почему wxl?

Название проекта wxl образовано аббревиатурой: WinUI XAML‑Less. Обошлось без африканских животных, редких растений или героев античных мифов — три буквы на ID неймспейса — удобнейший из вариантов. Эта библиотека — сборная солянка хардокорных решений нетривиальных задач (кому интересно покопаться в технических подробностях) и одновременно ваш инструмент минутной доступности для молниеносного построения GUI‑приложений. Одинаково хорошо подойдёт как студентам, так и руководителям проектов.

Это тонкий слой над WinUI 3, в котором интерфейс описывается прямо на C++ — декларативно. При этом стандартная проекция C++/WinRT скрыта внутри библиотеки и больше не нагружает ваше приложение мегабайтами тяжелых заголовков, скрытыми вызовами QueryInterface на каждый вызов метода, не занимается постоянными динамическими приведениями .as<T>() в тех местах, где работает приведение вверх сугубо в уме компилятора по правилам обычного человеческого ООП‑наследования.

Библиотека идеально подходит для небольших приложений и утилит весом в пару диалогов, чтобы не раскочегаривать всю эту монструозную XAML‑кухню, объявляя свой класс App и классы каждого окна — теперь GUI‑приложение можно озвучить парой‑тройкой десятков строк в main.

А еще она отлично подходит для больших приложений, потому что любой код на С++, даже если это код описания GUI, легко декомпозировать, повторно использовать, в том числе в банальных циклах или в ветках if. Для сравнения, рассыпанные по интернету примеры‑калькуляторы на различных XAML‑проектах показывают в том числе то, зачем появился этот проект:

  • Либо в тех приложениях каждая кнопка описана в XAML честным образом, и тогда описание простейшей формы превращается в XAML‑ленту неприлично большой длины для малюсенького примера;

  • Либо кнопки создаются программно в цикле на ЯП, а не на XAML, но при этом синтаксис цепочки вызовов АПИ фреймворка оставляет желать лучшего и самим собой напрашивается уточняющий вопрос — а зачем тогда вообще XAML?

И чтобы сразу удовлетворить любопытство, панель с кнопками приложения Калькулятор из примеров wxl описывается так:

    [&](iterate<L"C÷×√"
                L"789-"
                L"456+"
                L"123%"
                L"±0.="> key) {
        return Button {
            key.text(),
            fontSize = 18,
            fontWeight = 600,
            style = face(key),
            hAlign.stretch, vAlign.stretch,
            row = key.index / 4 + 1,
            column = key.index % 4,
            onClick = [symbol = key.value]() { type(symbol); },
        };
    },

Неужели совсем без XAML?

На деле, XAML много зачем нужен на инфраструктурном уровне: на уровне шаблонов и презентеров контролов, словарей ресурсов и обслуживания тем. Это то место, через которое можно управлять видом своего приложения, банально подгрузив свои XAML‑ресурсы (переписав нужные вхождения стандартных словарей), не трогая ни строчки XAML/C#/C++ кода целевого приложения.

Это всё нам нравится, а что нравится — непременно надо брать с собой, всю эту систему, как базу, как надёжный фундамент — она разработана на редкость вдумчиво. И это выглядит безальтернативным на сегодня. Особенно под Windows. И да, автор хорошо наслышан (и напрограммирован лично) о Qt еще с той её версии, когда только благодаря KDE о Qt и узнали столь широко. Но по сегодняшним меркам Qt — это заложник подходов прошлого тысячелетия... Какое‑то время по инерции библиотека еще проедет, особенно со своим декларативным QML (только благодаря ему и проедет, давайте уже начистоту), ну а потом либо всё, либо это будет не тот Qt, который мы знаем сегодня.

Так на чём писать для сегодня и для завтра? Куда податься?

Неужели не нашлось более подходящего бэкенда?

Не люблю XAML, ничего не могу с собой поделать, простите... Но хорошо понимаю, что WinUI 3 — потрясающая и, как ни крути, не просто «одна из самых продуманных на сегодня GUI‑библиотек», а самая продуманная и есть. И всей её мощи рядовой разработчик не видит за забором XAML, воспринимая эту технологию чем‑то вроде «очередной клон WPF» или «UWP++».

Это переосмысленная технология UWP, конечно. Причём переосмысливалась он долго и болезненно — сначала в условной версии UWP v2, известной как WinUI 2, которая много лет была относительно незаметной для индустрии, хотя была максимально обкатана внутри MS на новых небольших приложениях уровня «Терминала Windows» и обновлённых «настройках». Но стала вызывать неподдельный интерес в 3-м по счёту серьёзном подходе к этому семейству технологий, чей номер итерации и унаследовала в своём названии.

Напомню, что WinUI 3 построена и намертво сроднёна с современным Windows Composition API (Visual Layer). Это и есть его главное техническое свойство, определившее выбор автора. Корнями этот подход уходит еще в DirectComposition из Windows 8, но именно в 10-ке графический композитор десктопа был доведён до своего ультимативного, зрелого состояния. На самом деле, уникальной и, без ложной лести, непревзойдённой графической подсистемой является именно этот виндовый композитор. Ничего лучшего в этой области пока что в IT объективно не существовало и вряд ли столь же всеобъемлюще повторится хоть где‑то в обозримом будущем.

И это тоже одна из важных причин, почему огромное количество приложений промышленного уровня (почти все CAD‑ы из всех областей) всё еще позиционируют исключительно под Windows, и почему целые армии разработчиков, 100% времени пишущих под Linux, делают это из‑под Windows. Кто бы как ни относился к экосистеме MS, но мы не можем закрыть глаза на беспрецедентный уровень проработки графического слоя, начиная с архитектуры драйверов с самого нижнего уровня гипервизора, что прилогиненный в систему пользователь, когда та находится в роли Hyper‑V, даже не в состоянии ощутить факт работы в виртуальном сеансе, потому что вся система, включая графический слой, работает в точности с эффективностью обычного невиртуального сеанса.

На деле, если бы разработчики Qt смогли бы полностью переползти под Windows на этот композитор и сделали бы над собой еще одно важное усилие — дали полностью родной виндовый стек сборки, при котором интиллесенс не сходит с ума, тогда перспективы у Qt были бы поинтересней, потому что альтернатива XAML нужна и важна. Но команда Qt по неизвестной автору причине оказалась не в состоянии взять два не самых высоких барьера уже в течение десятка с лишним лет, что внушает некоторые опасения о будущем этой технологии. Стандартный ответ про «кроссплатформенность и переносимость» Qt известен, но таким же ответом являются технологии навроде wxl с подменяемым простой линковкой бэкендом — не зря же одной из целей было отсутствие протекания winrt‑заголовков в публичные зависимости.

Жаль, что столь крутой композитор Windows появился на ~20 лет позже, чем должен был. По личным наблюдениям автора интерес к контролописательству стал постепенно сходить на нет примерно с середины нулевых... Ух, если бы дать нам этот композитор хотя бы в начале нулевых, в эпоху триумфального утверждения графических ускорителей в роли неотъемлемой части десктопных платформ... Без сомнения, мы бы увидели россыпь отличных GUI‑библиотек еще 20 лет назад. Но так уж вышло, что эта задача была «поставлена на паузу» на долгих 20 лет натурального безвременья в деле графических технологий, многочисленных экспериментов «на коленке» с fallback‑капитуляцией в WebView/Electron за неимением вменяемых альтернатив. И вот только сейчас, спустя четверть 21-го века, туман в этой области потихоньку рассеивается.

Что такое нынешний композитор Windows? Это продвинутая «рисовалка», которая покрывает полный спектр атомарных графических задач, причём покрывает их хорошо и с большим запасом. По уровню АПИ он обитает выше «голых» DirectX или OpenGL, но ниже уже готовой библиотеки контролов. Просто идеальное позиционирование! Берёт на себя всю «черновую работу» графического бэкенда (и делает её потрясающе хорошо), не навязывая никаких идей разработчикам графических библиотек, то есть не связывает им руки и не ограничивает полёт их фантазии.

Композитор — это не только управление ресурсами/картинками/фигурами/блиттинг/тени/эффекты, это еще анимация/синхронизация, что позволяет писать «живые» и «дышащие» приложения без строчки обслуживания сложной анимации в самом приложении — сейчас достаточно попросить композитора обслужить анимацию. И в этом сценарии сидит кое‑что поважнее простого удобства — анимация будет плавной в собственном потоке композитора, даже если ваш GUI‑поток по какой‑то причине «заикается». Для сравнения, на технологии WPF анимация выполняется самим приложением, поэтому пользователь регулярно видит дергающуюся эту анимацию, если приложение чем‑то сильно занято.

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

Давайте начистоту — WebView популярен в том числе по этой причине, что современный браузерный контрол поддерживает новейшие стандарты CSS, декларативно‑описываемую анимацию при смене состояний визуальных элементов, тщательно отлаженные стили элементов и устоявшиеся правила позиционирования контента, одним словом, позволяет нарисовать GUI, за который сегодня не стыдно. Определяющее в выборе WebView для десктопных приложений именно это, а не пресловутая «планка входа». Ситуация тут обратная — это другие десктопные GUI‑технологии «ниасилили» требования, предъявляемые к современному высококачественному GUI.

И есть еще кое‑что важное — это корутины стандарта С++20, кои были сразу же подхвачены в WinUI 2, а потом и WinUI 3, и отныне обработка событий GUI‑потока может быть полностью асинхронной, дабы не замораживать GUI‑поток. Более того, многие новые АПИ идут только в асинхронном варианте. Но и здесь всё не так просто и не совсем чисто в плане эффективности, как может показаться на первый взгляд.

Поэтому, это будет цикл статей о качестве и перфекционизме, о нескольких неординарных мерах, принятых для достижения первого и удовлетворения второго. Мы не просто сделаем так, чтобы приложения wxl работали лучше альтернатив. Мы задерём эту планку настолько высоко, что либо заставим альтернативы начать шевелиться, либо вся эта сложившаяся ситуация проедет по ним натуральным асфальтовым катком.

Будем отталкиваться от того, что эффективность нейтивных оконных приложений, написанных поверх композитора Windows, обычно запредельная — примерно как в самых оптимизированных 3D‑играх, то есть главное для нас — не испортить. Увы, WinUI 3 не во всех точках соприкосновения с композитором и другим системным окружением показывает максимально‑возможную эффективность, но этих «слабых» точек немного, и после их закрытия результат превзошёл все мыслимые первоначальные ожидания.

Ваш покорный слуга тоже не раз упражнялся в написании библиотек контролов прямо поверх композитора, но дальше нескольких разновидностей кнопок и отображения списков/репитеров дело обычно не доходило. Почему?

Ответ всегда один: потому что после реализации кнопок, списков, статиков/фигур‑шейпов и даже после полноценного лейаута (например, на основе yoga-layout) перед разработчиком встают сразу 3 горы, величиной примерно с Эверест. И обойти эти горы никак нельзя, честному инженеру потребуется терпеливо взобраться на вершину каждой из них.

Три горы

  • Гора первая — всё связанное с редактированием и форматированием текста. Один хороший EditControl (TextInputControl, TextBox) сам по себе «весит» больше остальной GUI‑библиотеки, потому что этот контрол представляет собой полноценный текстовый редактор. Добавь к нему менюшку, получишь готовый Notepad. А более продвинутый вариант этого контрола, RichTextControl, — это уже уровень офисного Word из второй половины 90-х.

  • Вторая гора: всё связанное с автоматизацией GUI. Внешние инструменты должны «понимать» ваше GUI‑приложение, и особенно это стало востребовано в эпоху AI, где нейросети запросто запускают приложения, вводят тексты в окошки, нажимают кнопки... В том числе для тестирования того, что они сами же и написали за очередную итерацию вайб‑кодинга.

  • Третья гора: всё вокруг обеспечения доступности пользователям с ограниченными возможностями (Accessibility). Контрастные темы, экранный диктор, который должен уметь сделать обход вашего визуального дерева и рассказать, что там и зачем. Именно поэтому к кнопке с иконкой в wxl надо привязывать тултип с текстом (буквально в одну строчку, как alt для HTML: tooltip = L"Конфигурировать подложку"), чтобы подсистема обеспечения доступности смогла рассказать, что это за кнопка.

Три. Горы. Размером. С Эверест.

Это необходимо понять и принять. И перестать тратить время на бесконечные самописные библиотеки GUI, коих слишком много и степень реализации коих чаще всего на уровне «плачевная». Даже у самых выдающихся из них, типа ImGui.

Автор ни в коем случае не отговаривает участвовать в разработке GUI‑библиотек! Буде желание — это только приветствуется! Автор лишь пытается обратить внимание на истинную масштабность задачи. Вердикт из 2026-го однозначен: вкладывать силы имеет смысл в те GUI‑библиотеки, в которых не просто задействовано достаточно высококлассных разработчиков, но где упомянутые сверхзадачи решены, либо планируются быть обязательно решёнными.

Но всё вышесказанное не относится к библиотекам‑оберткам, типа ReactNative, SWT или wxl. Такие библиотеки на базаре GUI‑технологий не поставщики, а покупатели готовых решений, просто заворачивают эти решения в любимую упаковку.

Что делать, если любимая упаковка — С++?

Эта статья — про фундамент архитектуры wxl:

  1. Декларативная запись;

  2. Избегание монструозных WinRT‑проекций;

  3. Настоящая иерархия классов со встроенным кешем COM‑интерфейсов под ней.

Есть еще четвертый и пятый пункты, но они столь объемны, что мы будем кушать их частями в этом цикле статей: про бескомпромиссный выбор уникальных решений, про передовые характеристики подсистем, про подходы и трюки, учитывающие конкретный сценарий GUI и как легким движением руки превращать ограничения в бонусы.

Мы ведь пишем GUI на С++, отсюда растёт желание продемонстрировать — до чего же нейтивные GUI‑программы могут быть легковесными и отзывчивыми, как быстро умеют «летать», натурально обгоняя скорость света (и сберегая вашу батарейку, если вы запускаете такую программу на планшете или ноутбуке).

Мы ведь живём в удивительные времена — это времена мощных ядер процессоров+графических ускорителей и россыпи ужасающе прожорливых GUI‑подсистем поверх них. Современный ответ на любой чих — WebView/Electron + JS. Про WPF уже и не вспоминают толком, он работал ненамного лучше. Неужели это тот предел, который мы заслужили? Есть выход.

Как это выглядит?

Пример wxl/samples/HelloHere
Пример wxl/samples/HelloHere

Окно с карточкой, тенью и кнопкой — целиком, без единого файла XAML.

Ниже в листинге на пару элементов попроще композиция, просто чтобы передать суть и показать код приложения целиком. Обратите внимание, что методы‑свойства (в т.ч. их DSL‑прокси в глобальном неймспейсе) могут конфликтовать с именами одноимённых типов, поэтому все прикладные методы проекций WinRT‑объектов переведены из исходного PascalCase к camelCase.

#include "pch.h"

using namespace wxl;
using namespace wxl::dsl;

wxl::Teardown wxl_launched() {
  auto window = Window {
    title = L"Hello from WXL",
    minSize = {500, 430},
    Grid {
      rowDefinitions = L"auto,*",
      Card {
        row = 1,
        Rows {
          TextBlock { L"A modern C++23 library", styles.textBlock.subtitle },
          Button {
            content = L"Click",
            toolTip = L"Похвалить библиотеку",
            onClick = {content = L"Thank You!"},                },
          },
       },
    },
  };

  window.activate();
  return {};
}

Механика под этим кодом простая и унифицированная. Каждое присваивание внутри фигурных скобок (например, content = L"Click") вычисляется в легковесную отложенную операцию. Затем вариадический конструктор класса поочередно применяет весь этот пакет к свежесозданному объекту. Благодаря этому описание интерфейса превращается в единое чистое выражение, а не в императивную последовательность операторов. Здесь просто нет понятия «текущего объекта», на который свойство могло бы накататься по ошибке.

События пишутся естественно: onClick = lambda/functor/func, при этом лаконичная форма onClick = {content = L"Thank You!"} представляет собой хитрый обработчик, всё тело которого автоматически транслирует присваивания обратно вызвавшему его объекту.

Мегабайты, которые никто больше не читает

Стандартная проекция C++/WinRT для WinUI 3 — это 30 мегабайт заголовочных файлов, размазанных по 252 файлам. Проблема в том, что компилятор их не линкует — он их парсит. И в классическом проекте этот тяжелый процесс повторяется в каждой единице трансляции, где упоминается хотя бы одна кнопка. Именно отсюда растет дурная слава C++/WinRT + XAML как систем с мучительно долгой сборкой.

Проекция wxl генерируется собственным утилитарным инструментом winui-srcgen строго по профилю — JSON‑описанию того, какие классы и члены действительно нужны приложению. Всё остальное автоматически вычисляется по графу транзитивных зависимостей.

Результат: заголовки, которые в итоге включает приложение, весят всего 574 КБ — почти в 50 раз меньше. А весь сгенерированный слой вместе с реализацией укладывается в скромный мегабайт.

В GUI‑разработке критически важен короткий цикл обратной связи: поправил пиксель в коде — и тут же запустил посмотреть на результат. Официальный C++/WinRT этот цикл безжалостно уничтожает. Постоянное ожидание компиляции превращает нативную разработку в изощрённую форму пыток. Стало понятно: если мы хотим писать под Windows с удовольствием, эту стену придётся как‑то пробить.

А что, если зайти с фланга и скормить компилятору только то, что нам действительно нужно?

Вторая половина решения еще бескомпромисснее: оригинальная проекция принципиально не протекает в публичный интерфейс wxl. Ни одного имени winrt::, ни одного ABI‑указателя, ни единого упоминания HRESULT снаружи нет. Всё это намертво заперто внутри .cpp‑файлов библиотеки. Те самые 30 мегабайт от cppwinrt парсятся ровно один раз — при сборке самой библиотеки wxl.

И это не просто джентльменское соглашение, а жесткое требование CI. Тест wxl.winui.surface-test компилирует весь декларативный словарь, вообще не имея путей к оригинальной проекции в include paths. Любое случайно утёкшее имя winrt:: мгновенно ломает сборку библиотеки.

Что это даёт в реальном времени, измерено на демонстрационном дереве (Debug, Ninja):

  • Правка main.cpp в приложении‑примере пересобирается всего за 4.7 секунды — вместе с компоновкой exe.

  • Правка одного файла самой библиотеки, заставляющая компилятор перепарсить оригинальную проекцию cppwinrt, занимает 30.8 секунды.

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

Наследование настоящее, а не через приведения

У стандартной cppwinrt наследования в привычном понимании нет. Класс Button там не наследует UIElement. Вместо этого они оба собраны из прожорливых CRTP‑миксинов consume_X<D>, их родство выражается через явный вызов метода .as<T>(), а сами миксины инстанцируются компилятором заново для каждого конкретного типа D, раздувая бинарник одинаковыми телами по всей проекции.

В wxl всё устроено иначе: wxl::Button честно наследуется от wxl::ButtonBase, тот — от более базовых классов, и так вплоть до wxl::UIElement и wxl::Object. Это классическая, чистая иерархия C++, которая один в один повторяет логику самого WinUI. Кнопка передаётся туда, где ожидается UIElement, нативными средствами языка. А декларативный Preset<Border> без проблем надевается на всё, что унаследовано от Border, просто потому что так работает стандартный концепт std::derived_from.

Сделать такое публичное наследование абсолютно безопасным позволяет одно строгое правило: ни один уровень публичной обёртки не несёт в себе ни единого поля данных. Всё реальное состояние заперто в приватной цепочке Impl (Button::Impl : ButtonBase::Impl : ...), скрытой за forward declaration и определённой глубоко в .cpp‑файлах. Сама обёртка — это лишь легковесный умный указатель на эту цепочку, поэтому срезка объектов (object slicing) здесь физически не способна ничего сломать.

У такого подхода есть огромная побочная выгода, которой напрочь лишён CRTP: скомпилированный бинарный код уровня UIElement получается ровно один на всех его бесчисленных наследников, а не плодится уникальными копиями под каждого из них.

QueryInterface, который случается один раз

Настоящий COM‑класс в WinRT реализует внушительную пачку интерфейсов. К примеру, UIElement — это одновременно IUIElement, IUIElementProtected, IAnimationObject, IVisualElement и далее по списку. В стандартной проекции вызов метода базового класса или неосновного интерфейса каждый раз разворачивается в целую церемонию: сначала QueryInterface (виртуальный вызов с атомарным AddRef внутри), затем сам целевой метод, а следом — обязательный Release с interlocked‑декрементом и ветвлением «не пора ли объекту умирать». И эта тяжелая цепочка крутится по кругу постоянно, даже если вы просто обращаетесь к основному интерфейсу через производный класс.

В wxl каждый интерфейс, который конкретный уровень иерархии реализует напрямую, представляет собой обычное именованное поле внутри структуры Impl. Заполняется это поле всегда лениво:

template <auto member>
member_type<member> const& Object::Impl::get() {
    auto& field = static_cast<class_type<member>&>(*this).*member;
    if (!field) {
        field = inspectable_.as<member_type<member>>();  // Один раз за жизнь объекта
    }
    return field;
}

Первый вызов делает единственный QueryInterface, который этот объект вообще увидит для данного интерфейса. Каждый следующий шаг будет стоить одну проверку указателя на nullptr.

Ленивая запись без блокировок и атомиков здесь абсолютно законна. Она опирается на ту же фундаментальную гарантию, что и вся экосистема wxl: контролы создаются и живут строго на одном главном STA‑потоке. Именно поэтому счётчики ссылок здесь обходятся без interlocked‑операций, а сами объекты Impl выделяются сверхбыстрым STA‑аллокатором, которому будет посвящена одна из ближайших статей цикла.

Из описанного вытекает забавное следствие: такой кэш окупается, даже если закешированный интерфейс больше никогда не понадобится. Сама процедура разового вызова становится заметно дешевле стандартного механизма WinRT — cохранённому в поле указателю не требуется вызывать Release на выходе из каждой функции, где атомарный декремент с последующим ветвлением на значении счётчика тоже вносит свой вклад в неэффективность оригинальной цепочки. Кэш в wxl — это не привычный компромисс «жертвуем памятью ради скорости при повторных вызовах». Он дешевле с самого первого обращения.

🛠 Хелперы, которые диктует сверхзадача

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

  • minSize = {500, 430} — под капотом скрывает кастомный подкласс окна и автоматическую обработку системного сообщения WM_GETMINMAXINFO;

  • placement — умное восстановление позиции окна: разбор сохранённой строки, проверка существования монитора и переезд на ближайший доступный экран, если старый отключили;

  • rowDefinitions = L"auto,*" — быстрая разметка Grid понятной строкой вместо императивного наполнения коллекции объектов;

  • toolTip = L"..." — одна строка для двух важнейших задач: всплывающая подсказка при наведении и имя для экранного диктора (ведь контролу с иконкой‑глифом вместо текстового слова критически важны оба свойства);

  • brushes.Card.BackgroundFillColor — путь к тематическому ресурсу, который превращается в кисть непосредственно в момент применения, а синтаксис вызова автоматически вытягивает нужный цвет из словаря активной темы;

  • wxl::DrawingSurface — нативная поверхность композитора под собственный Direct2D со всей цепочкой графических устройств внутри;

  • wxl::accept_file_drops — лаконичный перехват файлов, брошенных пользователем в окно;

  • wxl::UiThread — удобная очередь интерфейсного потока, которую можно безопасно звать из любого фонового воркера.

Каждый такой помощник пишется внутри библиотеки ровно один раз — и навсегда исчезает из кода конечных приложений вместе с ворохом сопутствующей рутины. Итог складывается сам собой: программирование GUI на современном C++ перестаёт быть мучением и становится тем, чем ему положено быть, — удовольствием для перфекциониста.

📂 Где посмотреть

  • samples/HelloHere — окно, карточка, тень, тема, переключаемая кнопкой, и форматированный текст — 220 строк вместе с комментариями;

  • samples/Calculator — сетка кнопок повторителем, клавиатура, стили;

  • wxl.gen/profiles/ — профили генератора проекций, от базового к богатому;