実行中にメニューの内容を書き換える

Recipe ID: menu-006

アプリケーション実行中に、状況に応じてメニューのラベル(テキスト)を動的に変更する方法を解説します。

例えば、「保存」を「上書き保存」に変えたり、ログイン状態によって「ログイン」を「ログアウト」に変えるといったUIの更新に使用できます。

前提条件

Permissions (権限) の設定

src-tauri/capabilities/default.jsoncore:menu:default を追加します。

{
  "permissions": [
    ...,
    "core:menu:default"
  ]
}

1. フロントエンドから変更する (TypeScript)

MenuItem インスタンスを取得し、setText() などのメソッドを使用します。

サンプルコード

import { Menu, Submenu, MenuItem } from '@tauri-apps/api/menu';

// 1. 変更したい MenuItem を個別に作成 (参照を保持するため)
const authItem = await MenuItem.new({
  id: 'auth', // id は必須ではありませんが、識別用に推奨
  text: 'ログイン',
  action: async () => {
     await toggleAuth();
  }
});

// 2. メニュー構造を構築
const accountMenu = await Submenu.new({
  text: 'アカウント',
  items: [authItem]
});

const menu = await Menu.new({
  items: [accountMenu]
});

await menu.setAsAppMenu();

// 状態管理と連動させる例
let isLoggedIn = false;

async function toggleAuth() {
  isLoggedIn = !isLoggedIn;
  
  // 保持していたインスタンスに対してメソッドを呼び出す
  if (isLoggedIn) {
     await authItem.setText('ログアウト');
     console.log('ログインしました');
  } else {
     await authItem.setText('ログイン');
     console.log('ログアウトしました');
  }
}

その他の更新可能なプロパティ

setText 以外にも、以下のようなプロパティを動的に変更可能です。

* setEnabled(boolean): 有効/無効の切り替え
* setChecked(boolean): CheckMenuItem のチェック状態の切り替え
* setIcon(Image): IconMenuItem のアイコン変更
* setAccelerator(string): ショートカットキーの変更

2. バックエンドから変更する (Rust)

Rust 側でも MenuItem などのメソッドを使用して動的に変更できます。

サンプルコード

use tauri::menu::{Menu, MenuItem, Submenu};
use tauri::Manager;
use std::sync::Mutex;

// メニュー項目の参照と状態を管理する構造体
struct MenuState {
    auth_item: MenuItem<tauri::Wry>,
    is_logged_in: Mutex<bool>,
}



#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            // 1. 動的に変更したいメニュー項目を作成
            // ID "auth" を指定、初期テキストは "ログイン"
            let auth_item = MenuItem::with_id(app, "auth", "ログイン", true, None::<&str>)?;
            
            // 2. サブメニューを作成し、項目を追加
            let account_menu = Submenu::with_items(app, "アカウント", true, &[&auth_item])?;

            // 3. アプリケーション全体のメニューを作成
            let menu = Menu::with_items(app, &[&account_menu])?;

            // 4. メニューをセット
            app.set_menu(menu)?;

            // 5. 作成した MenuItem のインスタンスと初期状態を State に保存
            // MenuItem 自体はスレッドセーフなハンドルのため Mutex は不要 (bool のみラップする)
            app.manage(MenuState { 
                auth_item,
                is_logged_in: Mutex::new(false),
            });

            Ok(())
        })
        .on_menu_event(|app, event| {
            // メニューイベントのハンドリング
            // .0 で ID 文字列にアクセス (Tauri v2)
            if event.id().0 == "auth" {
                // State を取得 (Mutex でラップしていないので直接フィールドにアクセス可)
                let menu_state = app.state::<MenuState>();
                
                // フラグの更新 (ここだけロックする)
                let is_logged_in = {
                    let mut guard = menu_state.is_logged_in.lock().unwrap();
                    *guard = !*guard;
                    *guard
                };
                
                let new_text = if is_logged_in { "ログアウト" } else { "ログイン" };
                
                // MenuItem のテキストを更新 (set_text は &self で呼べる)
                if let Err(e) = menu_state.auth_item.set_text(new_text) {
                    eprintln!("Failed to update menu text: {}", e);
                }
                
                println!("Menu Item Clicked: State changed to {}", if is_logged_in { "Logged In" } else { "Logged Out" });
            }
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}