Как стать автором
Обновить

Комментарии 55

Здравствуйте! Спасибо за статью!
Небольшой совет, для улучшения читаемости код лучше поместить в специальный блок:

Приветствую!

Да вроде так и делал, все что можно было в блоки код добавил.. но подсветки синтаксиса почему-то нет. Возможно там как-то надо указать ему что это вот c++, а это json

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

Да, вам нужно выбрать нужный язык в выпадающем списке:

При попытке скачать демо-приложение с сайта https://libwui.org/ Windows Defender сказал, что обнаружен вирус

Видимо, это из-за того что оно просто не подписано. Собиралось на чистой виртуалке точно без вирусов.

Я тебе верю ?

Никаких проблем собрать самому ?
А так это не вирус, просто ругается что скачан подозрительный неподписанный exe. Еще лет 10 назад, все такие были )

Гигантская работа...
Хотел спросить, а почему был выбран подход sleep в случае GNU/Linux?

Там же есть poll, epoll, т.е. можно "нативным" образом "ждать" не жертвуя временем в функции ожидания. Там какой-нибудь контроллер сработал и пнул пулл событий, тогда поток проснулся и обработал их. Под Windows что-то похожее было типа WaitForMultipleObjects

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

НЛО прилетело и опубликовало эту надпись здесь

Приветствую.

Покритикую немного:

  • код примера окна написан плохо: #ifdef _WIN32 int APIENTRY wWinMain и тд пользователю это зачем. Если так придется писать везде, то не представляю как будет выглядеть приложение от 10к строк

  • в репе код тоже страшен

  • нет разделения кода для разных ОС, все склеено как и в примере #ifdef по месту

  • класс window.cpp весь в одном файле и занимает 3к строк кода, все остальные классы так же устроены

  • есть такая вещь как регистр символов, у вас классы, структуры, обычные переменные, статич переменные и тд - все в нижнем регистре. Как вы их отличаете?

  • что еще.. ладно

В общем, обычная поделка, долго не протянет.

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

Совет: возьмите wxwidget, добавьте туда удобную настройку графики (аналогично css на вэбе, или qss в qt), мне вот только ее не хватало, когда работал с wxwidget, остальное там все уже есть.

Вы не зрите в корень... wxWidgets с дизайном из начала 90х, против максимально приятного modern c++ да ещё и без шаблонной жести...

Насчёт "поделия", то это сделано для коммерческого продукта который решает боевые задачи и зарабатывает деньги компании.

Что до одного файла - "по уму" надо сделать "стратегии" а ля Александреску, но прямой путь зачастую выгоднее и проще в плане онборднига. Это все детали реализации.

Я же хотел привлечь внимание к тому, что UI может быть более прямым чем все мы привыкли.

wxWidgets с дизайном из начала 90х внутри дизайн из 90х вы имеете ввиду, и что, его не надо переписывать, он работает, обкатан на большем количестве пользователей, чем ваше изделие. А снаружи wxWidgets не хватает только удобной настройки стилей виджетов, больше ничего. Шаблонной жести там тоже не много. Ладно, дальше не буду спорить.

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

Эхх, были бы эти "личные договоренности" я бы донаты не собирал ))

Про дизайн из 90х я имел ввиду прежде всего дизайн интерфейсов. А дизайн wxWidgets/MFC/WTL строился вокруг системного API во время ещё до c++98...

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

о каком "modern C++" идет речь, если вы из конструктора исключение не выбрасываете?

и писать кросс платформу на #ifdef... ну это уровень студенческого диплома

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

есть такая вещь как регистр символов, у вас классы, структуры, обычные переменные, статич переменные и тд - все в нижнем регистре. Как вы их отличаете?

вы зря. В C++ нет де-юре официального code style, каждый проект имеет право использовать то, что нравится больше всего.

99% библиотек, которые мне нравятся, используют snake_case. Это, так же, требования boost code style, которого и придерживаемся.

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

Соглашусь с критикой. Директивы условной компиляции типа #ifdef __WIN32 и им подобные, подразумевают что между ними будет платформозависимый код. Таки и смысл перекладываться подобную условную компиляцию на плечи пользователя библиотеки? Не грамотнее было бы сокрыть платформенно зависимые вещи внутри кода своей библиотеки? О какой такой "кроссплатформенности" при таком подходе можно заявлять?

Все #ifdef _WIN32 приложения (не библиотеки) имеются только в файле где находится точка входа int main(...)
Можно сделать два файла а-ля: main_win.cpp, main_lin.cpp и обойтись вообще без #ifdef _WIN32 платой за это, станет дублирование части (довольно хорошей) кода функции main()

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

Весь пользовательский код, пишется в классах реализации окон, и там никаких #ifdef _WIN32 обычно нет. Ну если вам не нужно, например, работать с аудио / видео, так как системные API очень отличаются. Тут мы уже делаем как раз camera_lin.cpp / camera_win.cpp потому что дублирования кода практически нет.

