はじめに
前回は、フロントエンドのお絵描きをする機能の仕上げをしました。ウィンドウサイズに合わせてキャンバスを拡張したり、アンドゥやリドゥを実装したり、サンプルのトイプードルを描画したりしました。
今回は、前回に引き続き追記して、バックエンドのRustでプログラミングを始めます。バックエンドでは、ファイルメニューを開き、実行したらファイルを開くダイアログを表示して、サンプルのsample.bezierファイルを開きます。

メニュー機能について
メニュー機能は、フロントエンドではなくバックエンドで作ります。ボタンを使ったツールバーメニューのようなものはフロントエンドでもできますが、OS機能のメニューバーはバックエンドでしか作れません。
バックエンドでフロントエンドに「ファイル」→「開く」メニューを実行したことをメッセージを送って知らせます。するとフロントエンドでは、メッセージを受け取る準備をして待機します。メッセージを受け取ったらファイルを開くように、再度バックエンドを呼び出します。バックエンドで.bezierファイルを開いてフロントエンドにベジェ曲線データを返します。そうしてやっとフロントエンドで受け取ったベジェ曲線データを描画します。
「ファイル」と「開く」メニュー
「lib.rs」ファイルで以下のサンプルコード[1]を追記して、次のコマンドを実行しプロジェクトをデバッグビルドして実行すると、「ファイル」と「開く」というメニューができます。まだ実際にファイルを開くことはできません。同じ作業を繰り返すことで、他のメニュー項目も追加することも可能です。
$ cargo-tauri dev

// クレート use tauri::{menu::*, AppHandle, Wry, Error}; // エントリーポイント #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .invoke_handler(tauri::generate_handler![]) // セットアップ .setup(|app| { let _ = app.set_menu(get_menu(app.handle())?); app.on_menu_event(|handle, event| {}); Ok(()) }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } // メニュー取得 fn get_menu(handle: &AppHandle) -> Result<Menu<Wry>, Error> { let menu = Menu::new(handle)?; let _ = menu.append(&get_menu_file(handle)?)?; Ok(menu) } // メニュー項目取得 fn get_menu_file(handle: &AppHandle) -> Result<Submenu<Wry>, Error> { SubmenuBuilder::new(handle, "ファイル") .items(&[ &MenuItem::with_id(handle, MenuId::new("id_open"), "開く", true, Some("Ctrl+O"))?, ]) .build() }
サンプルコードの解説
「run」関数で、Tauri 2.0の初期設定をします。TAURIビルダーのsetupメソッドの中でメニューを設定します。
「get_menu」関数から呼び出したメニューを「set_menu」メソッドでセットします。
get_menu関数で、「Menu」クラスのインスタンスを生成して「menu」変数に代入し、「get_menu_file」関数を呼び出してmenu変数に追加します。
get_menu_file関数で、ファイルメニューとその子として開くメニューを追加したインスタンスを戻り値として返します。
ファイルメニュー
「開く」が実行されたら、フロントエンドにファイルを開くようにメッセージを送ります。今の状態では、フロントエンドでメッセージを受け取る準備はしていません。
もちろんですが、次のコードをそのままコピペしたら(後略)のところでエラーになります。以下のサンプルコードは、lib.rsファイルに追記してget_menu関数とget_menu_file関数が省略されているので、そこは残してください。
// クレート use tauri::{menu::*, AppHandle, Wry, Error}; // エントリーポイント #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .invoke_handler(tauri::generate_handler![]) // セットアップ .setup(|app| { let _ = app.set_menu(get_menu(app.handle())?); app.on_menu_event(|handle, event| { run_menu_process(handle, event.id().as_ref()) }); Ok(()) }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } // メニュー実行 fn run_menu_process(handle: &AppHandle, id: &str) { use tauri::Emitter; match id { "id_open" => { let _ = handle.emit_to("main", "event-open", "Open menu process"); }, _ => {}, } } (後略)
サンプルコードの解説
run関数のsetupメソッドの中でメニューが実行されたら「run_menu_process」関数を呼び出すように「on_menu_event」メソッドに登録します。
run_menu_process関数で、「emit_to」メソッドを使ってフロントエンドにファイルを開くことを"event-open"メッセージで送ります。「emit」とは「発する」という意味です。