Хотите писать фреймворки с графическим интерфейсом на Rust?

задняя часть Rust

Оригинальная ссылка:woohoo. from Nobody.net/blog/gui-put…

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

Я хотел бы попытаться прояснить это, сначала описав некоторые вещи, которые люди называют фреймворками/инструментариями GUI, а затем подробно остановиться на необходимых компонентах одной из классических фреймворков GUI для настольных компьютеров.

Хотя этот пост не относится конкретно к Rust, он исходит из Rust: он во многом основан на моем опыте работы с Druid, десктопной версией инструментария Rust GUI.

Как только у нас будет общее понимание проблемы, мы сможем лучше обсудить состояние этой работы в Rust, что станет предметом последующего поста.

О чем мы говорим, когда говорим о графическом интерфейсе

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

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

Таким образом, с одной стороны находятся инструменты для создания игр, встроенных приложений и (в меньшей степени) веб-приложений. В этом мире вы несете ответственность за предоставление почти всего, что нужно вашему приложению, и вы будете тесно взаимодействовать с базовым оборудованием: принимать необработанные входные события и выводить свой пользовательский интерфейс в какой-то буфер или поверхность. (Интернет отличается; здесь производитель браузера уже сделал всю работу по интеграции за вас.)

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

Игры и встроенные графические интерфейсы Прежде чем мы начнем копаться во всех интеграциях, которые вы можете ожидать от среды настольных приложений, давайте кратко поговорим о первом случае.

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

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

Игры похожи, плюс у них есть свои уникальные проблемы с производительностью и соображения, и я не имею права говорить о каких-либо реальных деталях.

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

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

Анатомия «родного настольного приложения»

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

Я хотел бы попытаться понять некоторые из основных необходимых точек интеграции и некоторые возможные методы, которые можно использовать для их предоставления.

оконный

Приложения должны создавать экземпляры окон и управлять ими. API должен позволять настраивать внешний вид и поведение окна, включая возможность изменения размера окна, наличие строки заголовка и т. д. API должен допускать несколько окон, а также поддерживать модальные окна и дочерние окна с соблюдением соглашений платформы. Это означает поддержку модальных окон приложения (таких как предупреждения, которые перехватывают фокус у всего приложения до тех пор, пока оно не будет обработано), а также оконных окон (предупреждения, которые перехватывают фокус у данного окна, пока оно не будет обработано). Модальные окна используются для реализации ряда общих функций, включая диалоги открытия/сохранения (вероятно, зависящие от платформы), оповещения, диалоги подтверждения и стандартные элементы пользовательского интерфейса, такие как поля со списком и другие раскрывающиеся меню (например, список завершения текстового поля). ).

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

Этикетка

Вам также нужна вкладка поддержки. Вы должны иметь возможность перетаскивать вкладки из группы вкладок для создания новых окон, а также перетаскивать вкладки между окнами. В идеале вы хотели бы использовать собственную инфраструктуру маркировки платформы, но... это сложно. Все браузеры развернули свои собственные реализации, и это, вероятно, не зря. Вы захотите уважать предпочтения пользователя в отношении вкладок (системы Mac позволяют нашим пользователям выбирать, какие вкладки открывать новые окна в масштабах всей системы), но это было бы дополнительным усложнением. Я прощу вас, если вы пропустите это, но если ваш фреймворк будет широко использоваться, кто-то будет сообщать о нем как об ошибке каждый месяц, пока вы не умрете, и они не ошибаются.

меню

Меню тесно связаны с окнами; настольные приложения должны соблюдать соглашения платформы относительно окон и меню приложений. В Windows (семейство операционных систем) меню является компонентом окна. В macOS меню — это свойство приложения, которое обновляется, чтобы отображать команды, доступные для активного окна. В линуксе все менее понятно. Если вы используете GTK, есть меню окон и приложений, хотя последнее устарело. Если вы нацелены на x11 или Wayland напрямую, вам нужно будет реализовать меню самостоятельно, теоретически вы можете делать все, что хотите, хотя самый простой способ — это оконное меню в стиле Windows.

Часто существуют четкие соглашения о том, какие меню должны быть представлены и какие команды должны в них появляться; хорошо работающее настольное приложение должно следовать этим соглашениям.

Рисование

Чтобы рисовать содержимое вашего приложения, вам нужен (как минимум) базовый API 2D-графики. Это должно обеспечить возможность заполнять и обводить пути (используя цвет, включая прозрачность, а также радиальные и линейные градиенты), размещать текст, рисовать изображения, определять области отсечения и применять преобразования. В идеале ваш API также предоставляет некоторые дополнительные функции, такие как режимы наложения и размытие, для теней и т. д.

