Tauriでメニューバーにアプリを常駐しよう[Rust編]
はじめに
以前[Rust]はじめてのデスクトップアプリ開発 with React × Tauriという記事を書いたのですが、
今年はその Tauri が待望の v2.0 にアップデートされました。
目玉機能であるモバイル対応が Stable になり、個人的には激アツなアップデートとなりました。
今回はそのうちの一つである、System Tray APIを使って、
メニューバーに常駐するアプリケーションを作成してみたいと思います。
(筆者のアドカレの準備不足により)
JavaScript の System Tray API については想定の挙動を実現できてないため、
続編として別途記事にさせていただきます、、!
本記事の中では Rust の API 編として記載させていただきます。
セットアップ
まずは任意のディレクトリで Tauri のプロジェクトを作成します。
今回は例をシンプルにするため、npm と React(TypeScript)を使うことにします。
npm create tauri-app@latest
// 以下実行結果
npm create tauri-app@latest
Need to install the following packages:
create-tauri-app@4.5.7
Ok to proceed? (y) y
✔ Project name · system-tray-sample
✔ Identifier · com.system-tray-sample.app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm, deno, bun)
✔ Choose your package manager · npm
✔ Choose your UI template · React - (https://react.dev/)
✔ Choose your UI flavor · TypeScript
// cd <作成したプロジェクト名>
cd system-tray-sample
// モジュールのインストール
npm install
Tauri を起動して、下記のような画面が表示されれば準備完了です。
npm run tauri dev
Cargo.toml の編集
まずプロジェクト名/src-tauri/Cargo.toml
の dependencies
セクションにある
tauri = { version = "2", features = [] }
に tray-icon
を追加します
- tauri = { version = "2", features = [] }
+ tauri = { version = "2", features = [ "tray-icon" ] }
メニューバーにアイコンを表示
次に、TrayIconBuilder
のインスタンスを使ってメニューバーにアイコンを表示させます。
lib.rs
を下記のように編集します。
+use tauri::tray::TrayIconBuilder;
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
+ .setup(|app| {
+ let _tray = TrayIconBuilder::new()
+ .icon(app.default_window_icon().unwrap().clone())
+ .build(app)?;
+ Ok(())
+ })
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
この状態で Tauri を立ち上げると、メニューバーに Tauri のデフォルトのアイコンが表示されます。
npm run tauri dev
現時点ではメニューを追加していないため、
クリックしても何も起きません。
メニューを追加して、任意の処理を実行できるようにしていきます。
メニューの追加
menu::{Menu, MenuItem}
をインポートし、
メニューとそのアイテムを追加していきます。
- use tauri::tray::TrayIconBuilder;
+use tauri::{
+ menu::{Menu, MenuItem},
+ tray::TrayIconBuilder,
+};
// 省略
tauri::Builder::default()
.setup(|app| {
+ // メニューの追加
+ let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
+ let menu = Menu::with_items(app, &[&quit_i])?;
+ let _tray = TrayIconBuilder::new()
+ .menu(&menu)
+ .menu_on_left_click(true)
.icon(app.default_window_icon().unwrap().clone())
.build(app)?;
Ok(())
})
npm run tauri dev
メニューバーのアイコンをクリックした際に、Quit
というメニューが表示されるようになりました。
ハンドラーの追加
続いて、メニューアイテムのハンドラーを追加していきます。
on_menu_event
メソッドを追加し、
メニューアイテムの ID をパターンマッチングして、任意の処理を追加します。
今回はクリックした時にアプリケーションを終了するようにしておきます。
let _tray = TrayIconBuilder::new()
.menu(&menu)
.menu_on_left_click(true)
+ .on_menu_event(|app, event| match event.id.as_ref() {
+ "quit" => {
+ println!("quit menu item was clicked");
+ app.exit(0);
+ }
+ _ => {
+ println!("menu item {:?} not handled", event.id);
+ }
+ })
.icon(app.default_window_icon().unwrap().clone())
.build(app)?;
セパレーターの追加
最後に、セパレーターを追加してメニューアイテムの見た目に区切りを入れてみます。
セパレーターはmenu::PredefinedMenuItem
より追加できます。
+use tauri::{
- menu::{Menu, MenuItem},
+ menu::{Menu, MenuItem, PredefinedMenuItem},
tray::TrayIconBuilder,
};
// 省略
let hide_i = MenuItem::with_id(app, "hide", "Hide", true, None::<&str>)?;
+ let separator = PredefinedMenuItem::separator(app)?;
+ let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
- let menu = Menu::with_items(app, &[&quit_i])?;
+ let menu = Menu::with_items(app, &[&hide_i, &separator, &quit_i])?;
let _tray = TrayIconBuilder::new()
.menu(&menu)
.menu_on_left_click(true)
.on_menu_event(|app, event| match event.id.as_ref() {
"quit" => {
println!("quit menu item was clicked");
app.exit(0);
}
+ "hide" => {
+ println!("hide menu item was clicked");
+ app.hide().unwrap();
+ }
_ => {
println!("menu item {:?} not handled", event.id);
}
})
上記のように編集することで、セパレーターの見た目を追加できました。
以上で Rust 側の記述は終了です。
lib.rs
の最終形は以下のような形になっています。
use tauri::{
menu::{Menu, MenuItem, PredefinedMenuItem},
tray::TrayIconBuilder,
};
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// メニューの追加
let hide_i = MenuItem::with_id(app, "hide", "Hide", true, None::<&str>)?;
let separator = PredefinedMenuItem::separator(app)?;
let quit_i = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&hide_i, &separator, &quit_i])?;
let _tray = TrayIconBuilder::new()
.menu(&menu)
.menu_on_left_click(true)
.on_menu_event(|app, event| match event.id.as_ref() {
"quit" => {
println!("quit menu item was clicked");
app.exit(0);
}
"hide" => {
println!("hide menu item was clicked");
app.hide().unwrap();
}
_ => {
println!("menu item {:?} not handled", event.id);
}
})
.icon(app.default_window_icon().unwrap().clone())
.build(app)?;
Ok(())
})
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
まとめ
Tauri 2.0 の System Tray API を使って、メニューバーに常駐するアプリを作成することができました!
次回 JavaScript 側の API でもリベンジしてみたいと思います!
余談
Mac の場合、メニューバーにあるアプリは
⌘+ドラッグ
で位置を変えることができます
Discussion