RustでGUI 〜iced編〜


プログラム全体はgithubのogata-k/GUI_cmp/example_icedをご覧ください。

iced

今回使用するバージョンは0.1.0-betaです。

どんなクレートか

icedはバージョンにbetaとあるようにまだまだ開発中のGUIライブラリです。READMEによるとElmにインスパイアされて作られたクロスプラットフォーム向けのライブラリです。Elmを参考にしているだけあってコードの見た目もElmに見えます。
珍しいことにWebアプリもサポートしてるとのことです。さらに各種基本的なレンダーライブラリの対応や非同期も対応しています。

コード

main.rs
use iced::{Sandbox, Element, Button, Column, Text, Settings, Container, Length, Align, HorizontalAlignment, Color, Background, button};
use iced::settings::Window;

pub fn main() {
    Counter::run(Settings {
        window: Window {
            size: (300, 300), // (x, y)
            resizable: false,
        }
    })
}

// state
struct Counter {
    // counter value
    value: i32,

    // state of the two buttons
    increment_button: button::State,
    reset_button: button::State,
}

// message
#[derive(Debug, Clone, Copy)]
pub enum Message {
    Increment,
    Reset,
}

impl Sandbox for Counter {
    type Message = Message;

    fn new() -> Self {
        Counter {
            value: 0,
            increment_button: button::State::default(),
            reset_button: button::State::default(),
        }
    }

    fn title(&self) -> String {
        String::from("count up")
    }

    // update
    fn update(&mut self, message: Message) {
        match message {
            Message::Increment => {
                self.value += 1;
            }
            Message::Reset => {
                self.value = 0;
            }
        }
    }

    // view logic
    fn view(&mut self) -> Element<Message> {
        Container::new(
            Column::new()
                .push(
                    Button::new(&mut self.increment_button, Text::new("+1"))
                        .on_press(Message::Increment)
                        .border_radius(5)
                        .background(Background::Color(Color{r: 0.8, g: 0.8, b: 0.8, a: 1.})),
                )
                .push(
                    Text::new(self.value.to_string()).size(50).horizontal_alignment(HorizontalAlignment::Center),
                )
                .push(
                    Button::new(&mut self.reset_button, Text::new("reset"))
                        .on_press(Message::Reset),
                )
                .align_items(Align::Center)
        )
            .width(Length::Fill)
            .center_x()
            .height(Length::Fill)
            .center_y()
            .into()
    }
}

コードの説明

main関数ではGUIのおおもととなるWindowの設定を元にアプリを起動するところだけを請け負います。
次に状態のモデルとなるCounterモデルです。今回はカウント値、カウントアップボタン、リセットボタンの状態を表現しています。
次に状態の変更を通知するときに通知するイベントメッセージのMessage列挙体です。今回は増加とリセットを表現しています。
そしてSandboxトレイトを実装することで描画と更新処理を実装します。Applicationというトレイトもあるのですが、こちらは非同期を扱うときのトレイトとなります。描画はview関数で定義します。少しレイアウトの定義関数に癖がある気がしますが、よくある記述で書くことができます。
更新処理のupdate関数は状態モデルを更新する処理だけを行います。

所感

まだまだ実用には向かず未成熟な感じが強いですが、触ってみた限り完全にElmですね。モデルやメッセージが肥大化しそうな予感はありますが、慣れるとElmアーキテクチャの方がGUIは表記しやすいかもしれませんね。

まとめ

まだまだ未成熟なライブラリですが、実用的になればRustでGUIと言ったらこれ!といえるGUIライブラリの一つになりそうな予感がします。