0Pricing
Learn Rust Coding · レッスン

Immediate-Mode UIの基本

eguiがどのように描画するかを理解します。

「Immediate-Mode UIの基本」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。

Immediate Modeの意味

eguiはimmediate-mode GUIライブラリです。永続的なウィジェットツリーを構築するretained-modeツールキット(GTK、Qt)とは異なり、eguiはフレームごとにUI全体を最初から再構築します。

コードは各フレームで上から下へ実行され、関数呼び出しとしてウィジェットを出力します。保存されたノードグラフはなく、UIはクロージャを実行した副作用にすぎません。

更新ループ

eguiアプリはeframe::Appを実装します。そのupdateメソッドは通常、毎秒60回、フレームごとに1回呼び出されます。

update内では、現在の状態に基づいて、UIが今この瞬間どのように見えるべきかを記述します。eguiは差分を計算せず、すべてをそのまま再出力します。

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _f: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.label("Hello, egui!");
        });
    }
}

ContextとUi

2つの主要な型がいたるところに登場します。egui::Contextは、そのフレームにおけるUI全体の状態を共有する、安価にcloneできるハンドルです。

Uiはウィジェットを追加する領域です。パネルやウィンドウはクロージャ内で&mut Uiを渡します。そこに対してui.labelやui.buttonなどのメソッドを呼び出します。

egui::CentralPanel::default().show(ctx, |ui| {
    ui.heading("Dashboard");
    ui.separator();
    ui.label("Widgets go here.");
});

ウィジェットはResponseを返す

UIはフレームごとに再構築されるため、retained-modeツールキットのようにコールバックを登録することはできません。その代わり、すべてのウィジェット呼び出しは即座にResponseを返します。

同じフレーム内でResponseを調べます。.clicked()、.hovered()、.changed()などを使います。これにより、イベント処理をレイアウトと同じ場所に記述できます。

if ui.button("Save").clicked() {
    self.save();
}
let resp = ui.text_edit_singleline(&mut self.name);
if resp.changed() {
    self.dirty = true;
}

状態は自分で管理する

eguiが保持するアプリケーションデータはごくわずかです。ウィジェットの値はApp構造体のselfに置き、ウィジェットには可変参照を渡します。

テキストフィールドには&mut Stringを、チェックボックスには&mut boolを渡します。eguiは参照を通して値を書き戻すため、構造体が唯一の信頼できるデータソースになります。

struct MyApp {
    name: String,
    enabled: bool,
}

// inside update:
ui.text_edit_singleline(&mut self.name);
ui.checkbox(&mut self.enabled, "Enabled");

eframeのセットアップ

eframeは、デスクトップとWebでeguiを動かす公式フレームワークです。ネイティブウィンドウ、GPUサーフェス、イベントループを管理します。

eframe::run_nativeを使って起動し、ウィンドウタイトル、ネイティブオプション、Appを構築するクロージャを渡します。

fn main() -> eframe::Result<()> {
    let opts = eframe::NativeOptions::default();
    eframe::run_native(
        "My App",
        opts,
        Box::new(|_cc| Ok(Box::new(MyApp::default()))),
    )
}

入力による再描画

デフォルトでは、eguiはマウスの移動、キーの押下、明示的な要求など、何かが起きたときだけ再描画します。これにより、アイドル状態のアプリでのCPU使用量を抑えられます。

アニメーションやライブデータを扱う場合は、ctx.request_repaint()を呼び出して次のフレームをスケジュールします。時間指定で更新する場合は、期間を指定してrequest_repaint_afterを使います。

ui.label(format!("Tick: {}", self.counter));
self.counter += 1;
ctx.request_repaint(); // keep animating

保持型ウィジェットIDは基本的に不要

eguiは呼び出し順と位置からウィジェットの識別情報を導出し、Idを自動生成します。これは、フレームをまたいで安定した状態を保持する必要がある折りたたみ可能なヘッダーなどで重要です。

ループ内で同じラベルのボタンを使う場合など、2つのウィジェットが衝突しそうなときは、ui.push_id(i, |ui| ...)で囲んで区別します。

for (i, item) in self.items.iter().enumerate() {
    ui.push_id(i, |ui| {
        if ui.button(&item.label).clicked() {
            println!("clicked {}", i);
        }
    });
}

イミディエートモードのトレードオフ

イミディエートモードでは、UIコードを簡単にデータ駆動にできます。ifでセクションを表示・非表示にし、forでリストを描画できます。ノードを手動で追加・削除する必要がなく、モデルとビューの不整合も起こりません。

その代わり、フレームごとにUI全体が再実行されるため、フレームごとの処理は軽くし、update内で重い計算を行わないようにします。

if self.logged_in {
    ui.label(format!("Welcome, {}", self.user));
} else {
    ui.label("Please sign in.");
}

最小限の完全なアプリ

ここまでをまとめると、構造体が状態を保持し、updateがそれを描画し、mainがウィンドウを実行します。これがeguiデスクトッププログラムの基本構造全体です。

レイアウト、テーマ設定、パッケージ化など、それ以外のすべてはこのループを基盤に構築されます。

#[derive(Default)]
struct App { count: i32 }

impl eframe::App for App {
    fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            if ui.button("+1").clicked() { self.count += 1; }
            ui.label(format!("Count: {}", self.count));
        });
    }
}

Rustとeguiの相性がよい理由

eguiの設計は、Rustの強みを活かします。1回のupdate呼び出しに&mut selfを借用して渡すことで、借用チェッカーがウィジェットとモデル間のデータ競合がないことを保証します。

共有可変状態を捕捉するコールバックも、保持型ツリーによって強制されるRc<RefCell>のグラフもありません。あるのは、素直な所有権とフレームごとに1回の可変アクセスだけです。

理解度チェック

イミディエートモードについての理解度を確認しましょう。

まとめ

eguiはイミディエートモードです。eframe::App::updateの中で、フレームごとにUI全体が再構築されます。ウィジェットは関数呼び出しであり、すぐに調べられるResponseを返します。

データはselfに保持し、可変参照で渡します。そのため、借用チェッカーがモデルとビューの同期を保ちます。eframe::run_nativeがウィンドウとイベントループを管理します。次は、ウィジェットの種類とレイアウトシステムについて見ていきます。

よくある質問

「Immediate-Mode UIの基本」レッスンは無料ですか?

はい。「Immediate-Mode UIの基本」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。

「Immediate-Mode UIの基本」で何を学びますか?

eguiがどのように描画するかを理解します。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「Immediate-Mode UIの基本」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Immediate-Mode UIの基本
  2. ウィジェットとレイアウト
  3. アプリの状態を管理する
  4. デスクトップアプリをパッケージ化する
← Learn Rust Codingに戻る