人人都会AI编程

13.2 应用菜单:应用菜单栏、上下文菜单、系统菜单定制

更新时间:2026-07-11

菜单是桌面应用的重要组成部分,Tauri 提供了灵活的 API 来定制三种常见的菜单形式:全局应用菜单栏(窗口顶部的标准菜单)、上下文菜单(右键弹出菜单)以及系统托盘菜单(托盘图标上的菜单)。所有菜单的定义和事件处理都集中在 Rust 后端,保证了安全性和一致性,同时也可以方便地与前端通信。


1. 应用菜单栏

全局菜单栏位于 macOS 屏幕顶部(或 Windows/Linux 的窗口内),通常包含“文件”“编辑”“帮助”等固定项。Tauri 允许通过 Rust 代码完全自定义菜单结构,并绑定菜单事件。

基本步骤:

  1. 在 Rust 中定义一个 Menu 对象,并添加菜单项与子菜单。
  2. 将菜单安装到应用的主窗口。
  3. 监听菜单项的点击事件,执行相应逻辑(例如向前端发送事件)。

示例:创建一个带“文件”和“帮助”的菜单

use tauri::{CustomMenuItem, Menu, MenuItem, Submenu};

fn main() {
    tauri::Builder::default()
        .setup(|app| {
            // 创建菜单项
            let new_file = CustomMenuItem::new("new_file".to_string(), "新建");
            let open = CustomMenuItem::new("open".to_string(), "打开");
            let save = CustomMenuItem::new("save".to_string(), "保存");
            let quit = MenuItem::Quit.to_owned(); // 系统内置退出项

            // 创建子菜单
            let file_menu = Submenu::new("文件", Menu::new()
                .add_item(new_file)
                .add_item(open)
                .add_item(save)
                .add_native_item(quit) // 加入原生退出动作
            );

            let about = CustomMenuItem::new("about".to_string(), "关于");
            let help_menu = Submenu::new("帮助", Menu::new().add_item(about));

            // 构建主菜单(macOS 默认会附带应用名称菜单,Windows/Linux 直接显示)
            let menu = Menu::new()
                .add_submenu(file_menu)
                .add_submenu(help_menu);

            // 应用到当前窗口
            let window = app.get_window("main").unwrap();
            window.set_menu(menu).unwrap();

            // 监听菜单事件
            window.on_menu_event(|event| {
                match event.menu_item_id() {
                    "new_file" => {
                        // 执行新建操作,可以全局广播或调用前端
                        event.window().emit("menu-event", "new_file").unwrap();
                    }
                    "open" => { /* ... */ }
                    "save" => { /* ... */ }
                    "about" => {
                        // 弹出关于对话框等
                    }
                    _ => {}
                }
            });

            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

说明:

  • 使用 CustomMenuItem::new(id, 标题) 创建普通菜单项,id 用于事件匹配。
  • MenuItem::QuitMenuItem::Copy 等是系统内置动作,行为已预定义。
  • window.on_menu_event 中的闭包可以调用 window.emit() 将事件发送至前端,实现前后端联动。
  • 在 macOS 上,如果希望保留默认的“关于”“偏好设置”等项,可以使用 Menu::os_default("应用名") 内置模板,再追加自定义菜单。

2. 上下文菜单

上下文菜单是用户通过鼠标右键(或长按)触发的弹出式菜单。Tauri 支持两种上下文菜单:

  • WebView 内的 DOM 元素右键菜单:可以完全在前端通过 JavaScript 阻止默认行为并显示自定义菜单,但最终需 Rust 参与实现原生菜单。
  • 窗口级别右键菜单:使用 Rust API 在指定窗口上设置上下文菜单。

通常推荐后者,因为原生菜单风格与系统一致,且不受前端 CSS 影响。

实现步骤:

  1. 在 Rust 中创建 Menu 对象(无需添加到主窗口)。
  2. 在前端监听 contextmenu 事件,获取坐标并通过 Tauri 命令告知 Rust。
  3. Rust 接收到命令后,调用 window.popup_menu(menu, x, y) 显示菜单。
  4. 使用菜单事件处理点击。

Rust 端核心函数示例:

use tauri::{CustomMenuItem, Menu, Manager};

#[tauri::command]
fn show_context_menu(window: tauri::Window, x: f64, y: f64) {
    let copy = CustomMenuItem::new("ctx_copy".to_string(), "复制");
    let paste = CustomMenuItem::new("ctx_paste".to_string(), "粘贴");
    let inspect = CustomMenuItem::new("ctx_inspect".to_string(), "检查元素");

    let menu = Menu::new()
        .add_item(copy)
        .add_item(paste)
        .add_item(inspect);

    // 在指定坐标弹出菜单
    window.popup_menu(menu, x as f64, y as f64).unwrap();
}

// 在 setup 中注册事件监听
app.get_window("main").unwrap().on_menu_event(|event| {
    match event.menu_item_id() {
        "ctx_copy" => { /* 通过 emit 通知前端复制 */ }
        "ctx_paste" => { /* 同理 */ }
        "ctx_inspect" => {
            // 打开开发者工具(仅调试用)
            event.window().open_devtools();
        }
        _ => {}
    }
});

前端触发(使用 JavaScript):

window.addEventListener('contextmenu', (e) => {
    e.preventDefault();
    // 调用 Rust 命令,传入客户端坐标
    window.__TAURI__.invoke('show_context_menu', {
        x: e.clientX,
        y: e.clientY,
    });
});

注意事项:

  • 如果只想在某些元素上显示菜单,可以在前端判断 e.target 是否符合条件再调用命令。
  • window.popup_menu 的坐标是基于窗口的物理坐标,传入 clientX/Y 即可。
  • 菜单事件与前文全局菜单事件共用一个监听器,务必通过 menu_item_id 区分来源。

3. 系统菜单(系统托盘菜单)

系统托盘菜单是附着在系统托盘图标上的菜单,通常用于后台驻留应用(如音乐播放器、网盘客户端)。Tauri 通过 tauri::SystemTray 提供完整支持。

实现步骤:

  1. 在 Rust 中创建 SystemTray 对象,包含图标和菜单。
  2. Builder 上通过 .system_tray(tray) 安装。
  3. 可选:监听托盘图标左键点击事件,显示或隐藏主窗口。
  4. 菜单项事件在统一的 .on_system_tray_event() 中处理。

简单示例:

use tauri::{CustomMenuItem, Menu, MenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu};

fn main() {
    let show = CustomMenuItem::new("show".to_string(), "显示窗口");
    let quit = CustomMenuItem::new("quit".to_string(), "退出");

    let tray_menu = SystemTrayMenu::new()
        .add_item(show)
        .add_native_item(MenuItem::Separator)
        .add_item(quit);

    let system_tray = SystemTray::new()
        .with_menu(tray_menu)
        .with_icon(tauri::Icon::Raw(include_bytes!("../icons/tray-icon.png").to_vec()));

    tauri::Builder::default()
        .system_tray(system_tray)
        .on_system_tray_event(|app, event| match event {
            SystemTrayEvent::LeftClick { .. } => {
                // 左键切换窗口可见性
                let window = app.get_window("main").unwrap();
                if window.is_visible().unwrap_or(false) {
                    window.hide().unwrap();
                } else {
                    window.show().unwrap();
                    window.set_focus().unwrap();
                }
            }
            SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
                "show" => {
                    let window = app.get_window("main").unwrap();
                    window.show().unwrap();
                    window.set_focus().unwrap();
                }
                "quit" => {
                    std::process::exit(0);
                }
                _ => {}
            },
            _ => {}
        })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

关键要点:

  • 图标建议使用 .png(最好是 32x32 或 64x64),直接编译进二进制。
  • 退出时若仅隐藏主窗口,应用会继续在后台运行,托盘图标保持活跃。
  • 如果不需要主窗口显示,可以在 tauri.conf.json 中将 windows.main.visible 设为 false,然后在托盘菜单中手动显示。
  • 在 macOS 上,托盘菜单使用原生模板图像效果更佳;Windows 上可以使用彩色图标。

小结:
Tauri 的菜单系统强制将结构与事件放在 Rust 端,保证了安全性和平台一致性。通过简单的配置和事件监听,你可以轻松实现应用必备的菜单栏、右键快捷菜单和系统托盘菜单,并优雅地与前端交互。