85
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rust] web-viewでGUIアプリをつくる

Last updated at Posted at 2020-01-25

Rust で web-view を用いて Win10 向け GUI アプリを開発する方法を試してみました. この方針の利点として

  • Rust + ウェブ技術の知識で GUI アプリが開発できる.
    • Electron と違い, レンダリングエンジンとして OS が提供するものを使用するためバイナリサイズが小さい.

という点があげられます. 本記事のコードを開発・実行した環境は Windows 10 v1809, Rust 1.40 stable-x86_64-pc-windows-msvc です. なお Mac でも動くかもしれませんが試していません. また Linux で動かすには Webkit2gtk が必要です.

Hello world

まず最初は Hello world してみます. 新規プロジェクト hello-webview を作成し, Cargo.toml

[dependencies]
web-view = { version = "0.6", features = ["edge"] }

を指定します. フィーチャーフラグ edge はレンダリングエンジンに MSHTML (IE) ではなく EdgeHTML を使用することを意味します.

次に src/main.rs を次のように作成します. cf. examples/pageload.rs

#![windows_subsystem="windows"]

fn main() {
    web_view::builder()
        .title("Hello world!")
        .content(web_view::Content::Html(HTML))
        .size(320, 240)
        .user_data(())
        .invoke_handler(|_, _| Ok(()))
        .run()
        .unwrap();
}

const HTML: &str = r#"<!DOCTYPE html>
<html>
    <body>
        Hello world from Rust!
    </body>
</html>"#;

最初の行は DOS 窓を非表示にするためのものです. たくさん並んでいるメソッドは以下で順番に説明します. これをコンパイル, 実行すれば WebView が起動し, Hello world が表示されます.

image.png

なお high-DPI 環境ではぼやけるかもしれません. その場合実行ファイル (./target/debug/hello-webview.exe) を右クリックし, 「プロパティ-互換性-高DPI設定の変更-高DPIスケールの動作を上書きします」のチェックマークをつければ解決します (拡大縮小の実行元: アプリケーション). この設定はコンパイルし直しても有効なままのようです.

カウンター

次の例として, ウィンドウにボタンを表示し, それをクリックした回数をカウントし表示するカウンターアプリをつくってみます. cf. examples/timer.rs

ブラウザ側のイベントの取得

まずブラウザ側で起こったイベントを扱う方法を見てみます. HTML ソースを

<!DOCTYPE html>
<html>
    <body>
        <div id="display">0</div>
        <button onclick="external.invoke('count')">count</button>
    </body>
</html>

と書き換えます. これを表示すると 0 という数字と count と書かれたボタンが表示されるはずです. ボタンをクリックすると onclick 属性で指定された JS コードが走ります. この JS コードは invoke という外部で定義された関数を呼び出そうとします.

invoke の実装は Rust 側で行います: 上で登場した invoke_handler というメソッドの引数であるクロージャが呼び出される関数です. なので上の Rust コードで該当する部分を

.invoke_handler(|_webview, arg| {
    if arg == "count" {
        eprintln!("Count!");
    }
    Ok(())
})

という形に書き替えてみます. このクロージャは, 第 2 引数が "count" なら標準エラーに "Count!" と表示し, そうでなければ何もしません. これを (最初に設定した #![windows_subsystem = "windows"] というアトリビュートを外した上で) 実行すると

image.png

というウィンドウが表示され, count ボタンをクリックすると次のように出力されます.

image.png

プログラムの内部状態の更新

カウンタープログラムはカウントした回数を保持している必要があります. そこでまず内部状態を表す struct を定義します.

struct UserData {
    count: u32,
}

そして, web_view のビルダー .user_data() にこの struct を渡すように変更します.

.user_data( UserData { count: 0 } )

内部状態には invoke_handler に渡すクロージャの第 1 引数 webviewwebview.user_data() または webview.user_data_mut() という形でアクセスします (前者が不変な参照, 後者が可変な参照). そこで上の invoke_handler を次のように書き換えれば, count ボタンが押されるたびに内部状態が 1 ずつ更新されるようになります.

.invoke_handler(|webview, arg| {
    if arg == "count" {
        webview.user_data_mut().count += 1;
        eprintln!("Count! {}", webview.user_data().count);
    }
    Ok(())
})

image.png

画面の表示内容の書き換え

最後に, 内部状態を更新する度に画面に表示されている内容を書き換える必要があります. これは webview.eval() メソッドによって適当な JS コードを呼び出すことによって達成されます. なおこのメソッドは Result<T, web_view::Error> を返すので, そのままクロージャの戻り値にします.

.invoke_handler(|webview, arg| {
    if arg == "count" {
        webview.user_data_mut().count += 1;
        eprintln!("Count! {}", webview.user_data().count);
        webview.eval(&format!("update({})", webview.user_data().count))
    } else {
        Ok(())
    }
})

あとは対応する JS 関数を HTML 側で定義すれば完成です.

<!DOCTYPE html>
<html>
    <body>
        <div id="display">0</div>
        <button onclick="external.invoke('count')">count</button>

        <script>
            function update(count) {
                document.getElementById('display').innerHTML = count;
            }
        </script>
    </body>
</html>
85
69
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
85
69

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?