LINUX.ORG.RU

Universal UI for Rust

 , , ,


4

4

Доброго времени суток.

Примерно полгода назад, когда я начинал изучать Rust, я обнаружил в нем фатальный недостаток - отсутсвие крейта, который бы реализовывал удобный и кроссплатформенный UI. Хотелось поддержки Linux/Windows/MacOS/WASM-web/Android/iOS и удобства использования уровня Qt с их сигналами-слотами.

В ходе парочки бессоных ночей (на самом деле намного больше) появилась некоторое количество крейтов, которые позволяет писать UI вот так:

#[derive(UWidget)]
struct MyWidget {
    #[uwidget]
    layout: LinearLayout,

    #[public_slot] // у структуры будет публичный метод, через который слот будет доступен снаружи
    #[uprocess(on_text_updated)]
    slot_set_text: SlotImpl<String>,

    slotproxy_set_text: SlotProxy<String>,

    #[uprocess(on_button_clicked)]
    slot_button_clicked: SlotImpl<()>,
}

impl MyWidget {
    pub fn new(placeholder: String) -> MyWidget {
        let slotproxy_set_text;
        let slot_button_clicked = SlotImpl::new();

        let mut layout = LinearLayout::new(Orientation::Horizontal);
        layout.push_widget({
           let textedit = TextEdit::new(placeholder);
           slotproxy_set_text = textedit.slot_set_text().proxy();
           textedit
        });

        layout.push_widget({
            let mut button  = Button::new("Push me");
            button.signal_clicked().connect(&slot_button_clicked);
            button
        });

        return MyWidget{
            layout,
            slot_set_text: SlotImpl::new(),
            slotproxy_set_text,
            slot_button_clicked,
        };
    }

    fn on_text_updated(&self, s: String) {
        self.slotproxy_set_text.exec_for(s);
    }

    fn on_button_clicked(&self, _:()) {
        info!("button clicked!");
    }
}



В ближайшем будущем планирую еще немного посидеть над макросами, чтобы можно было писать вот так:

pub fn new(placeholder: String) -> MyWidget {
    let layout = layout!(
        orientation: Horizontal,
        widget: text_edit!(
        	placeholder: placeholder,
        	slot_set_text: proxy!(slotproxy_set_text),
        ),
        widget: button!(
        	signal_clicked: connect!(slot_button_clicked),
        ),
    );

    return MyWidget{
        layout,
        slot_set_text: SlotImpl::new(),
        slotproxy_set_text,
        slot_button_clicked,
    };
}


Немного технических подробностей:
* Уже работает для Linux (Qt) и WASM (web-sys).
** В ближайших планах минимальная поддержка Windows/MacOS через Qt и поддержка Android (Java-jni, нативные виджеты).
** В среднесрочных планах поддержка нативных виджетов Win/Mac, поддержка iOS.
* Система сборки на build.rs-скриптах для cargo, которая позволяет из одних и тех же исходников собирать проект для любой платформы. Небольшой (2-5 строк) скрипт придется написать самому, используя готовые функции из библиотеки.
* В самой библиотеке есть еще Property, которые умеют хранить состояние и обладают встроенными сигналами/слотами. И немного других улучшалок.
* Никаких unsafe в интерфейсах для пользователя (внутри - только для работы с native-кодом).
* Никаких RefCell, Rc и прочих Arc в интерфейсах. Внутри их тоже практически нет, к релизу хочу выпилить совсем.


В основном вдохновлялся Qt, но изначально все писалось под web-sys, с последующим прикручиванием Desktop'а.


Тут хотелось бы обсудить 2 вещи (на самом деле 3):
1. Интересна ли вам такая библиотека.
2. Насколько удобным выглядит интерфейс.
3. Насколько Rust ущербен, порекомендуйте уже готовые библиотеки, и вот это вот все.

P.S. Буду держать вас в курсе, так что можете сразу ставить uniui тег в игнор.

★★★★★

Добро пожаловать в число тех, кто пилит очередной GUI фреймворк на расте. Таких как ты много.

Перед тем, как ты начнёшь рассказывать, чем твое решение принципиально лучше, спрошу: у тебя там можно руками рисовать кастомные виджеты, или всё как обычно у писак фреймворков?

