Tauri(五)——实现托盘菜单和图标切换功能
2025年1月17日 22:47
前言
在桌面端应用中,托盘图标是常见的功能,本文将以 Tauri V2 框架为例,展示如何实现托盘菜单以及根据主题切换托盘图标的功能。以下是效果截图和详细实现步骤和代码说明。
1. 修改 Cargo.toml
添加依赖
首先,在 src-tauri/Cargo.toml
文件中添加如下依赖:
[dependencies]
tauri = { version = "2.0.6", features = ["tray-icon", "image-png"] }
-
tray-icon
: 启用托盘图标功能。 -
image-png
: 支持自定义 PNG 图标。
2. 实现托盘菜单功能
在 Rust 中,我们创建一个 enable_tray
函数,用于初始化托盘菜单及其事件。
enable_tray
函数
fn enable_tray(app: &mut tauri::App) {
use tauri::{
image::Image,
menu::{MenuBuilder, MenuItem},
tray::TrayIconBuilder,
};
// 退出按钮
let quit_i = MenuItem::with_id(app, "quit", "Quit Coco", true, None::<&str>).unwrap();
// 设置按钮
let settings_i = MenuItem::with_id(app, "settings", "Settings...", true, None::<&str>).unwrap();
// 打开按钮
let open_i = MenuItem::with_id(app, "open", "Open Coco", true, None::<&str>).unwrap();
// 关于按钮
let about_i = MenuItem::with_id(app, "about", "About Coco", true, None::<&str>).unwrap();
// 隐藏按钮
let hide_i = MenuItem::with_id(app, "hide", "Hide Coco", true, None::<&str>).unwrap();
// ......
// 按照一定顺序 把按钮 放到 菜单里
let menu = MenuBuilder::new(app)
.item(&open_i)
.separator() // 分割线
.item(&hide_i)
.item(&about_i)
.item(&settings_i)
.separator() // 分割线
.item(&quit_i)
.build()
.unwrap();
let _tray = TrayIconBuilder::with_id("tray")
// .icon(app.default_window_icon().unwrap().clone()) // 默认的图片
.icon(Image::from_bytes(include_bytes!("../icons/light@2x.png")).expect("REASON")) // 自定义的图片
.menu(&menu)
.on_menu_event(|app, event| match event.id.as_ref() {
"open" => {
handle_open_coco(app); // 打开事件
}
"hide" => {
handle_hide_coco(app);
}
"about" => {
let _ = app.emit("open_settings", "about");
}
"settings" => {
// windows failed to open second window, issue: https://github.com/tauri-apps/tauri/issues/11144 https://github.com/tauri-apps/tauri/issues/8196
//#[cfg(windows)]
let _ = app.emit("open_settings", "");
// #[cfg(not(windows))]
// open_settings(&app);
}
"quit" => {
println!("quit menu item was clicked");
app.exit(0);
}
_ => {
println!("menu item {:?} not handled", event.id);
}
})
.build(app)
.unwrap();
}
功能说明
-
菜单项创建:使用
MenuItem::with_id
方法创建菜单项并设置唯一 ID 和显示文本。 -
菜单构建:通过
MenuBuilder
组合菜单项并添加分隔符。 -
托盘图标构建:通过
TrayIconBuilder
设置图标、菜单及点击事件。 -
事件监听:在
on_menu_event
中根据菜单项 ID 处理对应事件。
3. 注册托盘菜单
在 Tauri 应用启动时,调用 enable_tray
注册托盘菜单。
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let mut ctx = tauri::generate_context!();
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
switch_tray_icon, // 切换托盘图标
])
.setup(|app| {
init(app.app_handle());
enable_tray(app); // 注册事件
Ok(())
})
.run(ctx)
.expect("error while running tauri application");
}
4. 实现托盘图标切换
为了根据主题切换托盘图标,我们需要创建一个 switch_tray_icon
命令。
switch_tray_icon
命令
#[tauri::command]
fn switch_tray_icon(app: tauri::AppHandle, is_dark_mode: bool) {
let app_handle = app.app_handle();
println!("is_dark_mode: {}", is_dark_mode);
const DARK_ICON_PATH: &[u8] = include_bytes!("../icons/dark@2x.png");
const LIGHT_ICON_PATH: &[u8] = include_bytes!("../icons/light@2x.png");
// 根据 app 的主题切换 图标
let icon_path: &[u8] = if is_dark_mode {
DARK_ICON_PATH
} else {
LIGHT_ICON_PATH
};
// 获取托盘
let tray = match app_handle.tray_by_id("tray") {
Some(tray) => tray,
None => {
eprintln!("Tray with ID 'tray' not found");
return;
}
};
// 设置图标
if let Err(e) = tray.set_icon(Some(
tauri::image::Image::from_bytes(icon_path)
.unwrap_or_else(|e| panic!("Failed to load icon from bytes: {}", e)),
)) {
eprintln!("Failed to set tray icon: {}", e);
}
}
代码说明
-
动态加载图标:根据
is_dark_mode
参数决定使用亮色或暗色图标。 -
更新托盘图标:通过
set_icon
方法更新图标。 - 错误处理:在托盘实例不存在或图标加载失败时记录错误日志。
5. 前端调用 Rust 命令
前端可以通过 Tauri 的 invoke
API 调用 switch_tray_icon
命令。
示例代码
import { invoke } from "@tauri-apps/api/core";
async function switchTrayIcon(value: "dark" | "light") {
try {
// invoke switch_tray_icon 事件名 isDarkMode 参数名
await invoke("switch_tray_icon", { isDarkMode: value === "dark" });
} catch (err) {
console.error("Failed to switch tray icon:", err);
}
}
在主题切换时调用 switchTrayIcon
即可实现图标动态切换。
小结
通过本文的实现,我们完成了以下功能:
- 创建自定义托盘菜单。(更丰富的菜单内容可以自行扩展了)
- 响应托盘菜单事件。
- 根据主题动态切换托盘图标。(不仅仅可以主题切换图标,还可以依据 app 行为修改对应的图标)
这种方式为 Tauri 应用提供了更加友好的用户体验。如果有其他需求,可以在菜单事件中扩展更多功能。
参考
开源
最近,我正在基于 Tauri 开发一款项目,名为 Coco。目前已开源,项目仍在不断完善中,欢迎大家前往支持并为项目点亮免费的 star 🌟!
作为个人的第一个 Tauri 项目,开发过程中也是边探索边学习。希望能与志同道合的朋友一起交流经验、共同成长!
代码中如有问题或不足之处,期待小伙伴们的宝贵建议和指导!
- 官网: coco.rs/
- 前端仓库: github.com/infinilabs/…
- 服务端仓库: github.com/infinilabs/…
非常感谢您的支持与关注!