右クリックメニュー(コンテキストメニュー)を出す

Recipe ID: menu-008

右クリック時などに表示されるコンテキストメニュー(ポップアップメニュー)を実装する方法を解説します。

Tauri v2 では、作成したメニューインスタンスの popup() メソッドを呼び出すことで、任意の位置にメニューを表示できます。

1. フロントエンドから表示する (TypeScript)

contextmenu イベントをリッスンし、preventDefault() でブラウザ標準のメニューを無効化した上で、menu.popup() を呼び出します。

サンプルコード

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

// 1. ポップアップ用メニューの作成
const menu = await Menu.new({
  items: [
    {
      id: 'reload',
      text: '再読み込み',
      accelerator: 'CmdOrCtrl+R', // ショートカットキー
      action: () => window.location.reload()
    },
    { item: 'Separator', text: '-' },
    {
      id: 'copy',
      text: 'コピー (Custom)',
      action: () => console.log('Copy clicked')
    },
    { item: 'Separator', text: '-' },
    {
      id: 'close',
      text: '閉じる',
      action: () => console.log('Close clicked')
    }
  ]
});

// 2. 右クリックイベント (contextmenu) のハンドリング
document.addEventListener('contextmenu', async (e) => {
  // ブラウザ標準の右クリックメニューを抑制
  e.preventDefault();
  
  // 作成したメニューをカーソル位置にポップアップ表示
  await menu.popup();
});

2. バックエンドから表示する (Rust)

Rust 側で popup を呼び出す場合は、Window インスタンス(または WebviewWindow)が必要です。

サンプルコード

use tauri::menu::{ContextMenu, MenuBuilder, MenuItemBuilder};
use tauri::WebviewWindow;

// 1. コンテキストメニューを表示するコマンド
#[tauri::command]
fn show_context_menu(window: WebviewWindow) {
    // ショートカット付きのアイテムを作成
    let reload = MenuItemBuilder::new("再読み込み")
        .id("reload")
        .accelerator("CmdOrCtrl+R")
        .build(&window)
        .unwrap();

    let menu = MenuBuilder::new(&window)
        .item(&reload)
        .separator()
        .text("copy", "コピー (Custom)")
        .separator()
        .text("close", "閉じる")
        .build()
        .unwrap();
        
    // popup() は Window<R> を引数に取ります
    // WebviewWindow から Window インスタンスを取得・複製して渡します
    menu.popup(window.as_ref().window().clone()).unwrap();
}

// 2. イベントハンドリング (lib.rs / main.rs)
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![show_context_menu])
        .on_menu_event(|app, event| {
            // メニュー項目の ID に基づいて処理を分岐
            match event.id().0.as_str() {
                "reload" => {
                    println!("Reload clicked");
                    // 実際にリロードする場合の例:
                    // if let Some(window) = app.get_webview_window("main") {
                    //     let _ = window.eval("window.location.reload()");
                    // }
                }
                "copy" => {
                    println!("Copy clicked");
                }
                "close" => {
                    println!("Close clicked");
                }
                _ => {}
            }
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

補足

* 表示位置: popup() メソッドは、デフォルトで現在のマウスカーソル位置に表示されます。オプションで座標を指定することも可能です。
* 非同期: popup() は非同期関数です。await することでメニューが閉じられるまで待機するわけではありません(表示完了を待ちます)。