Эти API существуют на разных платформах в немного разных формах. В macOS есть CoreGraphics, в Windows — Direct2D, а в Linux — Cairo. Таким образом, один из способов состоит в том, чтобы представить общую абстракцию API поверх API этих платформ, зарисовав шероховатости и заполнив пробелы. (Это наш текущий подход с использованием библиотеки piet.)

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

Было бы проще использовать везде один и тот же рендерер. Одним из вариантов может быть что-то вроде Skia, механизма рендеринга, используемого в Chrome и Firefox. Это имеет преимущества переносимости и согласованности, но за счет размера бинарного файла и времени компиляции; базовый размер релизной сборки бинарных файлов Rust с использованием крейта skia-safe составляет около 17 МБ (мой подход не очень хорош, но я думаю Это разумная база.)

Skia по-прежнему является довольно традиционным программным рендерером, хотя теперь он имеет значительную поддержку графического процессора. В конечном счете, самые захватывающие перспективы связаны с переносом большего количества задач рендеринга на GPU.

Первоначальная проблема здесь заключается в разнообразии API-интерфейсов, используемых для программирования графических процессоров, даже для одного и того же оборудования. Один и тот же физический графический процессор можно подключить через Metal на платформах Apple, DirectX на Windows и Vulkan на многих других платформах. Для обеспечения переносимости кода на этих платформах требуется повторная реализация, какая-либо форма кросс-компиляции или уровень абстракции. Проблема в последнем случае заключается в том, что сложно написать абстракцию, обеспечивающую достаточный контроль над высокоуровневыми функциями графического процессора (такими как вычислительная мощность), которые охватывают слегка различающиеся низкоуровневые API.

Как только вы поймете, как вы хотите взаимодействовать с оборудованием, вам нужно выяснить, как эффективно и правильно растеризовать 2D-сцену на графическом процессоре. Это также может быть сложнее, чем вы изначально подозреваете. Поскольку графические процессоры хорошо рисуют 3D-сцены, а 3D-сцены кажутся «более сложными», чем 2D-сцены, кажется естественным сделать вывод, что графические процессоры должны с легкостью обрабатывать 2D-сцены. они не. Методы растеризации, используемые в 3D, плохо подходят для 2D-задач, таких как обрезка по векторным путям или сглаживание, а те, которые дают наилучшие результаты, работают хуже всего. Что еще хуже, когда задействовано большое количество групп микширования или областей отсечения, эти традиционные методы могут ужасно работать в 2D, поскольку для каждой области требуется собственный временный буфер и вызовы отрисовки.

Есть многообещающая новая работа (например, piet-gpu), которая использует вычислительные шейдеры и может рисовать сцены с плавной и стабильной производительностью в моделях 2D-изображений. Это активная область исследований. Потенциальное ограничение заключается в том, что вычислительные шейдеры являются относительно новой функцией и доступны только в графических процессорах, выпущенных за последние пять лет или около того. Другие средства визуализации, в том числе WebRender, используемый Firefox, используют более традиционные методы и имеют более широкую совместимость.

анимация

Да, и еще: какой бы метод вы ни выбрали, вам также необходимо предоставить эргономичный и высокопроизводительный API анимации. Стоит подумать об этом заранее, попытка добавить его позже может раздражать.

текст

Независимо от того, как вы рисуете, вам нужно визуализировать текст. Фреймворки с графическим интерфейсом должны как минимум поддерживать форматированный текст, сложные сценарии, макет текста (включая такие вещи, как перенос строк, выравнивание и выравнивание, а в идеале такие вещи, как перенос произвольных путей). Вам нужно поддерживать эмодзи. Вам также необходима поддержка редактирования текста, в том числе поддержка написания справа налево и BiDi. Можно сказать, что это очень большое мероприятие. На самом деле у вас есть два варианта: связать HarfBuzz или использовать текстовый API платформы: CoreText в macOS, DirectWrite в Windows и, возможно, Pango + HarfBuzz в Linux. Существуют и другие альтернативы, в том числе многообещающие проекты на Rust (такие как Allsorts, rustybuzz и swash), но ни один из них не является достаточно полным, чтобы полностью заменить HarfBuzz или Platform Text API.

синтезатор

2D-графика является основной частью рисования, которую могут выполнять настольные приложения, но не единственной частью. Стоит упомянуть еще два распространенных случая: видео и 3D-графика. В обоих случаях мы хотим иметь возможность использовать доступное оборудование: для видео — аппаратный декодер H.264, а для 3D — графический процессор. Это сводится к указанию ОС встроить видео или 3D-представление в определенную область нашего окна, что означает взаимодействие с компоновщиком. Композитор — это компонент операционной системы, который берет отображаемые данные из различных источников (разные окна разных программ, воспроизведение видео, вывод графического процессора) и собирает их в целостную картинку рабочего стола.

