123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-License-Identifier: MIT
- pub use tauri_runtime::{
- menu::{
- Menu, MenuEntry, MenuItem, MenuUpdate, Submenu, SystemTrayMenu, SystemTrayMenuEntry,
- SystemTrayMenuItem, TrayHandle,
- },
- Icon, SystemTrayEvent,
- };
- use wry::application::event_loop::EventLoopWindowTarget;
- pub use wry::application::{
- event::TrayEvent,
- event_loop::EventLoopProxy,
- menu::{
- ContextMenu as WryContextMenu, CustomMenuItem as WryCustomMenuItem, MenuItem as WryMenuItem,
- },
- system_tray::Icon as WryTrayIcon,
- TrayId as WryTrayId,
- };
- #[cfg(target_os = "macos")]
- pub use wry::application::platform::macos::{
- CustomMenuItemExtMacOS, SystemTrayBuilderExtMacOS, SystemTrayExtMacOS,
- };
- use wry::application::system_tray::{SystemTray as WrySystemTray, SystemTrayBuilder};
- use crate::{send_user_message, Context, Error, Message, Result, TrayId, TrayMessage};
- use tauri_runtime::{menu::MenuHash, SystemTray, UserEvent};
- use std::{
- collections::HashMap,
- fmt,
- sync::{Arc, Mutex},
- };
- pub type GlobalSystemTrayEventHandler = Box<dyn Fn(TrayId, &SystemTrayEvent) + Send>;
- pub type GlobalSystemTrayEventListeners = Arc<Mutex<Vec<Arc<GlobalSystemTrayEventHandler>>>>;
- pub type SystemTrayEventHandler = Box<dyn Fn(&SystemTrayEvent) + Send>;
- pub type SystemTrayEventListeners = Arc<Mutex<Vec<Arc<SystemTrayEventHandler>>>>;
- pub type SystemTrayItems = Arc<Mutex<HashMap<u16, WryCustomMenuItem>>>;
- #[derive(Clone, Default)]
- pub struct TrayContext {
- pub tray: Arc<Mutex<Option<WrySystemTray>>>,
- pub listeners: SystemTrayEventListeners,
- pub items: SystemTrayItems,
- }
- impl fmt::Debug for TrayContext {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("TrayContext")
- .field("items", &self.items)
- .finish()
- }
- }
- #[derive(Clone, Default)]
- pub struct SystemTrayManager {
- pub trays: Arc<Mutex<HashMap<TrayId, TrayContext>>>,
- pub global_listeners: GlobalSystemTrayEventListeners,
- }
- impl fmt::Debug for SystemTrayManager {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("SystemTrayManager")
- .field("trays", &self.trays)
- .finish()
- }
- }
- /// Wrapper around a [`wry::application::system_tray::Icon`] that can be created from an [`WindowIcon`].
- pub struct TrayIcon(pub(crate) WryTrayIcon);
- impl TryFrom<Icon> for TrayIcon {
- type Error = Error;
- fn try_from(icon: Icon) -> std::result::Result<Self, Self::Error> {
- WryTrayIcon::from_rgba(icon.rgba, icon.width, icon.height)
- .map(Self)
- .map_err(crate::icon_err)
- }
- }
- pub fn create_tray<T>(
- id: WryTrayId,
- system_tray: SystemTray,
- event_loop: &EventLoopWindowTarget<T>,
- ) -> crate::Result<(WrySystemTray, HashMap<u16, WryCustomMenuItem>)> {
- let icon = TrayIcon::try_from(system_tray.icon.expect("tray icon not set"))?;
- let mut items = HashMap::new();
- #[allow(unused_mut)]
- let mut builder = SystemTrayBuilder::new(
- icon.0,
- system_tray
- .menu
- .map(|menu| to_wry_context_menu(&mut items, menu)),
- )
- .with_id(id);
- #[cfg(target_os = "macos")]
- {
- builder = builder
- .with_icon_as_template(system_tray.icon_as_template)
- .with_menu_on_left_click(system_tray.menu_on_left_click);
- if let Some(title) = system_tray.title {
- builder = builder.with_title(&title);
- }
- }
- if let Some(tooltip) = system_tray.tooltip {
- builder = builder.with_tooltip(&tooltip);
- }
- let tray = builder
- .build(event_loop)
- .map_err(|e| Error::SystemTray(Box::new(e)))?;
- Ok((tray, items))
- }
- #[derive(Debug, Clone)]
- pub struct SystemTrayHandle<T: UserEvent> {
- pub(crate) context: Context<T>,
- pub(crate) id: TrayId,
- pub(crate) proxy: EventLoopProxy<super::Message<T>>,
- }
- impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {
- fn set_icon(&self, icon: Icon) -> Result<()> {
- self
- .proxy
- .send_event(Message::Tray(self.id, TrayMessage::UpdateIcon(icon)))
- .map_err(|_| Error::FailedToSendMessage)
- }
- fn set_menu(&self, menu: SystemTrayMenu) -> Result<()> {
- self
- .proxy
- .send_event(Message::Tray(self.id, TrayMessage::UpdateMenu(menu)))
- .map_err(|_| Error::FailedToSendMessage)
- }
- fn update_item(&self, id: u16, update: MenuUpdate) -> Result<()> {
- self
- .proxy
- .send_event(Message::Tray(self.id, TrayMessage::UpdateItem(id, update)))
- .map_err(|_| Error::FailedToSendMessage)
- }
- #[cfg(target_os = "macos")]
- fn set_icon_as_template(&self, is_template: bool) -> tauri_runtime::Result<()> {
- self
- .proxy
- .send_event(Message::Tray(
- self.id,
- TrayMessage::UpdateIconAsTemplate(is_template),
- ))
- .map_err(|_| Error::FailedToSendMessage)
- }
- #[cfg(target_os = "macos")]
- fn set_title(&self, title: &str) -> tauri_runtime::Result<()> {
- self
- .proxy
- .send_event(Message::Tray(
- self.id,
- TrayMessage::UpdateTitle(title.to_owned()),
- ))
- .map_err(|_| Error::FailedToSendMessage)
- }
- fn set_tooltip(&self, tooltip: &str) -> Result<()> {
- self
- .proxy
- .send_event(Message::Tray(
- self.id,
- TrayMessage::UpdateTooltip(tooltip.to_owned()),
- ))
- .map_err(|_| Error::FailedToSendMessage)
- }
- fn destroy(&self) -> Result<()> {
- let (tx, rx) = std::sync::mpsc::channel();
- send_user_message(
- &self.context,
- Message::Tray(self.id, TrayMessage::Destroy(tx)),
- )?;
- rx.recv().unwrap()?;
- Ok(())
- }
- }
- impl From<SystemTrayMenuItem> for crate::MenuItemWrapper {
- fn from(item: SystemTrayMenuItem) -> Self {
- match item {
- SystemTrayMenuItem::Separator => Self(WryMenuItem::Separator),
- _ => unimplemented!(),
- }
- }
- }
- pub fn to_wry_context_menu(
- custom_menu_items: &mut HashMap<MenuHash, WryCustomMenuItem>,
- menu: SystemTrayMenu,
- ) -> WryContextMenu {
- let mut tray_menu = WryContextMenu::new();
- for item in menu.items {
- match item {
- SystemTrayMenuEntry::CustomItem(c) => {
- #[allow(unused_mut)]
- let mut item = tray_menu.add_item(crate::MenuItemAttributesWrapper::from(&c).0);
- #[cfg(target_os = "macos")]
- if let Some(native_image) = c.native_image {
- item.set_native_image(crate::NativeImageWrapper::from(native_image).0);
- }
- custom_menu_items.insert(c.id, item);
- }
- SystemTrayMenuEntry::NativeItem(i) => {
- tray_menu.add_native_item(crate::MenuItemWrapper::from(i).0);
- }
- SystemTrayMenuEntry::Submenu(submenu) => {
- tray_menu.add_submenu(
- &submenu.title,
- submenu.enabled,
- to_wry_context_menu(custom_menu_items, submenu.inner),
- );
- }
- }
- }
- tray_menu
- }
|