Как на линуксах с пользовательскими настройками? Ну иконки/темы в пролете, я так понимаю, да и ладно, а что насчёт масштабирования на high DPI?

Смотрите, размер окна и все координаты контролов отображается 1 в 1 на экране, так как задается в пикселях. Т.е. с этим проблем нет, приложение имеет одинаковую геометрию на Win (при масштабировании 100%) и на Lin. Одно но - при использовании одинаковых шрифтов.

Я на Линукс затащил шрифты с Винды и кайфую )) тот же Телеграм в 100 удобнее читать стало. При использовании например Open Sans вместо Segoe UI, размер 14 Segoe выглядит на Linux + Open Sans на все 18. Это пока решаем поставкой тем для лиункса с уменьшенными размерами шрифтов на 4.

Самым грамотным решением, я считаю, как сделано в lvgl - тащим шрифты с собой и получаем, хоть на bare metal, пиксель в пискель то что мы хотим.

Так вот и проблема в том, что пиксель в пиксель, и при использовании масштабирования приложение будет выглядеть коряво. Вот как выглядит Java Swing приложение - часть элементов масштабируется, часть нет.

Скриншоты
Большое разрешение с масштабированием, Swing приложение выглядит криво
Большое разрешение с масштабированием, Swing приложение выглядит криво
Большое разрешение без масштабирования, все приложения выглядят одинаково
Большое разрешение без масштабирования, все приложения выглядят одинаково
Разрешение поменьше без масштабирования, все приложения выглядят одинаково
Разрешение поменьше без масштабирования, все приложения выглядят одинаково

В вашем случае скорее всего все элементы останутся неизменными и будут выглядеть мелко.

Весьма круто, надеюсь сделаете на macOS чтобы работало. Делали ли уже публикацию на Reddit? Стоит попробовать собрать донаты на уровне стран первого мира - тут вам максимум копейки накидают - разве что как-то подсесть на гос. заказы.

Да какие донаты, это как в деда мороза верить )

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

Публикация пока первая, Реддитом займемся ?

Про wx: если вы не любите кошек, то это значит, что вы не умеете их готовить.

Я всех люблю ?
Посмотрите лучше: https://lvgl.io/
wx отдыхает, как и wui ?

static constexpr const char* IMG_LOGO = "logo.png";

Для полного счастья надо было ещё unsigned добавить. Вы кажется не очень понимаете, как работают constexpr выражения.

Планируется ли сделать нормальную архитектуру, чтобы пользователь мог сделать какой-нибудь gui->run() и не обмазываться миллионом платформенных дефайнов?

Планируется ли перейти на string_view вместо string& ?

Спасибо за замечания по constexpr ?

По поводу платформенных кишков с ifdef вопрос уже решен - создана новая подсистема framework.

Теперь минимальный hello_word выглядит как-то так:

#ifdef _WIN32
int APIENTRY wWinMain(_In_ HINSTANCE,
    _In_opt_ HINSTANCE,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
#elif __linux__
int main(int argc, char *argv[])
#endif
{
    wui::framework::init();

    // Здесь код инициализации конфига, темы и локали

    MainFrame mainFrame;
    mainFrame.Run();

    wui::framework::run();

    return 0;
}

MainFrame.cpp:

window->init(wui::locale("main_frame", "caption"), { -1, -1, width, height },
        wui::window_style::frame, [this]() { 
          wui::framework::stop(); 
        });

Сейчас еще сделаю селектор для конфигов и обновлю статью.

Вам, и всем кто указал на эту недоработку огромная благодарность ?

Существенно переписал раздел "Главный цикл приложения". Теперь #ifdef'ы не торчат и все красиво. Спасибо еще раз за критику.

По stringview нужно подумать, скорее всего если c++14 на XP соберется то сделаем.

Думал cpp17, но ладно.

Последний из тулчейнов 2017 студии с поддержкой xp, вполне себе С++17 умеет

Дык ежели вы все эти HInstance не используете, то простой `int main()` должен завестись без проблем

1>LIBCMTD.lib(exe_winmain.obj) : error LNK2019: unresolved external symbol WinMain referenced in function "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)


К сожалению, на msvc так не работает.

почему сразу делалось не на Qt [...] это связано с [...] любовью к монолитным exe

Можно собрать статический Qt и линковать с ним, будет тоже монолитно.

Кстати, интересно, какой у вас получился финальный размер релизного бинарника/exe? Статический Qt (Quick, не Widgets) с лёгкостью бы накинул 20-30 МБ для среднего приложения (одно окно, простые/стандартные графические эффекты, сеть).

10 Mb x64, 8 Mb win32

>while (runned
Кажется вам не только Qt не нравится, но английский язык тоже, нужно переписать таблицы сильных глаголов! :)

/pedantic-mode Неправильных глаголов) Сильные - это другое.

