系统托盘 #
一、托盘概述 #
1.1 系统托盘功能 #
系统托盘允许应用在后台运行,通过托盘图标提供快速访问功能。
text
┌─────────────────────────────────────────────────────────────┐
│ 系统托盘 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 任务栏 │ │
│ │ [开始] [应用图标] [应用图标] [托盘图标] [时钟] │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │ 显示窗口 │ │ │
│ │ │ 设置 │ │ │
│ │ │ ────────────│ │ │
│ │ │ 退出 │ │ │
│ │ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
1.2 托盘功能 #
| 功能 | 说明 |
|---|---|
| 托盘图标 | 显示在系统托盘区域 |
| 托盘菜单 | 右键点击显示菜单 |
| 托盘提示 | 鼠标悬停显示提示 |
| 托盘事件 | 点击、双击事件 |
二、基本配置 #
2.1 配置托盘图标 #
json
// tauri.conf.json
{
"app": {
"trayIcon": {
"iconPath": "icons/icon.png",
"iconAsTemplate": true,
"id": "main"
}
}
}
2.2 Cargo.toml 配置 #
toml
[dependencies]
tauri = { version = "2", features = ["tray-icon"] }
三、创建托盘 #
3.1 基本托盘 #
rust
use tauri::{
menu::{Menu, MenuItem},
tray::{TrayIcon, TrayIconBuilder},
Manager,
};
fn create_tray(app: &tauri::AppHandle) -> Result<TrayIcon, Box<dyn std::error::Error>> {
let show = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?;
let quit = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&show, &quit])?;
let tray = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.menu(&menu)
.on_menu_event(|app, event| {
match event.id.as_ref() {
"show" => {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
"quit" => {
app.exit(0);
}
_ => {}
}
})
.build(app)?;
Ok(tray)
}
fn main() {
tauri::Builder::default()
.setup(|app| {
create_tray(app.handle())?;
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
3.2 托盘菜单 #
rust
use tauri::menu::{Menu, MenuItem, Submenu, PredefinedMenuItem};
fn create_tray_with_menu(app: &tauri::AppHandle) -> Result<TrayIcon, Box<dyn std::error::Error>> {
let show = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?;
let hide = MenuItem::with_id(app, "hide", "隐藏窗口", true, None::<&str>)?;
let separator = PredefinedMenuItem::separator(app)?;
let settings = MenuItem::with_id(app, "settings", "设置", true, None::<&str>)?;
let about = MenuItem::with_id(app, "about", "关于", true, None::<&str>)?;
let quit = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;
let menu = Menu::with_items(app, &[
&show,
&hide,
&separator,
&settings,
&about,
&separator,
&quit,
])?;
let tray = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.menu(&menu)
.on_menu_event(|app, event| {
match event.id.as_ref() {
"show" => {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
"hide" => {
if let Some(window) = app.get_webview_window("main") {
let _ = window.hide();
}
}
"settings" => {
// 打开设置窗口
}
"about" => {
// 打开关于窗口
}
"quit" => {
app.exit(0);
}
_ => {}
}
})
.build(app)?;
Ok(tray)
}
3.3 子菜单 #
rust
use tauri::menu::{Menu, MenuItem, Submenu};
fn create_submenu(app: &tauri::AppHandle) -> Result<Submenu, Box<dyn std::error::Error>> {
let light = MenuItem::with_id(app, "theme_light", "浅色", true, None::<&str>)?;
let dark = MenuItem::with_id(app, "theme_dark", "深色", true, None::<&str>)?;
let system = MenuItem::with_id(app, "theme_system", "跟随系统", true, None::<&str>)?;
let theme_menu = Menu::with_items(app, &[&light, &dark, &system])?;
let submenu = Submenu::with_items(app, "主题", true, &[&theme_menu])?;
Ok(submenu)
}
四、托盘事件 #
4.1 点击事件 #
rust
use tauri::tray::TrayIconBuilder;
let tray = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.on_tray_icon_event(|tray, event| {
match event {
tauri::tray::TrayIconEvent::Click { button, .. } => {
match button {
tauri::tray::MouseButton::Left => {
// 左键点击
if let Some(window) = tray.app_handle().get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
}
}
tauri::tray::MouseButton::Right => {
// 右键点击,显示菜单
}
_ => {}
}
}
tauri::tray::TrayIconEvent::Double { button, .. } => {
// 双击事件
}
_ => {}
}
})
.build(app)?;
4.2 托盘提示 #
rust
use tauri::tray::TrayIconBuilder;
let tray = TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.tooltip("我的应用")
.build(app)?;
五、动态更新 #
5.1 更新图标 #
rust
use tauri::image::Image;
fn update_tray_icon(tray: &TrayIcon, image_data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let image = Image::from_bytes(image_data)?;
tray.set_icon(Some(image))?;
Ok(())
}
5.2 更新菜单 #
rust
use tauri::menu::MenuItem;
fn update_menu_item(menu_item: &MenuItem, enabled: bool) -> Result<(), Box<dyn std::error::Error>> {
menu_item.set_enabled(enabled)?;
Ok(())
}
5.3 更新提示 #
rust
fn update_tooltip(tray: &TrayIcon, text: &str) -> Result<(), Box<dyn std::error::Error>> {
tray.set_tooltip(Some(text))?;
Ok(())
}
六、后台运行 #
6.1 关闭到托盘 #
rust
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
let window = app.get_webview_window("main").unwrap();
window.on_window_event(move |event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
// 阻止关闭
api.prevent_close();
// 隐藏窗口
let _ = window.hide();
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
6.2 前端控制 #
typescript
import { getCurrentWindow } from '@tauri-apps/api/window';
const window = getCurrentWindow();
// 关闭到托盘
await window.onCloseRequested(async (event) => {
event.preventDefault();
await window.hide();
});
七、托盘通知 #
7.1 显示通知 #
typescript
import { sendNotification } from '@tauri-apps/plugin-notification';
async function showTrayNotification(title: string, body: string) {
await sendNotification({
title,
body,
});
}
7.2 后台任务通知 #
rust
use tauri::Manager;
fn background_task(app: &tauri::AppHandle) {
// 执行后台任务
let result = do_task();
// 发送通知
app.emit("task-complete", result).ok();
}
八、最佳实践 #
8.1 托盘状态管理 #
rust
use std::sync::Mutex;
struct TrayState {
is_running: bool,
notification_count: u32,
}
struct AppState {
tray_state: Mutex<TrayState>,
}
#[tauri::command]
fn set_running(state: tauri::State<AppState>, running: bool) {
let mut tray_state = state.tray_state.lock().unwrap();
tray_state.is_running = running;
}
#[tauri::command]
fn get_running(state: tauri::State<AppState>) -> bool {
state.tray_state.lock().unwrap().is_running
}
8.2 托盘图标状态 #
rust
fn update_tray_status(tray: &TrayIcon, status: &str) -> Result<(), Box<dyn std::error::Error>> {
let icon = match status {
"running" => include_bytes!("../icons/running.png"),
"paused" => include_bytes!("../icons/paused.png"),
"error" => include_bytes!("../icons/error.png"),
_ => include_bytes!("../icons/default.png"),
};
let image = tauri::image::Image::from_bytes(icon)?;
tray.set_icon(Some(image))?;
Ok(())
}
九、总结 #
9.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 托盘创建 | TrayIconBuilder |
| 托盘菜单 | Menu 和 MenuItem |
| 托盘事件 | 点击、双击事件 |
| 后台运行 | 隐藏窗口而非关闭 |
| 动态更新 | 更新图标和菜单 |
9.2 下一步 #
现在你已经掌握了系统托盘,接下来让我们学习 菜单系统,了解如何创建应用菜单!
最后更新:2026-03-28