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