diff --git a/src/content/docs/zh-cn/learn/window-menu.mdx b/src/content/docs/zh-cn/learn/window-menu.mdx new file mode 100644 index 0000000000..124dfc81c9 --- /dev/null +++ b/src/content/docs/zh-cn/learn/window-menu.mdx @@ -0,0 +1,529 @@ +--- +title: 窗口菜单 +tableOfContents: + maxHeadingLevel: 4 +i18nReady: true +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +原生应用程序菜单可附加到窗口或系统托盘。仅适用于桌面平台。 + +## 创建一个基础菜单 + +要创建一个基础的本机窗口菜单,并将其附加到一个窗口。 + + + + +使用 [`Menu.new`] 静态函数创建一个窗口菜单。 + +```javascript +import { Menu } from '@tauri-apps/api/menu'; + +const menu = await Menu.new({ + items: [ + { + id: 'quit', + text: 'Quit', + action: () => { + console.log('quit pressed'); + }, + }, + ], +}); + +// 如果某个窗口未显式创建菜单,或者未显式设置菜单,那么此菜单将被分配给它。 +menu.setAsAppMenu().then((res) => { + console.log('menu set success', res); +}); +``` + + + + + +```rust +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use tauri::menu::{Menu, MenuItem}; + +fn main() { + tauri::Builder::default() + .setup(|app| { + let menu = MenuBuilder::new(app) + .text("open", "Open") + .text("close", "Close") + .build()?; + + app.set_menu(menu)?; + + Ok(()) + }) +} +``` + + + + +## 监听自定义菜单项上事件 + +每个自定义菜单项在被点击时都会触发一个事件。使用 `on_menu_event` API 来处理这些事件。 + + + + +```javascript +import { Menu } from '@tauri-apps/api/menu'; + +const menu = await Menu.new({ + items: [ + { + id: 'Open', + text: 'open', + action: () => { + console.log('open pressed'); + }, + }, + { + id: 'Close', + text: 'close', + action: () => { + console.log('close pressed'); + }, + }, + ], +}); + +await menu.setAsAppMenu(); +``` + + + + + +```rust +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use tauri::menu::{MenuBuilder}; + +fn main() { + tauri::Builder::default() + .setup(|app| { + let menu = MenuBuilder::new(app) + .text("open", "Open") + .text("close", "Close") + .build()?; + + app.set_menu(menu)?; + + app.on_menu_event(move |app_handle: &tauri::AppHandle, event| { + + println!("menu event: {:?}", event.id()); + + match event.id().0.as_str() { + "open" => { + println!("open event"); + } + "close" => { + println!("close event"); + } + _ => { + println!("unexpected menu event"); + } + } + }); + + Ok(()) + }) +} +``` + + + + +## 创建多级菜单 + +要创建多级菜单,您可以向菜单项添加一些子菜单。 + + + + +```javascript +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use tauri::{image::Image, menu::{CheckMenuItemBuilder, IconMenuItemBuilder, MenuBuilder, SubmenuBuilder}}; + +fn main() { + tauri::Builder::default() + .setup(|app| { + let text_menu = SubmenuBuilder::new(app, "File") + .text("open", "Open") + .text("quit", "Quit") + .build()?; + + let lang_str = "en"; + let check_sub_item_1 = CheckMenuItemBuilder::new("English") + .id("en") + .checked(lang_str == "en") + .build(app)?; + + let check_sub_item_2 = CheckMenuItemBuilder::new("Chinese") + .id("en") + .checked(lang_str == "en") + .enabled(false) + .build(app)?; + + let icon_image = Image::from_bytes(include_bytes!("../icons/icon.png")).unwrap(); + + let icon_item = IconMenuItemBuilder::new("icon") + .icon(icon_image) + .build(app)?; + + let check_menus = SubmenuBuilder::new(app, "language") + .item(&check_sub_item_1) + .item(&check_sub_item_2) + .build()?; + + + let menu = MenuBuilder::new(app) + .items(&[&text_menu, &check_menus, &icon_item]) + .build()?; + + app.set_menu(menu)?; + + print!("Hello from setup"); + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} +``` + + + + + +```rust +use tauri::menu::{CheckMenuItemBuilder, MenuBuilder, SubmenuBuilder}; + +fn main() { + tauri::Builder::default() + .setup(|app| { + let file_menu = SubmenuBuilder::new(app, "File") + .text("open", "Open") + .text("quit", "Quit") + .build()?; + + let lang_str = "en"; + let check_sub_item_1 = CheckMenuItemBuilder::new("English") + .id("en") + .checked(lang_str == "en") + .build(app)?; + + let check_sub_item_2 = CheckMenuItemBuilder::new("Chinese") + .id("en") + .checked(lang_str == "en") + .enabled(false) + .build(app)?; + + // 从路径加载图标 + let icon_image = Image::from_bytes(include_bytes!("../icons/icon.png")).unwrap(); + + let icon_item = IconMenuItemBuilder::new("icon") + .icon(icon_image) + .build(app)?; + + let other_item = SubmenuBuilder::new(app, "language") + .item(&check_sub_item_1) + .item(&check_sub_item_2) + .build()?; + + let menu = MenuBuilder::new(app) + .items(&[&file_menu, &other_item,&icon_item]) + .build()?; + + app.set_menu(menu)?; + + Ok(()) + }) +} +``` + +请注意,您需要启用 `image-ico` 或 `image-png` 功能才能使用此 API。 + +```toml title="src-tauri/Cargo.toml" +[dependencies] +tauri = { version = "...", features = ["...", "image-png"] } +``` + + + + +## 创建预定义菜单 + +若要使用由操作系统或 Tauri 预先定义了行为的内置(原生)菜单项。 + + + + +```javascript +import { Menu, PredefinedMenuItem } from '@tauri-apps/api/menu'; + +const copy = await PredefinedMenuItem.new({ + text: 'copy-text', + item: 'Copy', +}); + +const separator = await PredefinedMenuItem.new({ + text: 'separator-text', + item: 'Separator', +}); + +const undo = await PredefinedMenuItem.new({ + text: 'undo-text', + item: 'Undo', +}); + +const redo = await PredefinedMenuItem.new({ + text: 'redo-text', + item: 'Redo', +}); + +const cut = await PredefinedMenuItem.new({ + text: 'cut-text', + item: 'Cut', +}); + +const paste = await PredefinedMenuItem.new({ + text: 'paste-text', + item: 'Paste', +}); + +const select_all = await PredefinedMenuItem.new({ + text: 'select_all-text', + item: 'SelectAll', +}); + +const menu = await Menu.new({ + items: [copy, separator, undo, redo, cut, paste, select_all], +}); + +await menu.setAsAppMenu(); +``` + + + + + +```rust +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use tauri::menu::{MenuBuilder, PredefinedMenuItem}; + +fn main() { + tauri::Builder::default() + .setup(|app| { + let menu = MenuBuilder::new(app) + .copy() + .separator() + .undo() + .redo() + .cut() + .paste() + .select_all() + .item(&PredefinedMenuItem::copy(app, Some("custom text"))?) + .build()?; + app.set_menu(menu)?; + + Ok(()) + }) +} +``` + +有关更多预设功能,请参阅文档 [`PredefinedMenuItem`]。 + +:::tip +菜单构建器为添加每个预定义的菜单项都设有专门的方法,因此您可以调用 `.copy()` 替代 `.item(&PredefinedMenuItem::copy(app, None)?)`。 +::: + + + + +## 更改菜单状态 + +如果您想要更改菜单的状态,例如文本、图标或选中状态,可以再次使用 `set_menu`。 + + + + +```javascript +import { + Menu, + CheckMenuItem, + IconMenuItem, + MenuItem, +} from '@tauri-apps/api/menu'; +import { Image } from '@tauri-apps/api/image'; + +let currentLanguage = 'en'; + +const check_sub_item_en = await CheckMenuItem.new({ + id: 'en', + text: 'English', + checked: currentLanguage === 'en', + action: () => { + currentLanguage = 'en'; + check_sub_item_en.setChecked(currentLanguage === 'en'); + check_sub_item_zh.setChecked(currentLanguage === 'cn'); + console.log('English pressed'); + }, +}); + +const check_sub_item_zh = await CheckMenuItem.new({ + id: 'zh', + text: 'Chinese', + checked: currentLanguage === 'zh', + action: () => { + currentLanguage = 'zh'; + check_sub_item_en.setChecked(currentLanguage === 'en'); + check_sub_item_zh.setChecked(currentLanguage === 'zh'); + check_sub_item_zh.setAccelerator('Ctrl+L'); + console.log('Chinese pressed'); + }, +}); + +// 从路径加载图标 +const icon = await Image.fromPath('../src/icon.png'); +const icon2 = await Image.fromPath('../src/icon-2.png'); + +const icon_item = await IconMenuItem.new({ + id: 'icon_item', + text: 'Icon Item', + icon: icon, + action: () => { + icon_item.setIcon(icon2); + console.log('icon pressed'); + }, +}); + +const text_item = await MenuItem.new({ + id: 'text_item', + text: 'Text Item', + action: () => { + text_item.setText('Text Item Changed'); + console.log('text pressed'); + }, +}); + +const menu = await Menu.new({ + items: [ + { + id: 'change menu', + text: 'change_menu', + items: [text_item, check_sub_item_en, check_sub_item_zh, icon_item], + }, + ], +}); + +await menu.setAsAppMenu(); +``` + + + + + +```rust +// 更改菜单状态 +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use tauri::{ + image::Image, + menu::{CheckMenuItemBuilder, IconMenuItem, MenuBuilder, MenuItem, SubmenuBuilder}, +}; + +fn main() { + tauri::Builder::default() + .setup(|app| { + let check_sub_item_en = CheckMenuItemBuilder::with_id("en", "EN") + .checked(true) + .build(app)?; + + let check_sub_item_zh = CheckMenuItemBuilder::with_id("zh", "ZH") + .checked(false) + .build(app)?; + + let text_menu = MenuItem::with_id( + app, + "change_text", + &"Change menu".to_string(), + true, + Some("Ctrl+Z"), + ) + .unwrap(); + + let icon_menu = IconMenuItem::with_id( + app, + "change_icon", + &"Change icon menu", + true, + Some(Image::from_bytes(include_bytes!("../icons/icon.png")).unwrap()), + Some("Ctrl+F"), + ) + .unwrap(); + + let menu_item = SubmenuBuilder::new(app, "Change menu") + .item(&text_menu) + .item(&icon_menu) + .items(&[&check_sub_item_en, &check_sub_item_zh]) + .build()?; + let menu = MenuBuilder::new(app).items(&[&menu_item]).build()?; + app.set_menu(menu)?; + app.on_menu_event(move |_app_handle: &tauri::AppHandle, event| { + match event.id().0.as_str() { + "change_text" => { + text_menu + .set_text("changed menu text") + .expect("Change text error"); + + text_menu + .set_text("changed menu text") + .expect("Change text error"); + } + "change_icon" => { + icon_menu + .set_text("changed menu-icon text") + .expect("Change text error"); + icon_menu + .set_icon(Some( + Image::from_bytes(include_bytes!("../icons/icon-2.png")).unwrap(), + )) + .expect("Change icon error"); + } + + "en" | "zh" => { + check_sub_item_en + .set_checked(event.id().0.as_str() == "en") + .expect("Change check error"); + check_sub_item_zh + .set_checked(event.id().0.as_str() == "zh") + .expect("Change check error"); + check_sub_item_zh.set_accelerator(Some("Ctrl+L")) + .expect("Change accelerator error"); + } + _ => { + println!("unexpected menu event"); + } + } + }); + + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} + +``` + + + + +[`PredefinedMenuItem`]: https://docs.rs/tauri/latest/tauri/menu/struct.PredefinedMenuItem.html +[`Menu.new`]: https://v2.tauri.app/reference/javascript/api/namespacemenu/#new-2