Извините, я первым учил немецкий, поэтому диск для меня навсегда ц, а эти глаголы - сильные)

В пикселах - это, конечно, хорошо. На мониторах 4К мелко не будет это всё?

Будет мелко, однозначно. Поддержка DPI и svg скоро, надеюсь, будет.

Пока есть workaround - сделать 4K темы. Контролы автоматически увеличатся под текст. Но это такое, да...
Еще надо декларативный язык для создания UI и редактор...

... и получить на выходе Qt! /s

4K это такая штука, про которую если на этапе проектирования забыть/забить, то потом придется всё перелопачивать.

Спасибо!

Я пока думаю следующее:
1. Для ряда задач нужно пиксель в пиксель (условный embedded)
2. Для десктопа нужна читаемость/попадаемость мышью, т.е. нужен некий scaling.

Возможно я что-то упускаю, как я сейчас думаю это сделать:
2.1. Простой случай: просто умножаем координату/размер на scale factor
2.2. Для сложных приложений возможно лучше отдельное UI представление с другой компоновкой для 4К. Т. е. если у нас стало в 4 раза больше экрана, а кнопок у нас как в условном автокаде, то имеет смсыл показать больше кнопок/инструментов.

Вообще интересно что вы думаете про подводные камни 4К, и какие есть пути решения...

просто умножаем координату/размер на scale factor

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

Достойно. В своих проектах тихонько отхожу от Qt . По максимуму использую стандартные библиотеки (например std::thread вместо QThread, контейнеры, типы (qint64 в std::int64_t). Выбросил сигналы и слоты (замена на std:: functional и лямды). В результате собираю проект без moc компилятора.

Все виджеты из-за особенностей свои (отрисовка только QPainter).

Тоже хочется свой велосипед сделать в интерфейсе.

Достойно. В своих проектах тихонько отхожу от Qt . По максимуму использую стандартные библиотеки (например std::thread вместо QThread, контейнеры, типы (qint64 в std::int64_t). Выбросил сигналы и слоты (замена на std:: functional и лямды). В результате собираю проект без moc компилятора.

Я тут тоже подумываю как с Qt уйти, ибо не бесплатный он (постоянно все об этом напоминает). У меня небольшой вопрос: как из лямбды послать в другой поток данные. Есть там нюансы? То есть раньше я использовал сигнал/слот и он потоко-безопасный был. А теперь лямбдой как?

Ну лямбда не меняет контекст треда. В WUI события (и порождаемые ими вызовы лямбд) происходят из треда обрабатывающего event loop

"Методы, которые могут завершиться ошибкой возвращают bool..."

Для 2023 это как-то уже не камильфо... Как привет из древности)) Есть же промисы/фигомисы, какие-то современные концепции, которые за счет С++ inline будут достаточно эффективны. Эти IF с поверками кода ошибок, как крик из девности ))

Гора "wui::locale()" выглядит так себе...
Можно было бы хотя бы локальные макрос/inline функция вставить, чтобы это выглядело покороче, например LT("main_frame", "caption"), а еще лучше LT("main_frame/caption")

Опять гора static_cast выглядит тоже ободряюще )) Может как-то можно избавиться от необходимости в этих кастах.
Я думаю, вы со временем привыкли игнорировать эти мусорные части кода, но для неподготовленных (привыкших писать/читать лаконичный код на Kotlin, scala и т.д.) это выглядит так себе.

В примерах на С++20 можно применять using enum wui::menu_item::menu_type; (или какие там у вас тип), чтобы избежать этой боли для глаз с длинными дублирующимися выражениями wui::menu_item_state::normal, wui::menu_item_state::normal, wui::menu_item_state::separartor.

По поводу Mac: пользователи Mac любят, чтобы выглядело как на Mac... поэтому ваши самоотрисовки выбор для Mac не очень удачный. Эта же проблема была с Java Swing под Mac и Windows XP, отрисовать так красиво как нативно не получилось и обычным разбалованным пользователям это не очень нравилось.
Просто для медицины (и для внутренних корпоративных приложений) это не важно как и что отрисовано (главное чтобы работало), а вот для обычных приложений (у которых есть конкуренты) это может быть очень важно.

PS: В молодости тоже страдал чем-то эдаким (только без само-отрисовки на С++), а ля что-то своё, но более С++-ое и современное и более маленькое чем MFC. А с отрисовкой писал под Java 1.1 (частично крал код и/или идеи из Java Swing) :-) ))))

Справедливо, будем работать над этим 👍

Глянул сейчас репу. Круто, улучшения весьма заметные!

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

Да, лицензия boost. Нужно бы ещё компонентов дописать... Если есть желание, присоединяйтесь!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории