unistore_tray/lib.rs
1//! # unistore-tray
2//!
3//! 跨平台系统托盘能力 - UniStore 能力生态的一部分。
4//!
5//! ## 功能特性
6//!
7//! - **跨平台**: 支持 Windows/macOS/Linux
8//! - **统一 API**: 屏蔽平台差异,提供一致的接口
9//! - **异步友好**: 与 tokio 运行时无缝集成
10//! - **事件驱动**: 通过 broadcast channel 订阅托盘事件
11//! - **可扩展菜单**: 灵活的菜单构建器 API
12//!
13//! ## 快速开始
14//!
15//! ```rust,no_run
16//! use unistore_tray::{SystemTray, TrayConfig, TrayEvent, MenuBuilder};
17//!
18//! #[tokio::main]
19//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
20//! // 创建托盘配置
21//! let config = TrayConfig::builder()
22//! .tooltip("My App v1.0")
23//! .build();
24//!
25//! // 创建系统托盘(需要在主线程)
26//! let tray = SystemTray::new(config)?;
27//!
28//! // 设置菜单
29//! let menu = MenuBuilder::new()
30//! .item("open", "打开主窗口")
31//! .separator()
32//! .item("exit", "退出")
33//! .build();
34//! tray.set_menu(menu)?;
35//!
36//! // 订阅事件
37//! let mut rx = tray.subscribe();
38//!
39//! // 事件处理循环
40//! while let Ok(event) = rx.recv().await {
41//! match event {
42//! TrayEvent::MenuItemClicked(id) if id.as_str() == "exit" => break,
43//! _ => {}
44//! }
45//! }
46//!
47//! Ok(())
48//! }
49//! ```
50
51mod deps;
52mod error;
53mod config;
54mod event;
55mod icon;
56mod menu;
57mod tray;
58mod backend;
59
60// 公开导出
61pub use error::{TrayError, TrayResult};
62pub use config::{TrayConfig, TrayConfigBuilder};
63pub use event::{TrayEvent, MenuItemId};
64pub use icon::TrayIcon;
65pub use menu::{Menu, MenuItem, MenuBuilder};
66pub use tray::SystemTray;
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_config_builder() {
74 let config = TrayConfig::builder()
75 .tooltip("Test App")
76 .event_channel_capacity(128)
77 .build();
78
79 assert_eq!(config.tooltip, "Test App");
80 assert_eq!(config.event_channel_capacity, 128);
81 }
82
83 #[test]
84 fn test_menu_builder() {
85 let menu = MenuBuilder::new()
86 .item("open", "打开")
87 .separator()
88 .check_item("auto_start", "开机启动", true)
89 .item("exit", "退出")
90 .build();
91
92 assert_eq!(menu.items().len(), 4);
93 }
94
95 #[test]
96 fn test_menu_item_id() {
97 let id1: MenuItemId = "test".into();
98 let id2 = MenuItemId::new("test");
99 assert_eq!(id1, id2);
100 assert_eq!(id1.as_str(), "test");
101 }
102
103 #[test]
104 fn test_tray_event_debug() {
105 let event = TrayEvent::Click;
106 assert!(format!("{:?}", event).contains("Click"));
107
108 let event = TrayEvent::MenuItemClicked(MenuItemId::new("exit"));
109 assert!(format!("{:?}", event).contains("exit"));
110 }
111}