開発

[rust] GUI crateを調査

Rustはたまに触る程度で継続的にプログラミングを行っていないので時間が立つと忘れてしまいます。継続的に触れる機会としてなにかアプリケーションを作ろうと思います。ということでまずは見た目から。Rustで気になるGUI crateを試してみます。

どうやらデファクトスタンダードなGUI crateは現時点では存在せず、安定度もまちまちなようです。

そこで気になるcrateを使ってビルドができるのか?軽くサンプルプログラムを動作させて問題がなさそうか?、調べて見たいと思います。

2024/04-5月頃の情報になります。

ターゲット環境

開発環境

  • Ubuntu 23.04 Desktop
  • とりあえずOSBoxesよりダウンロードしたもので試用
  • こちらでクロスコンパイルしてWindows向けにする
  • 開発中はMobaXtermで画面をリモートに表示させたい
  • rustc 1.77.2 (25ef9e3d8 2024-04-09)

実行環境

  • Windows環境

dioxus

dioxusはデスクトップ、モバイル、Web、TUI(テキストベースのUI)などをカバーするフレームワークです。React系の設計思想です。クロスプラットフォーム対応です。

フルスタックを謳うフレームワークで機能が充実している分、必要なcrateが多いです。JavaScriptフレームワークのような専用コマンドが用意されています。

導入

sudo apt install -y gcc
sudo apt install -y make
cargo install dioxus-cli@0.5.4

dx new
✔ 🤷   Which sub-template should be expanded? · Desktop
🤷   Project Name: test01
✔ 🤷   How do you want to create CSS? · Vanilla
✔ 🤷   Should the application use the Dioxus router? · true

cd test01
sudo apt install -y pkg-config
sudo apt install -y librust-gio-sys-dev
sudo apt install -y libgtk-3-dev
sudo apt install -y libsoup-3.0-dev
sudo apt install -y libjavascriptcoregtk-4.1-dev
sudo apt install -y libwebkit2gtk-4.1-dev
sudo apt install -y libxdo-dev
dx serve --platform desktop

下記が場合によって必要かもしれません
sudo apt install -y libglib2.0-dev

クロスコンパイル

フレームワーク提供のコマンドで実行環境向けインストーラーが作成されるらしい。詳しくはこちらを参照。

main.rsに下記を追加(オプショナル。実行時にDOS窓が表示されなくなる。他のcrateにも有効)

#![windows_subsystem = "windows"]
Dioxus.tomlに下記を追加

[bundle]
resources = ["**/main.css", "**/header.svg" ]
dx bundle --release --platform desktop

Failed to bundle project: BundlerError(
    Error {
        context: "Failed to build data folders and files",
        source: BundlerError(
            Error {
                context: "Failed to copy resource files",
                source: IoError(
                    Os {
                        code: 36,
                        kind: InvalidFilename,
                        message: "File name too long",
                    },
                ),
            },
        ),
    },
)

結果

成果物はネイティブアプリケーションではありませんでした。Electronのような実行環境の中でロジックが動くイメージでした。そのため、リモートに画面を表示することができません。

クロスコンパイルはBundlerErrorが出て先に進みませんでした。distフォルダーにassetsフォルダーの内容がコピーされているので動いてそうですが原因がよくわかりませんでした。

Assetsのドキュメントを見るとmanganisという別の仕組みが必要なようですがまだ不安定な印象です。

floem

floemLapceというRustで書かれたエディターで使われているUIフレームワークです。クロスプラットフォーム対応でXilemLeptos等に影響を受けたリアクティブ系のフレームワークのようです。

State管理はSolidJSと同様のアプローチのようです。

エディターというアプリケーションの実需で使用されているので安定度は高そうです。

導入

# Cargo.toml
[dependencies]
floem = "0.1.1"

sudo apt install -y gcc
cargo b

画面を最大化した場合、落ちる
cargo r
failed to present the surface buffer: PlatformError(Some("Failed to draw image to window"), Some(X11(ConnectionError(MaximumRequestLengthExceeded))))

サンプルプログラム

READMEにあるものを流用。

#![windows_subsystem = "windows"]

use floem::reactive::create_signal;
use floem::view::View;
use floem::views::{h_stack, label, v_stack, Decorators};
use floem::widgets::button;

fn app_view() -> impl View {
    // Create a reactive signal with a counter value, defaulting to 0
    let (counter, set_counter) = create_signal(0);

    // Create a vertical layout
    v_stack((
        // The counter value updates automatically, thanks to reactivity
        label(move || format!("Value: {}", counter.get())),
        // Create a horizontal layout
        h_stack((
            button(|| "Increment").on_click_stop(move |_| {
                set_counter.update(|value| *value += 1);
            }),
            button(|| "Decrement").on_click_stop(move |_| {
                set_counter.update(|value| *value -= 1);
            }),
        )),
    ))
}

fn main() {
    floem::launch(app_view);
}

クロスコンパイル

sudo apt install -y mingw-w64
cargo build --target x86_64-pc-windows-gnu

結果

コードがシンプルで良さそうですが、X Window上で画面を最大化するだけで落ちてしまうのは不便だし安定度が疑わしくなってしまいます。クロスコンパイルしてWindows環境で実行した際には最大化しても問題ありませんでした。

まだ知名度は低いので日本語の情報は少ないです。

iced

icedElmの設計思想に影響を受けた型安全なクロスプラットフォームのUIフレームワークです。Webまでカバーしているようですが、詳細は不明です。

