![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Осваиваю Iced, библиотечку для построения графических интерфейсов на языке Rust. В качестве примера сделал простейшую программку с тремя кнопками. Вроде ничо так выходит. Выложу здесь, вдруг кому пригодится.
Наше приложение будет иметь три кнопки и текстовую фразу, отображаемую в окне. Фраза будет меняться при нажатии кнопок.
Сообщение это очень простая штука: значение типа enum. Но в Rust, в отличие от Си/Си++, enum хранит не только целочисленное значение (селектор вариантов), но и для каждого варианта может иметь дополнительные параметры. Фактически это вариантная запись в смысле Паскаля.
В нашем приложении будут сообщения только одного типа Phrase, но с параметром: текстом фразы.
В качестве изначальной фразы ставим традиционное "Hello, World!".
Строка не обязана быть константой: она может меняться по мере необходимости.
Создаём объект content типа iced::Row из двух элементов. Он делит область окна на две части: левую и правую.
В левой части помещаем объект canvas типа iced::Container. Он будет центрировать нашу текстовую фразу. Внутри него ставим собственно фразу: объект типа iced::Text. Зададим тексту размер побольше.
В правой части окна создаём объект controls типа iced::Column. В него поместим три кнопки типа iced::Button. Для каждой кнопки определяем, какое сообщение мы хотим отправить, когда юзер кликнет по кнопке. Сообщение будет содержать в себе текст фразы.
Бывает так, что в процессе обработки одного сообщения появляется необходимость сгенерить другое сообщение для приложения.
В этом случае метод update() возвращает новую команду для обработки. В нашем случае возвращаем пустую команду.

Кликаем на кнопку "Приветик". Текст в окне меняется.

Собственно, это всё, что я имею сказать по поводу Iced. Подробности читайте на сайте проекта: https://docs.rs/iced
Вышеприведённые тексты выложены на Гитхабе: https://github.com/sergev/iced-demo
Главная программа
Выполнение начинается с main(), как обычно. Вызывается метод run() от класса MyApp, который определим позже. Этот метод мы унаследуем от интерфейса iced::Application: он создаст собственно экземпляр приложения и запустит его. Установки приложения (размеры, стили и прочее) передаются в этот метод в виде параметра. Мы используем дефолтные значения, для простоты.Здесь мы вынуждены написать "use iced::Application;", чтобы дать доступ компилятору к интерфейсу (trait) Application. Иначе вызов MyApp::run() не пройдёт, так как здесь происходит приведение типа MyApp к Application.use iced::Application;
pub fn main() -> iced::Result {
MyApp::run(iced::Settings::default())
}
Класс MyApp
Теперь определим класс MyApp, точнее его поля, хранящие состояние. В Rust нет отдельного понятия "class". Классы это структуры плюс интерфейсы (предопределённые наборы методов). Начинаем со структур.Наше приложение будет иметь три кнопки и текстовую фразу, отображаемую в окне. Фраза будет меняться при нажатии кнопок.
Хитрая строка "#[derive(Default)]" здесь означает, что для класса MyApp мы хотим определить интерфейс Default. Он добавляет метод default() для инициализации полей значениями по умолчанию (обычно нулями). Но мы ленимся определять метод default() сами, и за нас это сделает стандартный макрос derive(). Он просмотрит исходник нашей структуры и автоматически сгенерит код метода default() с инициализацией всех полей.#[derive(Default)]
struct MyApp {
button1_state: iced::button::State,
button2_state: iced::button::State,
button3_state: iced::button::State,
phrase_selected: String,
}
Сообщения
В Iced, когда пользователь взаимодействует с приложением (кликает мышкой, к примеру), графические элементы (кнопки и прочие) порождают специальные объекты, называемые сообщениями, и отправляют их приложению на обработку (вызовом метода update()).Сообщение это очень простая штука: значение типа enum. Но в Rust, в отличие от Си/Си++, enum хранит не только целочисленное значение (селектор вариантов), но и для каждого варианта может иметь дополнительные параметры. Фактически это вариантная запись в смысле Паскаля.
В нашем приложении будут сообщения только одного типа Phrase, но с параметром: текстом фразы.
Здесь мы тоже добавляем магическое заклинание "#[derive(Debug, Clone)]", чтобы автоматически создать для класса Message интерфейсы Debug и Clone. Интерфейс Debug позволяет печатать сообщения через стандартный println, а Clone - создавать копии объектов Message.#[derive(Debug, Clone)]
pub enum Message {
Phrase(String),
}
Интерфейс Application
Собственно создание приложения состоит в реализации для нашего класса MyApp интерфейса iced::Application. Надо определить три типа и четыре метода:- type Message - энум для посылки сообщений, как описано выше
- type Executor - внутренняя кухня Iced, для нас здесь несущественно
- type Flags - аналогично
- fn new() - конструктор, создающий экземпляр приложения
- fn title() - определяет титульную строку для главного окна
- fn view() - строит визуальное представление главного окна
- fn update() - принимает сообщение и изменяет состояние приложения
impl iced::Application for MyApp {
type Message = Message;
type Executor = iced::executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Self, iced::Command<Message>) {
...
}
fn title(&self) -> String {
...
}
fn update(&mut self, message: Message, _cb: &mut iced::Clipboard) -> iced::Command<Message> {
...
}
fn view(&mut self) -> iced::Element<Message> {
...
}
}
Начальное состояние
Статический метод new() создаёт объект класса MyApp. Ровно то же самое, что конструктор в Си++. Но кроме самого объекта, он дополнительно возвращает "команду", содержащую сообщение, требующее обработки в момент старта. Здесь мы возвращаем пустую команду.В качестве изначальной фразы ставим традиционное "Hello, World!".
fn new(_flags: ()) -> (Self, iced::Command<Message>) {
(
Self {
phrase_selected: "Hello, World!".to_string(),
..Self::default()
},
iced::Command::none(),
)
}
Титульная строка
Метод title() возвращает текст, которая будет служить титульной строкой главного окна.Строка не обязана быть константой: она может меняться по мере необходимости.
fn title(&self) -> String {
"MyApp - Iced".to_string() }
Компоновка главного окна
Задача метода view() - построить и вернуть структуру данных, определяющую компоновку и внешний вид графических элементов главного окна приложения.Создаём объект content типа iced::Row из двух элементов. Он делит область окна на две части: левую и правую.
В левой части помещаем объект canvas типа iced::Container. Он будет центрировать нашу текстовую фразу. Внутри него ставим собственно фразу: объект типа iced::Text. Зададим тексту размер побольше.
В правой части окна создаём объект controls типа iced::Column. В него поместим три кнопки типа iced::Button. Для каждой кнопки определяем, какое сообщение мы хотим отправить, когда юзер кликнет по кнопке. Сообщение будет содержать в себе текст фразы.
fn view(&mut self) -> iced::Element<Message> {
let message = iced::Text::new(&self.phrase_selected)
.size(96);
let canvas = iced::Container::new(message)
.width(iced::Length::Fill)
.height(iced::Length::Fill)
.padding(20)
.center_x()
.center_y();
let controls = iced::Column::new()
.padding(10)
.spacing(20)
.push(iced::Button::new(&mut self.button1_state, iced::Text::new("Hi there!"))
.on_press(Message::Phrase("Hi there!".to_string())))
.push(iced::Button::new(&mut self.button2_state, iced::Text::new("Hola!"))
.on_press(Message::Phrase("Hola!".to_string())))
.push(iced::Button::new(&mut self.button3_state, iced::Text::new("Приветик!"))
.on_press(Message::Phrase("Приветик!".to_string())));
let content = iced::Row::new()
.spacing(20)
.push(canvas)
.push(controls);
content.into()
}
Обработка сообщений
Метод update() будет вызван, когда юзер кликнет мышкой по одной из кнопок. Параметр message содержит сообщение от кнопки. Внутри сообщения находится текст. Заменим отображаемую фразу на этот текст.Бывает так, что в процессе обработки одного сообщения появляется необходимость сгенерить другое сообщение для приложения.
В этом случае метод update() возвращает новую команду для обработки. В нашем случае возвращаем пустую команду.
Приложение готово. Компилируем и запускаем:fn update(&mut self, message: Message, _cb: &mut iced::Clipboard) -> iced::Command<Message> {
match message {
Message::Phrase(phrase) => {
self.phrase_selected = phrase;
}
}
iced::Command::none()
}
$ cargo run
Compiling libc v0.2.109
...
Compiling iced v0.3.0
Compiling demo v0.1.0 (/Users/vak/demo_iced)
Finished dev [unoptimized + debuginfo] target(s) in 33.21s
Running `/Users/vak/demo_iced/target/debug/demo`

Кликаем на кнопку "Приветик". Текст в окне меняется.

Собственно, это всё, что я имею сказать по поводу Iced. Подробности читайте на сайте проекта: https://docs.rs/iced
Вышеприведённые тексты выложены на Гитхабе: https://github.com/sergev/iced-demo
no subject
Date: 2021-12-11 05:17 (UTC)Забавно, что не пришлось трахаться со всеми этими займами, со всей этой линейной логикой.
no subject
Date: 2021-12-11 05:29 (UTC)no subject
Date: 2021-12-11 05:50 (UTC)Да я пробовал делать projecteuler на расте, но что-то меня достала эта линейная логика; все приходится доставать где-то. Наживать непосильным трудом.
no subject
Date: 2021-12-11 09:23 (UTC)каждый раз откат обратно к пресмыкающимся :о((
no subject
Date: 2021-12-11 09:58 (UTC)no subject
Date: 2021-12-11 10:13 (UTC)no subject
Date: 2021-12-11 09:36 (UTC)no subject
Date: 2021-12-15 19:09 (UTC)https://github.com/ocornut/imgui для C++
https://github.com/emilk/egui для Rust
https://github.com/Immediate-Mode-UI/Nuklear для C
no subject
Date: 2021-12-15 20:40 (UTC)