quantum-troll ★★★★★ ()
Ответ на: комментарий от quantum-troll

у тебя там можно руками рисовать кастомные виджеты

Есть поддержка canvas и opengl/webgl.

Каждый виджет лежит в отдельном крейте, чтобы не быть связаным c внутренностями core-части.

trex6 ★★★★★ ()
Последнее исправление: trex6 (всего исправлений: 2)

Пристально слежу за темой, но воз и ныне там. Проще всего писать на Qt, а Rust дергать через json-rpc. Все текущие реализации гуи - просто унылый ужас.

RazrFalcon ★★★★★ ()

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

zendrz ()

Примерно полгода назад, когда я начинал изучать Rust, я обнаружил в нем фатальный недостаток - отсутсвие крейта, который бы реализовывал удобный и кроссплатформенный UI.

Это не недостаток не Rust, ни какого-либо иного языка программирования.

Владимир

anonymous ()

Метапрог всех «заткнет за пояс» /у него обширные планы/.
Разработчики QT, GTK, … еще не догадываются, что скоро им придет - …

«Гусарам молчать!» /мой вариант «капут»/.

Владимир

anonymous ()

GUI надо отделять от ядра приложения, это многие пишут. Только отделять можно и на другом ЯП с нормальными тулкитами, типа питона, QML, да хоть HTML/CSS/JS в браузере, как делает CUPS.

ArkaDOSik ()
Ответ на: комментарий от RazrFalcon

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

WatchCat ★★★★★ ()
Последнее исправление: WatchCat (всего исправлений: 1)

Сам не знаю для чего, но решил изучить и попробовать rust для своих gui поделок, которые сейчас на qt

Но отсутствие аналога qt меня и остановило, а может мне оно и не надо

x905 ★★★★★ ()
Последнее исправление: x905 (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Все текущие реализации гуи - просто унылый ужас.

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

trex6 ★★★★★ ()
Ответ на: комментарий от zendrz

Насколько я помню, автор druid использует его для редактора шрифтов. Возможно и другие библиотеки так же создаются не на ровном месте.

Я свою начал писать для визуализации игры.

trex6 ★★★★★ ()
Ответ на: комментарий от WatchCat

Благо делается это там достаточно легко.

До тех пор, пока вам не нужно пересылать что-то сложнее int/float. Даже со строками начинаются «пляски», а со сложными/древовидными структурами данных - совсем все плохо. Плюс придется обмазываться unsafe по всему коду.

Как результат, для конкретной билиотеки, обычно, пишут отдельную обвязку, чтобы весь «секс» локализировать в одном крейте. Ну а крейт этот потом уезжает на crates.io.

trex6 ★★★★★ ()
Ответ на: комментарий от fsb4000

И еще десяток других есть. Но мне интерфейсы нигде не понравились.

Хочу чтобы все было так же просто, как Qt с их сигналами-слотами. Пропихивать повсюду лямбды/FnMut-ы - не мое.

trex6 ★★★★★ ()
Ответ на: комментарий от trex6

а со сложными/древовидными структурами данных - совсем все плохо.

Ну я кагбе изначально говорил про простые структуры.
То что можно описать обычным struct без указателей.
Т.е. массивы, структуры, массивы структур и т.п.

А если у тебя всё так сложно и ты в ui передаёшь что-то выходящее из этих рамок, то КМК тут проблемы в изначальном проектировании.

WatchCat ★★★★★ ()

#[public_slot] // у структуры будет публичный метод, через который слот будет доступен снаружи

Очевидно, что никаких методов особенно публичных у тебя нет.

let slot_button_clicked = SlotImpl::new(); connect(&slot_button_clicked)

Очевидно, что это не сработает.

  • Никаких unsafe в интерфейсах для пользователя (внутри - только для работы с native-кодом).

Зачем ты врёшь?

Но ты пили, потом покажешь.

anonymous ()
Ответ на: комментарий от trex6

Так оно уже работает.

Оно не может работать в таком виде. Ты не можешь реализовать коннекты не через unsafe и не может их реализовать через ссылки.

Тебе там нужно костылять целый гц, что-бы это в каком-то виде работало полноценно.

anonymous ()