unistore-tray 0.1.0

System tray capability for UniStore - cross-platform tray icon, menu, and notifications
Documentation
//! 【系统托盘】- SystemTray 主入口
//!
//! 职责:
//! - 提供统一的跨平台托盘 API
//! - 管理托盘生命周期

use crate::backend::{PlatformTray, PlatformTrayOps};
use crate::config::TrayConfig;
use crate::error::TrayResult;
use crate::event::TrayEvent;
use crate::icon::TrayIcon;
use crate::menu::Menu;

use tokio::sync::broadcast;

/// 系统托盘
///
/// 跨平台的系统托盘实现,支持 Windows/macOS/Linux。
///
/// # 示例
///
/// ```rust,no_run
/// use unistore_tray::{SystemTray, TrayConfig, MenuBuilder};
///
/// let config = TrayConfig::builder()
///     .tooltip("My App")
///     .build();
///
/// let tray = SystemTray::new(config)?;
///
/// let menu = MenuBuilder::new()
///     .item("exit", "退出")
///     .build();
/// tray.set_menu(menu)?;
///
/// // 订阅事件
/// let mut rx = tray.subscribe();
/// # Ok::<(), unistore_tray::TrayError>(())
/// ```
pub struct SystemTray {
    inner: PlatformTray,
    event_tx: broadcast::Sender<TrayEvent>,
}

impl SystemTray {
    /// 创建系统托盘
    ///
    /// # Arguments
    /// * `config` - 托盘配置
    ///
    /// # Returns
    /// 成功返回 SystemTray 实例,失败返回 TrayError
    ///
    /// # 注意
    /// 在某些平台上(如 macOS),托盘需要在主线程创建。
    pub fn new(config: TrayConfig) -> TrayResult<Self> {
        let (event_tx, _) = broadcast::channel(config.event_channel_capacity);
        let inner = PlatformTray::new(&config, event_tx.clone())?;

        Ok(Self { inner, event_tx })
    }

    /// 设置托盘图标
    ///
    /// # Arguments
    /// * `icon` - 新图标
    pub fn set_icon(&self, icon: &TrayIcon) -> TrayResult<()> {
        self.inner.set_icon(icon)
    }

    /// 设置提示文字(鼠标悬停显示)
    ///
    /// # Arguments
    /// * `tooltip` - 提示文字
    pub fn set_tooltip(&self, tooltip: &str) -> TrayResult<()> {
        self.inner.set_tooltip(tooltip)
    }

    /// 设置右键菜单
    ///
    /// # Arguments
    /// * `menu` - 菜单
    pub fn set_menu(&self, menu: Menu) -> TrayResult<()> {
        self.inner.set_menu(&menu)
    }

    /// 订阅托盘事件
    ///
    /// 返回一个 broadcast Receiver,用于接收托盘事件。
    /// 可以多次调用获取多个 Receiver。
    ///
    /// # 示例
    ///
    /// ```rust,no_run
    /// # use unistore_tray::{SystemTray, TrayConfig, TrayEvent};
    /// # let tray = SystemTray::new(TrayConfig::default())?;
    /// let mut rx = tray.subscribe();
    ///
    /// tokio::spawn(async move {
    ///     while let Ok(event) = rx.recv().await {
    ///         match event {
    ///             TrayEvent::Click => println!("点击"),
    ///             TrayEvent::MenuItemClicked(id) => {
    ///                 println!("菜单: {}", id);
    ///             }
    ///             _ => {}
    ///         }
    ///     }
    /// });
    /// # Ok::<(), unistore_tray::TrayError>(())
    /// ```
    pub fn subscribe(&self) -> broadcast::Receiver<TrayEvent> {
        self.event_tx.subscribe()
    }

    /// 销毁托盘
    ///
    /// 调用后托盘图标将从系统托盘移除。
    /// 此操作会发送 `TrayEvent::Destroyed` 事件。
    pub fn destroy(self) -> TrayResult<()> {
        self.inner.destroy()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_create_tray_config() {
        let config = TrayConfig::builder()
            .tooltip("Test")
            .event_channel_capacity(32)
            .build();

        assert_eq!(config.tooltip, "Test");
        assert_eq!(config.event_channel_capacity, 32);
    }

    // 注意:实际的托盘测试需要 GUI 环境,这里只测试配置
}