Возможно, лучший способ понять, почему это важно для нас, — подумать о взаимодействии с прокруткой. Если у вас есть представление с возможностью прокрутки, и это представление содержит видео, вы хотите, чтобы видео перемещалось синхронно с остальной частью представления при прокрутке представления. Это сложнее, чем кажется. Нельзя просто определить область окна и встроить в нее видео, нужно как-то сказать ОС, чтобы видео двигалось синхронно с прокруткой.

браузер

Давайте не будем забывать об этом: рано или поздно кто-то захочет отобразить HTML (или настоящий веб-сайт!) в своем приложении. Мы действительно не хотим связывать весь движок браузера, чтобы сделать это, но использование веб-просмотра платформы также требует компоновщиков и значительно усложняет нашу жизнь в целом. Может быть, вашим пользователям вообще не нужен этот веб-представление? Во всяком случае, есть некоторые вещи, чтобы рассмотреть.

обработка ввода

Как только вы поймете, как управлять окном и как отрисовывать содержимое, вам нужно обрабатывать пользовательский ввод. Мы можем грубо разделить ввод на указатели, клавиатуры и другие элементы, например джойстики, геймпады и другие HID-устройства. Мы проигнорируем последнюю категорию и просто скажем, что это нормально, но не должно быть приоритетом. Наконец, есть входные события, исходящие от функций доступности системы; мы рассмотрим их, когда будем говорить о специальных возможностях.

Для событий указателя и клавиатуры есть относительно простой способ, а есть принципиальный, правильный, но гораздо более правильный.

ввод указателя

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

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

ввод с клавиатуры

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

Для ввода с клавиатуры простой способ очень прост: клавиши клавиатуры обычно связаны с символом или строкой, и когда пользователь нажимает клавишу, вы можете взять эту строку и вставить ее в позицию курсора в активном текстовом поле. . Это работает достаточно хорошо для одноязычных английских текстов и немного меньше, но по крайней мере один для языков с латиницей 1 в целом и шрифтов, которые ведут себя как латиница, таких как греческий, кириллица или турецкий. К сожалению (но не случайно), большое количество программистов в основном печатают только в ASCII, но большая часть мира этого не делает. Обслуживание этих пользователей требует интеграции с системами ввода текста и IME платформы, что является досадной проблемой, которая является одновременно важной и чрезвычайно громоздкой.

IME (сокращение от Input Method Editor) — это общий термин для специфичных для платформы механизмов, которые преобразуют события клавиатуры в текст. Для большинства европейских языков и письменностей процесс довольно прост, вам может потребоваться вставить не более одной гласной с ударением, но для восточноазиатских языков (китайский, японский и корейский, или вместе CJK) процесс намного сложнее. сложный , потому что и различные другие сложные сценарии.

Это сложно во многих отношениях. Во-первых, это означает, что взаимодействие между заданным текстовым полем и IME является двунаправленным: IME должен иметь возможность изменять содержимое текстового поля, но он также должен иметь возможность запрашивать текущее содержимое текстового поля в чтобы иметь надлежащий контекст для интерпретации события. Точно так же необходимо уведомлять об изменениях положения курсора или состояния выделения; нажатие одной и той же клавиши может привести к разным результатам в зависимости от окружающего текста. Во-вторых, нам также необходимо поддерживать актуальность IME в отношении расположения текстового поля на экране, поскольку IME часто предоставляет окно «кандидата» для возможного ввода для активной последовательности событий клавиатуры. Наконец (на самом деле не так, как в прошлый раз, я написал три тысячи слов, но это не сделано) Реализация IME кросс-платформенным способом очень сложна из-за различий в базовых API-интерфейсах платформы; macOS требует редактируемых текстовых полей для реализации протокола, а затем наличие обработки текстового поля для принятия и применения изменений из IME, в то время как Windows API использует механизм блокировки и освобождения; разработка абстракции для обоих методов является дополнительным уровнем сложности.

Существует дополнительная сложность, связанная с вводом текста: в macOS вам необходимо поддерживать текстовую систему Cocoa, которая позволяет пользователям указывать общесистемные привязки клавиш, которые могут выполнять различные команды редактирования текста и навигации.

Подводя итог: для правильной обработки ввода требуется много работы, и если вы этого не сделаете, ваш фреймворк в основном будет игрушкой.

Доступность

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

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

