[Перевод] Разработка интерфейса для Windows — это боль

af4d558af92302957306174a1a3df46a.png

Последние несколько дней я пытался найти библиотеку, которая бы позволила мне писать на C++ программы с GUI. Мои требования были довольно просты:

  • Достаточно только поддержки Windows

  • Разрешено коммерческое использование

  • Простая стилизация, в том числе и тёмный режим

  • Результатом должен быть единый файл .exe размером меньше 40 МБ без или с минимальным количеством зависимостей

  • Написание части программы с GUI не должно занимать больше времени, чем сама функциональность

WinUI 3

На первый взгляд кажется идеальным выбором. Позволяет использовать современные компоненты Windows, в то же время допуская настройку цветов стилизации. Для дизайна можно пользоваться XAML, который очень легко освоить, или же напрямую работать с Visual Studio designer.

screenshot

Галерея элементов управления WinUI 3

Проблема:  выпуск приложений в неупакованном виде поддерживается не очень хорошо. При попытках перенести приложение в VM или на другой компьютер чаще всего оно отказывалось запускаться из-за отсутствия каких-то непонятных зависимостей. Хуже того, в комплекте нужно поставлять несколько файлов .dll, управляющих функциональностью WinUI. Получить единый портируемый файл .exe никак невозможно. Использование упакованного вида обычно работает без проблем, но приложения устанавливаются как пакеты AppX, что привносит множество других проблем (особенно если вам нужен доступ ко всем Win32 API).

Win32 / MFC / небольшие библиотеки-обёртки для Win32

Мне нужна была высокая степень портируемости, так что логично было использовать нативный рендеринг операционной системы. Такая программа может быть единым файлом .exe (если мы статически компонуем MFC) и в то же время очень маленькой (всего несколько килобайтов). Можно было бы использовать и ещё более минимальную библиотеку, которую уже написали, что позволило бы мне быстро превратить концепцию в работающее приложение.

screenshot

Простая форма Win32

Проблема:  стилизовать нативные элементы управления (controls) Win32 крайне сложно. Мне пришлось бы писать собственную функцию Paint для каждого элемента управления, что потребовало бы столько времени, что я бы успел ещё и обзавестись семьёй. Существует «тайный» тёмный режим для элементов управления Win32, используемый в Проводнике Windows, который можно активировать, но он охватывает только часть элементов управления и выглядит не очень красиво.

Qt

Эта библиотека — Святой Грааль GUI для C++. Хотя она довольно сложна, в ней есть удобная стилизация при помощи языка Qt Style Sheets, похожего на CSS.

screenshot

Использование Qt и собственных таблиц стилей в OBS studio

Проблема:  при динамической компоновке для запуска приложения требуется куча разных .dll общим размером более 40 МБ. Можно статически скомпоновать Qt в программу, что существенно уменьшит её размер (так как неиспользуемые части удаляются), но тогда по лицензии LGPL Qt вам придётся сделать её опенсорсной или распространять в виде объектных файлов. Или же можно ежегодно покупать коммерческую лицензию за несколько тысяч долларов.

wxWidgets

Достаточно простая в изучении библиотека с опцией использования wxFormBuilder. Её лицензия менее требовательна, чем у Qt, и её можно статически скомпоновать в исполняемый файл размером 3 МБ.

screenshot

wxWidgets со включенной экспериментальной опцией тёмного режима в Windows

Проблема:  в Windows эта библиотека использует нативные компоненты Win32 и не предоставляет никаких опций стилизации (так как мы не можем напрямую переписать функции Paint, она даже хуже, чем применение непосредственно Win32/MFC). Она поддерживает применение тёмных элементов управления Проводника Windows, но, как я говорил, они некрасивые.

hikogui

Довольно новая библиотека GUI retained mode, использующая в качестве бэкенда Vulkan. Имеет встроенный тёмный режим и достаточно проста в самостоятельной стилизации.

screenshot

Скриншоты из официального репозитория

Проблема:  для успешной компиляции вам понадобится докторская степень по computer science со специализацией в разработке ПО. Потратив больше получаса на попытки скомпилировать пример (в том числе различных веток и меток релизов), единственное, что мне удалось получить — это исполняемый файл, немедленно вылетавший с ошибкой нарушения доступа внутри какой-то библиотеки Vulkan, поэтому я просто сдался. Проект выглядит очень многообещающе, хотя мне и не нравится активное применение надоедливой STL (без которой иногда даже можно было обойтись).

Sciter

Хорошая альтернатива Electron, позволяющая писать GUI для десктопного приложения на HTML/CSS.

screenshot

Пример плохого сглаживания SVG-значков

Проблема:  вы могли подумать, что проблема будет заключаться в большом размере, но на самом деле готовое приложение со всеми .dll занимает около 25 МБ, что меня вполне устраивает. Библиотека могла бы быть ещё лучше, если бы она была опенсорсной и можно было бы пользоваться статически скомпонованной версией для коммерческого использования (та же проблема, что и у Qt). Она не такая дорогая, как Qt (сейчас $310 за лицензию Indie), поэтому я бы заплатил и был счастлив. Но проблема в том, что, как видно на скриншоте (посмотрите на значки заголовка окна), рендеринг неидеален. У меня возникали всевозможные проблемы со сглаживанием шрифтов и изображений. Кроме того, что бы я ни делал, у окна сохранялась довольно толстая (2–3 пикселя) серая рамка, которую никак нельзя ни настроить, ни изменить.

WinForms / WPF

Если вы начнёте спрашивать на форумах про GUI-библиотеки C++ для Windows, то чаще всего вам будут говорить, что это плохая идея (не спорю с этим) и что вместо этого вам нужно писать фронтенд программы на каком-то другом стеке, а затем просто загрузить свою написанную на C++ функциональность как компонент/модуль. Это позволит легко стилизовать её и сильно ускорить разработку. Теоретически, можно получить единый .exe небольшого размера и использовать WinForms/WPF. Это возможно реализовать двумя способами:

  1. Встроить .dll как ресурс в приложение и заставить его извлекать библиотеку в какую-то временную папку, а затем использовать P/Invoke и вызывать скомпилированную .dll из приложения на C#/.NET.

  2. Использовать C++/CLI.

screenshot

Проблема:  .NET Framework поставляется в комплекте с Windows 10+, так что, строго говоря, это удовлетворяет требованию отсутствия зависимостей. Проблема в том, что при встраивании .dll его всё равно придётся куда-то извлекать и писать дополнительный код для работы P/Invoke, а C++/CLI компилируется в код .NET IL (иными словами, можно открыть получившееся приложение в dnSpy и увидеть код на C++, транслированный в эквивалент на C#); мне же нужно не это, а нативный код).

Решение?

Это лишь некоторые из рассмотренных мной вариантов. После длительных попыток работы со всевозможными библиотеками и даже написания собственных стилей MFC я осознал, что для простых приложений нет ничего более подходящего, чем Dear ImGui.

У неё есть недостатки, в основном возникающие при попытке создания сложных UI; кроме того, это UI не retained mode, а immediate mode, так что только для рендеринга UI с частотой 60 или более кадров нам придётся задействовать GPU-рендерер наподобие DirectX.

Однако библиотека отвечает всем остальным требованиям, потому что DirectX по умолчанию встроен в современные версии Windows.

screenshot

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

screenshot

Размер скомпилированного приложения ImGui AppKit

Скомпилированная программа весит всего 500 КБ и не требует ничего устанавливать, даже VC++ redistributable, если статически скомпоновать в него MFC.

Habrahabr.ru прочитано 2779 раз