メニューがクリックされた時の処理を書く

Recipe ID: menu-005

メニューがクリックされた時の処理を記述する方法について解説します。

Tauri v2 では、メニュー項目の作成時に action プロパティに関数を渡すだけで、簡単にイベントハンドラを定義できます (JavaScript)。

前提条件

Permissions (権限) の設定

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

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

1. フロントエンドで処理する (TypeScript)

action オプションを使用します。この関数はメニューがクリックされた時に呼び出され、引数として項目の id を受け取ります。

シンプルな記法(オブジェクト形式)

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

const handleMenuClick = (id: string) => {
  console.log(`Common Handler: ${id}`);
};

const menu = await Menu.new({
  items: [
    {
      id: 'open',
      text: '開く',
      action: (id) => {
        console.log(`個別ハンドラ: ${id} がクリックされました`);
      }
    },
    {
      id: 'save',
      text: '保存',
      action: handleMenuClick // 関数参照を渡すことも可能
    }
  ]
});

await menu.setAsAppMenu();

クラスを使用した記法

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

// 1. 各メニュー項目を作成し、action を定義する
const openItem = await MenuItem.new({
  id: 'open',
  text: '開く',
  action: (id) => {
    console.log(`Action Handler: ${id} がクリックされました`);
  }
});

const saveItem = await MenuItem.new({
  id: 'save',
  text: '保存',
  action: (id) => {
    console.log(`Action Handler: ${id} がクリックされました`);
  }
});

// 2. サブメニューを作成し、項目を追加する
const fileSubmenu = await Submenu.new({
  text: 'ファイル',
  items: [openItem, saveItem]
});

// 3. メニューを作成し、サブメニューを追加する
const menu = await Menu.new({
  items: [fileSubmenu]
});

// 4. アプリケーションメニューとして設定する
await menu.setAsAppMenu();

参考: MenuItemOptions API リファレンス

2. バックエンドで処理する (Rust)

Rust 側でメニューイベントをハンドリングするには、tauri::Builderon_menu_event を使用します。

MenuBuilder を使用した記法

use tauri::menu::MenuBuilder;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            let menu = MenuBuilder::new(app)
                .text("open", "開く")
                .text("save", "保存")
                .build()?;
            
            app.set_menu(menu)?;
            Ok(())
        })
        .on_menu_event(|app, event| {
            match event.id().0.as_str() {
                "open" => {
                    println!("Open clicked");
                    // フロントエンドにイベントを送信する等の処理
                    // app.emit("open-requested", ()).unwrap();
                }
                "save" => {
                    println!("Save clicked");
                }
                _ => {}
            }
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

補足

* イベントの競合: フロントエンド (action) とバックエンド (on_menu_event) の両方で処理を書いた場合、バックエンドのイベントハンドラは常に発火しますが、フロントエンドの action はフロントエンドで作成されたメニュー項目に対してのみ機能します。
* 推奨: フロントエンド中心のロジックであれば TypeScript の action を、システム操作やウィンドウ制御などが必要な場合は Rust の on_menu_event を使用すると良いでしょう。