導入

# Cargo.toml
[dependencies]
iced = "0.12.1"

sudo apt install -y gcc
cargo b

サンプルプログラム

Exampleより流用。

#![windows_subsystem = "windows"]

use iced::widget::{button, column, text};
use iced::{Alignment, Element, Sandbox, Settings};

pub fn main() -> iced::Result {
    Counter::run(Settings::default())
}

struct Counter {
    value: i32,
}

#[derive(Debug, Clone, Copy)]
enum Message {
    IncrementPressed,
    DecrementPressed,
}

impl Sandbox for Counter {
    type Message = Message;

    fn new() -> Self {
        Self { value: 0 }
    }

    fn title(&self) -> String {
        String::from("Counter - Iced")
    }

    fn update(&mut self, message: Message) {
        match message {
            Message::IncrementPressed => {
                self.value += 1;
            }
            Message::DecrementPressed => {
                self.value -= 1;
            }
        }
    }

    fn view(&self) -> Element<Message> {
        column![
            button("Increment").on_press(Message::IncrementPressed),
            text(self.value).size(50),
            button("Decrement").on_press(Message::DecrementPressed)
        ]
        .padding(20)
        .align_items(Alignment::Center)
        .into()
    }
}

クロスコンパイル

sudo apt install -y mingw-w64
cargo build --target x86_64-pc-windows-gnu

結果

コードがシンプルで良さそうです。クロスコンパイルも動作も問題ないようです。

ボタンの見た目がボタンらしくないのですが、サンプルを見るとちゃんとしているのでそのあたりの研究が必要かもしれません。レイアウト系がシンプルな仕組みのみで弱いかもしれません。

日本語の情報を多めです。ただし初心者向けのものが多いようです。

Vizia

Viziaは宣言的でリアクティブなクロスプラットフォーム対応のUIフレームワークです。アニメーションやGPUアクセラレーター対応のようです。

導入

# Cargo.toml
[dependencies]
#vizia = "0.1.0"
vizia = {git = "https://github.com/vizia/vizia"}

sudo apt install -y gcc
cargo b

サンプルプログラム

Bookより流用。

#![windows_subsystem = "windows"]

use vizia::prelude::*;

#[derive(Lens)]
pub struct AppData {
    count: i32,
}

pub enum AppEvent {
    Increment,
    Decrement,
}

impl Model for AppData {
    fn event(&mut self, _cx: &mut EventContext, event: &mut Event) {
        event.map(|app_event, _meta| match app_event {
            AppEvent::Decrement => self.count -= 1,
            AppEvent::Increment => self.count += 1,
        });
    }
}

fn main() {
    let _ = Application::new(|cx| {
        AppData { count: 0 }.build(cx);

        HStack::new(cx, |cx| {
            Button::new(cx, |cx| Label::new(cx, "Decrement"))
                .on_press(|ex| ex.emit(AppEvent::Decrement))
                .class("dec");
            Button::new(cx, |cx| Label::new(cx, "Increment"))
                .on_press(|ex| ex.emit(AppEvent::Increment))
                .class("inc");
            Label::new(cx, AppData::count);
        })
        .child_space(Stretch(1.0))
        .col_between(Pixels(20.0));
    })
    .title("Counter")
    .inner_size((400, 100))
    .run();
}

クロスコンパイル

sudo apt install -y mingw-w64
cargo build --target x86_64-pc-windows-gnu

結果

0.1.0ではコンパイルできませんでした。それ以外の部分では問題がありませんでした。

バージョンがまだ若いので今後大きな変化が起きる恐れがあります。

まとめ

各crateはほぼ何らかのJavaScriptフレームワークの影響を受けたものになっているので過去に使用したことがある場合は学習コストを抑えて使っていける可能性があります。

dioxusはエコシステムが充実しているので今後の期待度が高いですが、覚えることも多そうです。また、リモートに画面を表示することができなかったのが惜しいところです。

floemはシンプルに書けそうですが、最大化するだけで落ちるといった品質面に不安が残る結果となりました。またカッコが多くなりそうです。

icedはシンプルに書けそうですが、今回試した中でボタンの見た目が一番ネイティブ表示から遠いのでカスタマイズの可能性をもう少し調べる必要がありそうです。

Viziaは動作しますが、まだ公式バージョンが安定しておらず、バージョンも若いため今後大きな変化が起こる可能性があり、現時点で使用していくには勇気が必要です。

ただ、この記事を書いている最中もアップデートが進んでおり、上記紹介したサンプルも次のリビジョンで書き方が異なっているものもあります。これらのcrateを採用するにしても、まだプロトタイプレベルの使用に抑えておいたほうがいいかもしれません。

Tags: rust
管理人

Recent Posts

CanvaがSerif (Affinity) を買収

私は使ったことがないのですが名前はよく聞…

2か月 ago

Serifのスプリングセール – アドオンが50%オフ

Affinity Photoなどレタッチ…

2か月 ago

音声がロボットのようになるときの対処

リモート会議などでたまに相手の音声がおか…

3か月 ago

Serifのブラックフライデー – 全品40%オフ V1ユーザは更にお得!

恒例のブラックフライデーセールが始まりま…

6か月 ago

[rust] rayonで書き直してみました

前回のコードを元にrayonを使った処理…

6か月 ago

[rust] async-stdで書き直してみました

前回のコードをasync-stdで書き直…

7か月 ago