workflow_nw/
tray.rs

1//!
2//! Builder for the application system Tray menu.
3//!
4//! # Synopsis
5//! ```rust
6//! use workflow_nw::prelude::*;
7//! use workflow_nw::result::Result;
8//! use workflow_nw::tray::TrayMenuBuilder;
9//! use workflow_dom::utils::window;
10//! use wasm_bindgen::JsValue;
11//!
12//! # fn test()->Result<()>{
13//! // create Tray icon menu without submenus
14//! TrayMenuBuilder::new()
15//!     .icon("resources/icons/tray-icon@2x.png")
16//!     .icons_are_templates(false)
17//!     .callback(|_|{
18//!         window().alert_with_message("Tray menu click")?;
19//!         Ok(())
20//!     })
21//!     .build()?;
22//!
23//! // create Tray menu icon with submenus
24//! let submenu_1 = MenuItemBuilder::new()
25//!     .label("Say hi")
26//!     .key("6")
27//!     .modifiers("ctrl")
28//!     .callback(move |_|->std::result::Result<(), JsValue>{
29//!         window().alert_with_message("hi")?;
30//!         Ok(())
31//!     }).build()?;
32//!     
33//! let exit_menu = MenuItemBuilder::new()
34//!     .label("Exit")
35//!     .callback(move |_|->std::result::Result<(), JsValue>{
36//!         nw_sys::app::close_all_windows();
37//!         Ok(())
38//!     }).build()?;
39//!     
40//! let _tray = TrayMenuBuilder::new()
41//!     .icon("resources/icons/tray-icon@2x.png")
42//!     .icons_are_templates(false)
43//!     .submenus(vec![submenu_1, menu_separator(), exit_menu])
44//!     .build()?;
45//!
46//! # Ok(())
47//! # }
48//!
49//! ```
50//!
51
52use crate::application::app;
53use crate::result::Result;
54use nw_sys::prelude::*;
55use nw_sys::{menu_item::MenuItem, tray::Options, Menu, Tray};
56use wasm_bindgen::prelude::*;
57use web_sys::MouseEvent;
58use workflow_wasm::prelude::*;
59
60/// Provides a builder pattern for constructing a system tray menu
61/// for the application.
62///
63/// For usage example please refer to [Examples](self)
64pub struct TrayMenuBuilder {
65    pub options: Options,
66    pub menu: Option<Menu>,
67    pub tooltip: Option<String>,
68    pub callback: Option<Callback<CallbackClosure<MouseEvent>>>,
69}
70
71impl Default for TrayMenuBuilder {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77impl TrayMenuBuilder {
78    pub fn new() -> Self {
79        Self {
80            options: Options::new(),
81            menu: None,
82            tooltip: None,
83            callback: None,
84        }
85    }
86
87    pub fn set(mut self, key: &str, value: JsValue) -> Self {
88        self.options = self.options.set(key, value);
89        self
90    }
91
92    /// Set the title of the tray.
93    ///
94    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#traytitle)
95    pub fn title(self, title: &str) -> Self {
96        self.set("title", JsValue::from(title))
97    }
98
99    /// Set the tooltip of the tray. tooltip shows when you hover the Tray with mouse.
100    ///
101    /// Note: tooltip is showed on all three platforms.
102    /// Should be set as Tray property rather from option object constructor.
103    ///
104    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#traytooltip)
105    pub fn tooltip(mut self, tooltip: &str) -> Self {
106        self = self.set("tooltip", JsValue::from(tooltip));
107        self.tooltip = Some(tooltip.to_string());
108
109        self
110    }
111
112    /// Set the icon of the tray, icon must receive a path to your icon file.
113    /// It can be a relative path which points to an icon in your app,
114    /// or an absolute path pointing to a file in user’s system.
115    ///
116    /// Mac OS X caveat: when used in notification context,
117    /// png icon is not sized down like in windows notification area,
118    /// it is rather displayed in 1:1 ratio.
119    ///
120    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#trayicon)
121    pub fn icon(self, icon: &str) -> Self {
122        self.set("icon", JsValue::from(icon))
123    }
124
125    /// (Mac) Set the alternate (active) tray icon.
126    ///
127    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#trayalticon-mac)
128    pub fn alticon(self, alticon: &str) -> Self {
129        self.set("alticon", JsValue::from(alticon))
130    }
131
132    /// (Mac) Set whether icon and alticon images are treated as "templates" (true by default).
133    /// When the property is set to true the images are treated as “templates”
134    /// and the system automatically ensures proper styling according to the various
135    /// states of the status item (e.g. dark menu, light menu, etc.).
136    /// Template images should consist only of black and clear colours
137    /// and can use the alpha channel in the image to adjust the opacity of black content.
138    ///
139    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#trayiconsaretemplates-mac)
140    pub fn icons_are_templates(self, icons_are_templates: bool) -> Self {
141        self.set("iconsAreTemplates", JsValue::from(icons_are_templates))
142    }
143
144    /// Set the menu of the tray, menu will be showed when you click on the tray icon.
145    ///
146    /// On Mac OS X the menu will be showed when you click on the
147    /// tray (which is the only action available for tray icons on Mac OS X).
148    /// On Windows and Linux, the menu will be showed when you single click on the
149    /// tray with right mouse button, clicking with left mouse button sends the click
150    /// event and does not show a menu.
151    ///
152    /// In order to reduce differences from different platforms, setting menu property
153    /// is the only way to bind a menu to tray, there’s no way to popup a menu with
154    /// left mouse button click on Linux and Windows.
155    ///
156    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#traymenu)
157    pub fn menu(mut self, menu: Menu) -> Self {
158        self.menu = Some(menu);
159        self
160    }
161
162    /// The callback function when tray icon is clicked.
163    ///
164    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#event-click)
165    pub fn callback<F>(mut self, callback: F) -> Self
166    where
167        F: FnMut(MouseEvent) -> std::result::Result<(), JsValue> + 'static,
168    {
169        self.callback = Some(Callback::new(callback));
170
171        self
172    }
173
174    /// A submenu
175    ///
176    /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Tray/#traymenu)
177    pub fn submenus(self, items: Vec<MenuItem>) -> Self {
178        let submenu = nw_sys::Menu::new();
179        for menu_item in items {
180            submenu.append(&menu_item);
181        }
182
183        self.menu(submenu)
184    }
185
186    pub fn build_impl(self) -> Result<(Tray, Option<Callback<CallbackClosure<MouseEvent>>>)> {
187        let tray = Tray::new(&self.options);
188
189        if let Some(menu) = self.menu {
190            tray.set_menu(&menu);
191        }
192        if let Some(tooltip) = self.tooltip {
193            tray.set_tooltip(&tooltip);
194        }
195
196        if let Some(callback) = self.callback {
197            tray.on("click", callback.as_ref());
198            Ok((tray, Some(callback)))
199        } else {
200            Ok((tray, None))
201        }
202    }
203
204    pub fn build(self) -> Result<Tray> {
205        let (tray, callback) = self.build_impl()?;
206
207        if let Some(callback) = callback {
208            let app = match app() {
209                Some(app) => app,
210                None => return Err("app is not initialized".to_string().into()),
211            };
212            app.callbacks.retain(callback)?;
213        }
214
215        Ok(tray)
216    }
217
218    pub fn finalize(self) -> Result<(Tray, Option<Callback<CallbackClosure<MouseEvent>>>)> {
219        let (tray, callback) = self.build_impl()?;
220
221        Ok((tray, callback))
222    }
223}