Интернационализация и локализация

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

Другие менее распространенные функции

В дополнение ко всем функциям, общим для большинства сред рабочего стола, есть также функции, зависящие от платформы, которые следует учитывать: некоторые из них носят стилистический характер, например API для добавления прозрачности или яркости частям окна или поддержка добавления дополнительных меню. bar или используйте расширение панели задач, или быстрый просмотр, или реализуйте элементы панели управления, или множество других вещей. Ваш фреймворк должен, по крайней мере, делать это возможным.По крайней мере, вы должны предоставить пользователям возможность напрямую использовать API платформы, чтобы они могли реализовать то, что вы не предусмотрели (или еще не обошли), когда им это действительно нужно.

комбинировать

Кажется, это разумное место, чтобы остановиться; я, конечно, упустил из виду некоторые вещи, но я надеюсь, что затронул самые важные вещи. Когда у вас есть представление о том, что вам нужно поддерживать и реализовывать, вы можете начать думать о том, как собрать все это воедино.

Разработка кроссплатформенных API

Одной из наиболее тонких и интересных задач при разработке фреймворков графического пользовательского интерфейса является разработка API. Здесь вы сталкиваетесь с очень специфической проблемой: вы пытаетесь разработать API, который предоставляет общий интерфейс для принципиально другого набора базовых API-интерфейсов платформы.

Хороший пример — меню вашего приложения. Как упоминалось ранее, Linux и Windows обычно предполагают наличие строки меню в каждом окне вашего приложения, в то время как в macOS есть строка меню, которая является компонентом среды рабочего стола, которая, когда ваше приложение активно, будет вашим меню приложения.

Чтобы справиться с этим наивно, у вас могут быть отдельные меню приложения и окна, а затем у вас может быть условный код для обновления одного или другого на основе условной компиляции или проверок во время выполнения. Однако это приводит к созданию большого количества дублированного кода и очень подвержено ошибкам. В данном конкретном случае, я думаю, есть довольно понятный и довольно простой API, который работает на обеих платформах. В вашей среде вы рассматриваете меню как свойство окна: в Windows и Linux это действительно так, поэтому нет проблем, тогда в macOS вы устанавливаете меню приложения как меню активного в данный момент окна и меняете его как Windows приобретает или теряет активность по мере необходимости.

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

веб-просмотр

Некоторые из основных кроссплатформенных графических интерфейсов успешно решили все эти сложности платформы со всеми их тонкими недостатками дизайна, отсутствующей документацией и загадочными ошибками: основные браузеры, Chrome, Firefox и (все чаще) Edge. беспокойтесь об этом, потому что это не кросс-платформенный.)

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

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

Родной внешний вид

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

Нативное поведение относится ко многим вещам, которые мы уже обсуждали, и к некоторым другим. Вот некоторые примеры поведения при прокрутке: Учитывает ли ваше приложение предпочтения пользователя в отношении прокрутки? Имеет ли ваше приложение ту же кривую ускорения при прокрутке, что и представление прокрутки платформы по умолчанию? Обрабатывает ли ваше приложение стандартные системные сочетания клавиш, например разворачивание или скрытие окон? Метод ввода работает? Это также распространяется на другие, менее очевидные соглашения: хранит ли приложение пользовательские данные в обычном местоположении текущей платформы? Использует ли он диалог открытия/сохранения системных файлов? Показывает ли он ожидаемое меню с ожидаемыми пунктами меню?

Эти вещи более важны на некоторых платформах, чем на других. В частности, на Mac важно правильно определить эти поведенческие детали: Mac в большей степени, чем другие платформы, разработан с учетом определенных соглашений, которых разработчики приложений для Mac исторически старались придерживаться. Это, в свою очередь, помогает создать сообщество пользователей, которые ценят и чутко относятся к этим соглашениям, и их нарушение неизбежно приведет к разрушению этой группы. В Windows все немного проще: исторически сложилось, что для Windows существует широкий спектр программного обеспечения, и Microsoft никогда не была столь догматична в отношении того, как приложения должны выглядеть и вести себя, как Apple.

Родной внешний вид больше относится к внешнему виду приложения. Ваши кнопки выглядят как родные кнопки? У них одинаковый размер и градиент? Используете ли вы элементы управления, ожидаемые платформой для данного взаимодействия, такие как предпочтение флажков на рабочем столе и переключателей на мобильных устройствах?

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

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

Суммировать

Я надеюсь, что этот справочник поможет хотя бы смутно определить масштаб проблемы. Ничто из того, что я здесь описал, не является невозможным, но сделать все это, и сделать это хорошо, — немалая работа.

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