mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
committed by
GitHub
parent
fd992c547e
commit
4d063ae9ee
7
.changes/runtime-tray.md
Normal file
7
.changes/runtime-tray.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"tauri": minor
|
||||
"tauri-runtime": minor
|
||||
"tauri-runtime-wry": minor
|
||||
---
|
||||
|
||||
Added APIs to create a system tray at runtime.
|
||||
5
.changes/tray-destroy.md
Normal file
5
.changes/tray-destroy.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": minor
|
||||
---
|
||||
|
||||
Added the `SystemTrayHandle::destroy` method.
|
||||
@@ -8,7 +8,7 @@ members = [
|
||||
"core/tauri-utils",
|
||||
"core/tauri-build",
|
||||
"core/tauri-codegen",
|
||||
|
||||
|
||||
# integration tests
|
||||
"core/tests/restart",
|
||||
"core/tests/app-updater"
|
||||
|
||||
@@ -30,16 +30,11 @@ use webview2_com::FocusChangedEventHandler;
|
||||
use windows::Win32::{Foundation::HWND, System::WinRT::EventRegistrationToken};
|
||||
#[cfg(target_os = "macos")]
|
||||
use wry::application::platform::macos::WindowBuilderExtMacOS;
|
||||
#[cfg(all(feature = "system-tray", target_os = "macos"))]
|
||||
use wry::application::platform::macos::{SystemTrayBuilderExtMacOS, SystemTrayExtMacOS};
|
||||
#[cfg(target_os = "linux")]
|
||||
use wry::application::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
|
||||
#[cfg(windows)]
|
||||
use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWindows};
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
use wry::application::system_tray::{SystemTray as WrySystemTray, SystemTrayBuilder};
|
||||
|
||||
use tauri_utils::{config::WindowConfig, debug_eprintln, Theme};
|
||||
use uuid::Uuid;
|
||||
use wry::{
|
||||
@@ -91,7 +86,6 @@ use std::{
|
||||
HashMap, HashSet,
|
||||
},
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
@@ -104,6 +98,8 @@ use std::{
|
||||
pub type WebviewId = u64;
|
||||
type IpcHandler = dyn Fn(&Window, String) + 'static;
|
||||
type FileDropHandler = dyn Fn(&Window, WryFileDropEvent) -> bool + 'static;
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
pub use tauri_runtime::TrayId;
|
||||
|
||||
#[cfg(desktop)]
|
||||
mod webview;
|
||||
@@ -173,7 +169,6 @@ fn send_user_message<T: UserEvent>(context: &Context<T>, message: Message<T>) ->
|
||||
&context.main_thread.window_target,
|
||||
message,
|
||||
UserMessageContext {
|
||||
marker: &PhantomData,
|
||||
webview_id_map: context.webview_id_map.clone(),
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
global_shortcut_manager: context.main_thread.global_shortcut_manager.clone(),
|
||||
@@ -181,7 +176,7 @@ fn send_user_message<T: UserEvent>(context: &Context<T>, message: Message<T>) ->
|
||||
clipboard_manager: context.main_thread.clipboard_manager.clone(),
|
||||
windows: context.main_thread.windows.clone(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context: &context.main_thread.tray_context,
|
||||
system_tray_manager: context.main_thread.system_tray_manager.clone(),
|
||||
},
|
||||
&context.main_thread.web_context,
|
||||
);
|
||||
@@ -256,7 +251,7 @@ pub struct DispatcherMainThreadContext<T: UserEvent> {
|
||||
pub clipboard_manager: Arc<Mutex<Clipboard>>,
|
||||
pub windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
pub tray_context: TrayContext,
|
||||
system_tray_manager: SystemTrayManager,
|
||||
}
|
||||
|
||||
// SAFETY: we ensure this type is only used on the main thread.
|
||||
@@ -1086,7 +1081,8 @@ pub enum TrayMessage {
|
||||
UpdateIcon(Icon),
|
||||
#[cfg(target_os = "macos")]
|
||||
UpdateIconAsTemplate(bool),
|
||||
Close,
|
||||
Create(SystemTray, Sender<Result<()>>),
|
||||
Destroy,
|
||||
}
|
||||
|
||||
pub type CreateWebviewClosure<T> = Box<
|
||||
@@ -1098,7 +1094,7 @@ pub enum Message<T: 'static> {
|
||||
Window(WebviewId, WindowMessage),
|
||||
Webview(WebviewId, WebviewMessage),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
Tray(TrayMessage),
|
||||
Tray(TrayId, TrayMessage),
|
||||
CreateWebview(WebviewId, CreateWebviewClosure<T>),
|
||||
CreateWindow(
|
||||
WebviewId,
|
||||
@@ -1117,7 +1113,7 @@ impl<T: UserEvent> Clone for Message<T> {
|
||||
match self {
|
||||
Self::Webview(i, m) => Self::Webview(*i, m.clone()),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
Self::Tray(m) => Self::Tray(m.clone()),
|
||||
Self::Tray(i, m) => Self::Tray(*i, m.clone()),
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
Self::GlobalShortcut(m) => Self::GlobalShortcut(m.clone()),
|
||||
#[cfg(feature = "clipboard")]
|
||||
@@ -1525,23 +1521,6 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct TrayContext {
|
||||
tray: Arc<Mutex<Option<Arc<Mutex<WrySystemTray>>>>>,
|
||||
listeners: SystemTrayEventListeners,
|
||||
items: SystemTrayItems,
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
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)]
|
||||
enum WindowHandle {
|
||||
Webview(Arc<WebView>),
|
||||
@@ -1650,7 +1629,10 @@ impl<T: UserEvent> fmt::Debug for Wry<T> {
|
||||
.field("web_context", &self.context.main_thread.web_context);
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
d.field("tray_context", &self.context.main_thread.tray_context);
|
||||
d.field(
|
||||
"system_tray_manager",
|
||||
&self.context.main_thread.system_tray_manager,
|
||||
);
|
||||
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
#[cfg(feature = "global-shortcut")]
|
||||
@@ -1741,9 +1723,22 @@ impl<T: UserEvent> RuntimeHandle<T> for WryHandle<T> {
|
||||
send_user_message(&self.context, Message::Task(Box::new(f)))
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "system-tray"))]
|
||||
fn remove_system_tray(&self) -> Result<()> {
|
||||
send_user_message(&self.context, Message::Tray(TrayMessage::Close))
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
fn system_tray(
|
||||
&self,
|
||||
system_tray: SystemTray,
|
||||
) -> Result<<Self::Runtime as Runtime<T>>::TrayHandler> {
|
||||
let id = system_tray.id;
|
||||
let (tx, rx) = channel();
|
||||
send_user_message(
|
||||
&self.context,
|
||||
Message::Tray(id, TrayMessage::Create(system_tray, tx)),
|
||||
)?;
|
||||
rx.recv().unwrap()?;
|
||||
Ok(SystemTrayHandle {
|
||||
id,
|
||||
proxy: self.context.proxy.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
@@ -1766,7 +1761,7 @@ impl<T: UserEvent> Wry<T> {
|
||||
let webview_id_map = WebviewIdStore::default();
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
let tray_context = TrayContext::default();
|
||||
let system_tray_manager = Default::default();
|
||||
|
||||
let context = Context {
|
||||
webview_id_map,
|
||||
@@ -1781,7 +1776,7 @@ impl<T: UserEvent> Wry<T> {
|
||||
clipboard_manager,
|
||||
windows,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context,
|
||||
system_tray_manager,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1906,50 +1901,45 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
fn system_tray(&self, system_tray: SystemTray) -> Result<Self::TrayHandler> {
|
||||
let icon = TrayIcon::try_from(system_tray.icon.expect("tray icon not set"))?;
|
||||
|
||||
let mut items = HashMap::new();
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut tray_builder = SystemTrayBuilder::new(
|
||||
icon.0,
|
||||
system_tray
|
||||
.menu
|
||||
.map(|menu| to_wry_context_menu(&mut items, menu)),
|
||||
);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
tray_builder = tray_builder
|
||||
.with_icon_as_template(system_tray.icon_as_template)
|
||||
.with_menu_on_left_click(system_tray.menu_on_left_click);
|
||||
fn system_tray(&self, mut system_tray: SystemTray) -> Result<Self::TrayHandler> {
|
||||
let id = system_tray.id;
|
||||
let mut listeners = Vec::new();
|
||||
if let Some(l) = system_tray.on_event.take() {
|
||||
listeners.push(Arc::new(l));
|
||||
}
|
||||
|
||||
let tray = tray_builder
|
||||
.build(&self.event_loop)
|
||||
.map_err(|e| Error::SystemTray(Box::new(e)))?;
|
||||
|
||||
*self.context.main_thread.tray_context.items.lock().unwrap() = items;
|
||||
*self.context.main_thread.tray_context.tray.lock().unwrap() = Some(Arc::new(Mutex::new(tray)));
|
||||
let (tray, items) = create_tray(WryTrayId(id), system_tray, &self.event_loop)?;
|
||||
self
|
||||
.context
|
||||
.main_thread
|
||||
.system_tray_manager
|
||||
.trays
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(
|
||||
id,
|
||||
TrayContext {
|
||||
tray: Arc::new(Mutex::new(Some(tray))),
|
||||
listeners: Arc::new(Mutex::new(listeners)),
|
||||
items: Arc::new(Mutex::new(items)),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(SystemTrayHandle {
|
||||
id,
|
||||
proxy: self.event_loop.create_proxy(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
fn on_system_tray_event<F: Fn(&SystemTrayEvent) + Send + 'static>(&mut self, f: F) -> Uuid {
|
||||
let id = Uuid::new_v4();
|
||||
fn on_system_tray_event<F: Fn(TrayId, &SystemTrayEvent) + Send + 'static>(&mut self, f: F) {
|
||||
self
|
||||
.context
|
||||
.main_thread
|
||||
.tray_context
|
||||
.listeners
|
||||
.system_tray_manager
|
||||
.global_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(id, Arc::new(Box::new(f)));
|
||||
id
|
||||
.push(Arc::new(Box::new(f)));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -1972,7 +1962,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
let web_context = &self.context.main_thread.web_context;
|
||||
let plugins = &mut self.plugins;
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
let tray_context = self.context.main_thread.tray_context.clone();
|
||||
let system_tray_manager = self.context.main_thread.system_tray_manager.clone();
|
||||
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
let global_shortcut_manager = self.context.main_thread.global_shortcut_manager.clone();
|
||||
@@ -2010,7 +2000,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context: &tray_context,
|
||||
system_tray_manager: system_tray_manager.clone(),
|
||||
},
|
||||
web_context,
|
||||
);
|
||||
@@ -2034,7 +2024,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context: &tray_context,
|
||||
system_tray_manager: system_tray_manager.clone(),
|
||||
},
|
||||
web_context,
|
||||
);
|
||||
@@ -2050,7 +2040,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
let mut plugins = self.plugins;
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
let tray_context = self.context.main_thread.tray_context;
|
||||
let system_tray_manager = self.context.main_thread.system_tray_manager;
|
||||
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
let global_shortcut_manager = self.context.main_thread.global_shortcut_manager.clone();
|
||||
@@ -2080,7 +2070,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context: &tray_context,
|
||||
system_tray_manager: system_tray_manager.clone(),
|
||||
},
|
||||
&web_context,
|
||||
);
|
||||
@@ -2103,7 +2093,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context: &tray_context,
|
||||
system_tray_manager: system_tray_manager.clone(),
|
||||
},
|
||||
&web_context,
|
||||
);
|
||||
@@ -2122,12 +2112,10 @@ pub struct EventLoopIterationContext<'a, T: UserEvent> {
|
||||
#[cfg(feature = "clipboard")]
|
||||
pub clipboard_manager: Arc<Mutex<Clipboard>>,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
pub tray_context: &'a TrayContext,
|
||||
pub system_tray_manager: SystemTrayManager,
|
||||
}
|
||||
|
||||
struct UserMessageContext<'a> {
|
||||
#[allow(dead_code)]
|
||||
marker: &'a PhantomData<()>,
|
||||
struct UserMessageContext {
|
||||
webview_id_map: WebviewIdStore,
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
global_shortcut_manager: Arc<Mutex<WryShortcutManager>>,
|
||||
@@ -2135,17 +2123,16 @@ struct UserMessageContext<'a> {
|
||||
clipboard_manager: Arc<Mutex<Clipboard>>,
|
||||
windows: Arc<Mutex<HashMap<WebviewId, WindowWrapper>>>,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context: &'a TrayContext,
|
||||
system_tray_manager: SystemTrayManager,
|
||||
}
|
||||
|
||||
fn handle_user_message<T: UserEvent>(
|
||||
event_loop: &EventLoopWindowTarget<Message<T>>,
|
||||
message: Message<T>,
|
||||
context: UserMessageContext<'_>,
|
||||
context: UserMessageContext,
|
||||
web_context: &WebContextStore,
|
||||
) -> RunIteration {
|
||||
let UserMessageContext {
|
||||
marker: _,
|
||||
webview_id_map,
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
global_shortcut_manager,
|
||||
@@ -2153,7 +2140,7 @@ fn handle_user_message<T: UserEvent>(
|
||||
clipboard_manager,
|
||||
windows,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context,
|
||||
system_tray_manager,
|
||||
} = context;
|
||||
match message {
|
||||
Message::Task(task) => task(),
|
||||
@@ -2455,49 +2442,74 @@ fn handle_user_message<T: UserEvent>(
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
Message::Tray(tray_message) => match tray_message {
|
||||
TrayMessage::UpdateItem(menu_id, update) => {
|
||||
let mut tray = tray_context.items.as_ref().lock().unwrap();
|
||||
let item = tray.get_mut(&menu_id).expect("menu item not found");
|
||||
match update {
|
||||
MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
|
||||
MenuUpdate::SetTitle(title) => item.set_title(&title),
|
||||
MenuUpdate::SetSelected(selected) => item.set_selected(selected),
|
||||
Message::Tray(tray_id, tray_message) => {
|
||||
let mut trays = system_tray_manager.trays.lock().unwrap();
|
||||
|
||||
if let TrayMessage::Create(tray, tx) = tray_message {
|
||||
match create_tray(WryTrayId(tray_id), tray, event_loop) {
|
||||
Ok((tray, items)) => {
|
||||
trays.insert(
|
||||
tray_id,
|
||||
TrayContext {
|
||||
tray: Arc::new(Mutex::new(Some(tray))),
|
||||
listeners: Default::default(),
|
||||
items: Arc::new(Mutex::new(items)),
|
||||
},
|
||||
);
|
||||
|
||||
tx.send(Ok(())).unwrap();
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
tx.send(Err(e)).unwrap();
|
||||
}
|
||||
}
|
||||
} else if let Some(tray_context) = trays.get(&tray_id) {
|
||||
match tray_message {
|
||||
TrayMessage::UpdateItem(menu_id, update) => {
|
||||
let mut tray = tray_context.items.as_ref().lock().unwrap();
|
||||
let item = tray.get_mut(&menu_id).expect("menu item not found");
|
||||
match update {
|
||||
MenuUpdate::SetEnabled(enabled) => item.set_enabled(enabled),
|
||||
MenuUpdate::SetTitle(title) => item.set_title(&title),
|
||||
MenuUpdate::SetSelected(selected) => item.set_selected(selected),
|
||||
#[cfg(target_os = "macos")]
|
||||
MenuUpdate::SetNativeImage(image) => {
|
||||
item.set_native_image(NativeImageWrapper::from(image).0)
|
||||
}
|
||||
}
|
||||
}
|
||||
TrayMessage::UpdateMenu(menu) => {
|
||||
if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
|
||||
let mut items = HashMap::new();
|
||||
tray.set_menu(&to_wry_context_menu(&mut items, menu));
|
||||
*tray_context.items.lock().unwrap() = items;
|
||||
}
|
||||
}
|
||||
TrayMessage::UpdateIcon(icon) => {
|
||||
if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
|
||||
if let Ok(icon) = TrayIcon::try_from(icon) {
|
||||
tray.set_icon(icon.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
MenuUpdate::SetNativeImage(image) => {
|
||||
item.set_native_image(NativeImageWrapper::from(image).0)
|
||||
TrayMessage::UpdateIconAsTemplate(is_template) => {
|
||||
if let Some(tray) = &mut *tray_context.tray.lock().unwrap() {
|
||||
tray.set_icon_as_template(is_template);
|
||||
}
|
||||
}
|
||||
TrayMessage::Create(_tray, _tx) => {
|
||||
// already handled
|
||||
}
|
||||
TrayMessage::Destroy => {
|
||||
*tray_context.tray.lock().unwrap() = None;
|
||||
tray_context.listeners.lock().unwrap().clear();
|
||||
tray_context.items.lock().unwrap().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
TrayMessage::UpdateMenu(menu) => {
|
||||
if let Some(tray) = &*tray_context.tray.lock().unwrap() {
|
||||
let mut items = HashMap::new();
|
||||
tray
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_menu(&to_wry_context_menu(&mut items, menu));
|
||||
*tray_context.items.lock().unwrap() = items;
|
||||
}
|
||||
}
|
||||
TrayMessage::UpdateIcon(icon) => {
|
||||
if let Some(tray) = &*tray_context.tray.lock().unwrap() {
|
||||
if let Ok(icon) = TrayIcon::try_from(icon) {
|
||||
tray.lock().unwrap().set_icon(icon.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
TrayMessage::UpdateIconAsTemplate(is_template) => {
|
||||
if let Some(tray) = &*tray_context.tray.lock().unwrap() {
|
||||
tray.lock().unwrap().set_icon_as_template(is_template);
|
||||
}
|
||||
}
|
||||
TrayMessage::Close => {
|
||||
*tray_context.tray.lock().unwrap() = None;
|
||||
tray_context.listeners.lock().unwrap().clear();
|
||||
tray_context.items.lock().unwrap().clear();
|
||||
}
|
||||
},
|
||||
}
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
Message::GlobalShortcut(message) => {
|
||||
handle_global_shortcut_message(message, &global_shortcut_manager)
|
||||
@@ -2531,7 +2543,7 @@ fn handle_event_loop<T: UserEvent>(
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context,
|
||||
system_tray_manager,
|
||||
} = context;
|
||||
if *control_flow != ControlFlow::Exit {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
@@ -2612,13 +2624,40 @@ fn handle_event_loop<T: UserEvent>(
|
||||
..
|
||||
} => {
|
||||
let event = SystemTrayEvent::MenuItemClick(menu_id.0);
|
||||
let listeners = tray_context.listeners.lock().unwrap().clone();
|
||||
for handler in listeners.values() {
|
||||
handler(&event);
|
||||
|
||||
let trays = system_tray_manager.trays.lock().unwrap();
|
||||
let trays_iter = trays.iter();
|
||||
|
||||
let (mut listeners, mut tray_id) = (None, 0);
|
||||
for (id, tray_context) in trays_iter {
|
||||
let has_menu = {
|
||||
let items = tray_context.items.lock().unwrap();
|
||||
items.contains_key(&menu_id.0)
|
||||
};
|
||||
if has_menu {
|
||||
listeners.replace(tray_context.listeners.clone());
|
||||
tray_id = *id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
drop(trays);
|
||||
if let Some(listeners) = listeners {
|
||||
let listeners = listeners.lock().unwrap();
|
||||
let handlers = listeners.iter();
|
||||
for handler in handlers {
|
||||
handler(&event);
|
||||
}
|
||||
|
||||
let global_listeners = system_tray_manager.global_listeners.lock().unwrap();
|
||||
let global_listeners_iter = global_listeners.iter();
|
||||
for global_listener in global_listeners_iter {
|
||||
global_listener(tray_id, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
Event::TrayEvent {
|
||||
id,
|
||||
bounds,
|
||||
event,
|
||||
position: _cursor_position,
|
||||
@@ -2634,10 +2673,13 @@ fn handle_event_loop<T: UserEvent>(
|
||||
// default to left click
|
||||
_ => SystemTrayEvent::LeftClick { position, size },
|
||||
};
|
||||
let listeners = tray_context.listeners.lock().unwrap();
|
||||
let handlers = listeners.values();
|
||||
for handler in handlers {
|
||||
handler(&event);
|
||||
let trays = system_tray_manager.trays.lock().unwrap();
|
||||
if let Some(tray_context) = trays.get(&id.0) {
|
||||
let listeners = tray_context.listeners.lock().unwrap();
|
||||
let iter = listeners.iter();
|
||||
for handler in iter {
|
||||
handler(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
@@ -2715,7 +2757,6 @@ fn handle_event_loop<T: UserEvent>(
|
||||
event_loop,
|
||||
message,
|
||||
UserMessageContext {
|
||||
marker: &PhantomData,
|
||||
webview_id_map,
|
||||
#[cfg(all(desktop, feature = "global-shortcut"))]
|
||||
global_shortcut_manager,
|
||||
@@ -2723,7 +2764,7 @@ fn handle_event_loop<T: UserEvent>(
|
||||
clipboard_manager,
|
||||
windows,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_context,
|
||||
system_tray_manager,
|
||||
},
|
||||
web_context,
|
||||
);
|
||||
|
||||
@@ -9,6 +9,7 @@ pub use tauri_runtime::{
|
||||
},
|
||||
Icon, SystemTrayEvent,
|
||||
};
|
||||
use wry::application::event_loop::EventLoopWindowTarget;
|
||||
pub use wry::application::{
|
||||
event::TrayEvent,
|
||||
event_loop::EventLoopProxy,
|
||||
@@ -16,26 +17,62 @@ pub use wry::application::{
|
||||
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;
|
||||
pub use wry::application::platform::macos::{
|
||||
CustomMenuItemExtMacOS, SystemTrayBuilderExtMacOS, SystemTrayExtMacOS,
|
||||
};
|
||||
|
||||
use crate::{Error, Message, Result, TrayMessage};
|
||||
use wry::application::system_tray::{SystemTray as WrySystemTray, SystemTrayBuilder};
|
||||
|
||||
use tauri_runtime::{menu::MenuHash, UserEvent};
|
||||
use crate::{Error, Message, Result, TrayId, TrayMessage};
|
||||
|
||||
use uuid::Uuid;
|
||||
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<HashMap<Uuid, Arc<SystemTrayEventHandler>>>>;
|
||||
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);
|
||||
|
||||
@@ -48,8 +85,39 @@ impl TryFrom<Icon> for TrayIcon {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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) id: TrayId,
|
||||
pub(crate) proxy: EventLoopProxy<super::Message<T>>,
|
||||
}
|
||||
|
||||
@@ -57,28 +125,39 @@ impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {
|
||||
fn set_icon(&self, icon: Icon) -> Result<()> {
|
||||
self
|
||||
.proxy
|
||||
.send_event(Message::Tray(TrayMessage::UpdateIcon(icon)))
|
||||
.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(TrayMessage::UpdateMenu(menu)))
|
||||
.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(TrayMessage::UpdateItem(id, update)))
|
||||
.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(TrayMessage::UpdateIconAsTemplate(
|
||||
is_template,
|
||||
)))
|
||||
.send_event(Message::Tray(
|
||||
self.id,
|
||||
TrayMessage::UpdateIconAsTemplate(is_template),
|
||||
))
|
||||
.map_err(|_| Error::FailedToSendMessage)
|
||||
}
|
||||
|
||||
fn destroy(&self) -> Result<()> {
|
||||
self
|
||||
.proxy
|
||||
.send_event(Message::Tray(self.id, TrayMessage::Destroy))
|
||||
.map_err(|_| Error::FailedToSendMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ http = "0.2.4"
|
||||
http-range = "0.1.4"
|
||||
infer = "0.7"
|
||||
raw-window-handle = "0.5"
|
||||
rand = "0.8"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
webview2-com = "0.16.0"
|
||||
|
||||
@@ -34,16 +34,71 @@ use crate::http::{
|
||||
InvalidUri,
|
||||
};
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
use std::fmt;
|
||||
|
||||
pub type TrayId = u16;
|
||||
pub type TrayEventHandler = dyn Fn(&SystemTrayEvent) + Send + 'static;
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SystemTray {
|
||||
pub id: TrayId,
|
||||
pub icon: Option<Icon>,
|
||||
pub menu: Option<menu::SystemTrayMenu>,
|
||||
#[cfg(target_os = "macos")]
|
||||
pub icon_as_template: bool,
|
||||
#[cfg(target_os = "macos")]
|
||||
pub menu_on_left_click: bool,
|
||||
pub on_event: Option<Box<TrayEventHandler>>,
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
impl fmt::Debug for SystemTray {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("SystemTray");
|
||||
d.field("id", &self.id)
|
||||
.field("icon", &self.icon)
|
||||
.field("menu", &self.menu);
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
d.field("icon_as_template", &self.icon_as_template)
|
||||
.field("menu_on_left_click", &self.menu_on_left_click);
|
||||
}
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
impl Clone for SystemTray {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
id: self.id,
|
||||
icon: self.icon.clone(),
|
||||
menu: self.menu.clone(),
|
||||
on_event: None,
|
||||
#[cfg(target_os = "macos")]
|
||||
icon_as_template: self.icon_as_template,
|
||||
#[cfg(target_os = "macos")]
|
||||
menu_on_left_click: self.menu_on_left_click,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
impl Default for SystemTray {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: rand::random(),
|
||||
icon: None,
|
||||
menu: None,
|
||||
#[cfg(target_os = "macos")]
|
||||
icon_as_template: false,
|
||||
#[cfg(target_os = "macos")]
|
||||
menu_on_left_click: false,
|
||||
on_event: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
@@ -57,6 +112,13 @@ impl SystemTray {
|
||||
self.menu.as_ref()
|
||||
}
|
||||
|
||||
/// Sets the tray id.
|
||||
#[must_use]
|
||||
pub fn with_id(mut self, id: TrayId) -> Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the tray icon.
|
||||
#[must_use]
|
||||
pub fn with_icon(mut self, icon: Icon) -> Self {
|
||||
@@ -86,6 +148,12 @@ impl SystemTray {
|
||||
self.menu.replace(menu);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn on_event<F: Fn(&SystemTrayEvent) + Send + 'static>(mut self, f: F) -> Self {
|
||||
self.on_event.replace(Box::new(f));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of user attention requested on a window.
|
||||
@@ -261,9 +329,13 @@ pub trait RuntimeHandle<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'st
|
||||
/// Run a task on the main thread.
|
||||
fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()>;
|
||||
|
||||
#[cfg(all(windows, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(all(windows, feature = "system-tray"))))]
|
||||
fn remove_system_tray(&self) -> Result<()>;
|
||||
/// Adds an icon to the system tray with the specified menu items.
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "system-tray"))))]
|
||||
fn system_tray(
|
||||
&self,
|
||||
system_tray: SystemTray,
|
||||
) -> Result<<Self::Runtime as Runtime<T>>::TrayHandler>;
|
||||
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle;
|
||||
}
|
||||
@@ -348,7 +420,7 @@ pub trait Runtime<T: UserEvent>: Debug + Sized + 'static {
|
||||
/// Registers a system tray event handler.
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
|
||||
fn on_system_tray_event<F: Fn(&SystemTrayEvent) + Send + 'static>(&mut self, f: F) -> Uuid;
|
||||
fn on_system_tray_event<F: Fn(TrayId, &SystemTrayEvent) + Send + 'static>(&mut self, f: F);
|
||||
|
||||
/// Sets the activation policy for the application. It is set to `NSApplicationActivationPolicyRegular` by default.
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -152,6 +152,7 @@ pub trait TrayHandle: fmt::Debug + Clone + Send + Sync {
|
||||
fn update_item(&self, id: u16, update: MenuUpdate) -> crate::Result<()>;
|
||||
#[cfg(target_os = "macos")]
|
||||
fn set_icon_as_template(&self, is_template: bool) -> crate::Result<()>;
|
||||
fn destroy(&self) -> crate::Result<()>;
|
||||
}
|
||||
|
||||
/// A window menu.
|
||||
|
||||
@@ -2329,9 +2329,7 @@ impl Default for UpdaterConfig {
|
||||
#[cfg_attr(feature = "schema", derive(JsonSchema))]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct SystemTrayConfig {
|
||||
/// Path to the icon to use on the system tray.
|
||||
///
|
||||
/// It is forced to be a `.png` file on Linux and macOS, and a `.ico` file on Windows.
|
||||
/// Path to the default icon to use on the system tray.
|
||||
#[serde(alias = "icon-path")]
|
||||
pub icon_path: PathBuf,
|
||||
/// A Boolean value that determines whether the image represents a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc) image on macOS.
|
||||
|
||||
@@ -47,8 +47,6 @@ use std::{
|
||||
use crate::runtime::menu::{Menu, MenuId, MenuIdRef};
|
||||
|
||||
use crate::runtime::RuntimeHandle;
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
use crate::runtime::SystemTrayEvent as RuntimeSystemTrayEvent;
|
||||
|
||||
#[cfg(updater)]
|
||||
use crate::updater;
|
||||
@@ -326,8 +324,6 @@ pub struct AppHandle<R: Runtime> {
|
||||
global_shortcut_manager: R::GlobalShortcutManager,
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: R::ClipboardManager,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_handle: Option<tray::SystemTrayHandle<R>>,
|
||||
/// The updater configuration.
|
||||
#[cfg(updater)]
|
||||
pub(crate) updater_settings: UpdaterSettings,
|
||||
@@ -379,8 +375,6 @@ impl<R: Runtime> Clone for AppHandle<R> {
|
||||
global_shortcut_manager: self.global_shortcut_manager.clone(),
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: self.clipboard_manager.clone(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_handle: self.tray_handle.clone(),
|
||||
#[cfg(updater)]
|
||||
updater_settings: self.updater_settings.clone(),
|
||||
}
|
||||
@@ -403,13 +397,6 @@ impl<R: Runtime> AppHandle<R> {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Removes the system tray.
|
||||
#[cfg(all(windows, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(all(windows, feature = "system-tray"))))]
|
||||
fn remove_system_tray(&self) -> crate::Result<()> {
|
||||
self.runtime_handle.remove_system_tray().map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Adds a Tauri application plugin.
|
||||
/// This function can be used to register a plugin that is loaded dynamically e.g. after login.
|
||||
/// For plugins that are created when the app is started, prefer [`Builder::plugin`].
|
||||
@@ -513,7 +500,9 @@ impl<R: Runtime> AppHandle<R> {
|
||||
}
|
||||
#[cfg(all(windows, feature = "system-tray"))]
|
||||
{
|
||||
let _ = self.remove_system_tray();
|
||||
for tray in self.manager().trays().values() {
|
||||
let _ = tray.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,8 +534,6 @@ pub struct App<R: Runtime> {
|
||||
global_shortcut_manager: R::GlobalShortcutManager,
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: R::ClipboardManager,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_handle: Option<tray::SystemTrayHandle<R>>,
|
||||
handle: AppHandle<R>,
|
||||
}
|
||||
|
||||
@@ -607,14 +594,72 @@ macro_rules! shared_app_impl {
|
||||
updater::builder(self.app_handle())
|
||||
}
|
||||
|
||||
/// Gets a handle to the first system tray.
|
||||
///
|
||||
/// Prefer [`Self::tray_handle_by_id`] when multiple system trays are created.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let app_handle = app.handle();
|
||||
/// SystemTray::new()
|
||||
/// .with_menu(
|
||||
/// SystemTrayMenu::new()
|
||||
/// .add_item(CustomMenuItem::new("quit", "Quit"))
|
||||
/// .add_item(CustomMenuItem::new("open", "Open"))
|
||||
/// )
|
||||
/// .on_event(move |event| {
|
||||
/// let tray_handle = app_handle.tray_handle();
|
||||
/// })
|
||||
/// .build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
|
||||
/// Gets a handle handle to the system tray.
|
||||
pub fn tray_handle(&self) -> tray::SystemTrayHandle<R> {
|
||||
self
|
||||
.tray_handle
|
||||
.clone()
|
||||
.expect("tray not configured; use the `Builder#system_tray` API first.")
|
||||
.manager()
|
||||
.trays()
|
||||
.values()
|
||||
.next()
|
||||
.cloned()
|
||||
.expect("tray not configured; use the `Builder#system_tray`, `App#system_tray` or `AppHandle#system_tray` APIs first.")
|
||||
}
|
||||
|
||||
|
||||
/// Gets a handle to a system tray by its id.
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let app_handle = app.handle();
|
||||
/// let tray_id = "my-tray";
|
||||
/// SystemTray::new()
|
||||
/// .with_id(tray_id)
|
||||
/// .with_menu(
|
||||
/// SystemTrayMenu::new()
|
||||
/// .add_item(CustomMenuItem::new("quit", "Quit"))
|
||||
/// .add_item(CustomMenuItem::new("open", "Open"))
|
||||
/// )
|
||||
/// .on_event(move |event| {
|
||||
/// let tray_handle = app_handle.tray_handle_by_id(tray_id).unwrap();
|
||||
/// })
|
||||
/// .build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
|
||||
pub fn tray_handle_by_id(&self, id: &str) -> Option<tray::SystemTrayHandle<R>> {
|
||||
self
|
||||
.manager()
|
||||
.get_tray(id)
|
||||
}
|
||||
|
||||
/// The path resolver for the application.
|
||||
@@ -672,7 +717,7 @@ impl<R: Runtime> App<R> {
|
||||
/// Sets the activation policy for the application. It is set to `NSApplicationActivationPolicyRegular` by default.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```,no_run
|
||||
/// let mut app = tauri::Builder::default()
|
||||
/// // on an actual app, remove the string argument
|
||||
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
|
||||
@@ -695,7 +740,7 @@ impl<R: Runtime> App<R> {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let matches = app.get_cli_matches()?;
|
||||
@@ -714,7 +759,7 @@ impl<R: Runtime> App<R> {
|
||||
/// Runs the application.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```,no_run
|
||||
/// let app = tauri::Builder::default()
|
||||
/// // on an actual app, remove the string argument
|
||||
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
|
||||
@@ -752,7 +797,7 @@ impl<R: Runtime> App<R> {
|
||||
/// Additionally, the cleanup calls [AppHandle#remove_system_tray](`AppHandle#method.remove_system_tray`) (Windows only).
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```no_run
|
||||
/// let mut app = tauri::Builder::default()
|
||||
/// // on an actual app, remove the string argument
|
||||
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
|
||||
@@ -831,7 +876,7 @@ impl<R: Runtime> App<R> {
|
||||
/// Builds a Tauri application.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```,no_run
|
||||
/// tauri::Builder::default()
|
||||
/// // on an actual app, remove the string argument
|
||||
/// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
|
||||
@@ -941,7 +986,7 @@ impl<R: Runtime> Builder<R> {
|
||||
/// Defines the JS message handler callback.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```
|
||||
/// #[tauri::command]
|
||||
/// fn command_1() -> String {
|
||||
/// return "hello world".to_string();
|
||||
@@ -980,7 +1025,7 @@ impl<R: Runtime> Builder<R> {
|
||||
/// Defines the setup hook.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
@@ -1076,7 +1121,7 @@ impl<R: Runtime> Builder<R> {
|
||||
///
|
||||
/// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```,no_run
|
||||
/// use std::{collections::HashMap, sync::Mutex};
|
||||
/// use tauri::State;
|
||||
/// // here we use Mutex to achieve interior mutability
|
||||
@@ -1111,7 +1156,7 @@ impl<R: Runtime> Builder<R> {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```,no_run
|
||||
/// use tauri::State;
|
||||
///
|
||||
/// struct MyInt(isize);
|
||||
@@ -1149,7 +1194,21 @@ impl<R: Runtime> Builder<R> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds the icon configured on `tauri.conf.json` to the system tray with the specified menu items.
|
||||
/// Sets the given system tray to be built before the app runs.
|
||||
///
|
||||
/// Prefer the [`SystemTray#method.build`] method to create the tray at runtime instead.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .system_tray(SystemTray::new().with_menu(
|
||||
/// SystemTrayMenu::new()
|
||||
/// .add_item(CustomMenuItem::new("quit", "Quit"))
|
||||
/// .add_item(CustomMenuItem::new("open", "Open"))
|
||||
/// ));
|
||||
/// ```
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
|
||||
#[must_use]
|
||||
@@ -1161,7 +1220,7 @@ impl<R: Runtime> Builder<R> {
|
||||
/// Sets the menu to use on all windows.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```
|
||||
/// use tauri::{MenuEntry, Submenu, MenuItem, Menu, CustomMenuItem};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
@@ -1185,7 +1244,7 @@ impl<R: Runtime> Builder<R> {
|
||||
/// Registers a menu event handler for all windows.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```
|
||||
/// use tauri::{Menu, MenuEntry, Submenu, CustomMenuItem, api, Manager};
|
||||
/// tauri::Builder::default()
|
||||
/// .menu(Menu::with_items([
|
||||
@@ -1225,7 +1284,7 @@ impl<R: Runtime> Builder<R> {
|
||||
/// Registers a window event handler for all windows.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// ```
|
||||
/// tauri::Builder::default()
|
||||
/// .on_window_event(|event| match event.event() {
|
||||
/// tauri::WindowEvent::Focused(focused) => {
|
||||
@@ -1248,13 +1307,15 @@ impl<R: Runtime> Builder<R> {
|
||||
|
||||
/// Registers a system tray event handler.
|
||||
///
|
||||
/// Prefer the [`SystemTray#method.on_event`] method when creating a tray at runtime instead.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,no_run
|
||||
/// use tauri::Manager;
|
||||
/// ```
|
||||
/// use tauri::{Manager, SystemTrayEvent};
|
||||
/// tauri::Builder::default()
|
||||
/// .on_system_tray_event(|app, event| match event {
|
||||
/// // show window with id "main" when the tray is left clicked
|
||||
/// tauri::SystemTrayEvent::LeftClick { .. } => {
|
||||
/// SystemTrayEvent::LeftClick { .. } => {
|
||||
/// let window = app.get_window("main").unwrap();
|
||||
/// window.show().unwrap();
|
||||
/// window.set_focus().unwrap();
|
||||
@@ -1313,7 +1374,7 @@ impl<R: Runtime> Builder<R> {
|
||||
///
|
||||
/// - Use a macOS Universal binary target name:
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```
|
||||
/// let mut builder = tauri::Builder::default();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// {
|
||||
@@ -1323,7 +1384,7 @@ impl<R: Runtime> Builder<R> {
|
||||
///
|
||||
/// - Append debug information to the target:
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```
|
||||
/// let kind = if cfg!(debug_assertions) { "debug" } else { "release" };
|
||||
/// tauri::Builder::default()
|
||||
/// .updater_target(format!("{}-{}", tauri::updater::target().unwrap(), kind));
|
||||
@@ -1331,7 +1392,7 @@ impl<R: Runtime> Builder<R> {
|
||||
///
|
||||
/// - Use the platform's target triple:
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```
|
||||
/// tauri::Builder::default()
|
||||
/// .updater_target(tauri::utils::platform::target_triple().unwrap());
|
||||
/// ```
|
||||
@@ -1349,18 +1410,6 @@ impl<R: Runtime> Builder<R> {
|
||||
self.menu = Some(Menu::os_default(&context.package_info().name));
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
let system_tray_icon = context.system_tray_icon.clone();
|
||||
|
||||
#[cfg(all(feature = "system-tray", target_os = "macos"))]
|
||||
let (system_tray_icon_as_template, system_tray_menu_on_left_click) = context
|
||||
.config
|
||||
.tauri
|
||||
.system_tray
|
||||
.as_ref()
|
||||
.map(|t| (t.icon_as_template, t.menu_on_left_click))
|
||||
.unwrap_or_default();
|
||||
|
||||
#[cfg(shell_scope)]
|
||||
let shell_scope = context.shell_scope.clone();
|
||||
|
||||
@@ -1418,8 +1467,6 @@ impl<R: Runtime> Builder<R> {
|
||||
global_shortcut_manager: global_shortcut_manager.clone(),
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_handle: None,
|
||||
handle: AppHandle {
|
||||
runtime_handle,
|
||||
manager,
|
||||
@@ -1427,8 +1474,6 @@ impl<R: Runtime> Builder<R> {
|
||||
global_shortcut_manager,
|
||||
#[cfg(feature = "clipboard")]
|
||||
clipboard_manager,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
tray_handle: None,
|
||||
#[cfg(updater)]
|
||||
updater_settings: self.updater_settings,
|
||||
},
|
||||
@@ -1481,77 +1526,25 @@ impl<R: Runtime> Builder<R> {
|
||||
}
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
if let Some(system_tray) = self.system_tray {
|
||||
let mut ids = HashMap::new();
|
||||
if let Some(menu) = system_tray.menu() {
|
||||
tray::get_menu_ids(&mut ids, menu);
|
||||
{
|
||||
if let Some(tray) = self.system_tray {
|
||||
tray.build(&app)?;
|
||||
}
|
||||
let tray_icon = if let Some(icon) = system_tray.icon {
|
||||
Some(icon)
|
||||
} else if let Some(tray_icon) = system_tray_icon {
|
||||
Some(tray_icon.try_into()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut tray = tray::SystemTray::new()
|
||||
.with_icon(tray_icon.expect("tray icon not found; please configure it on tauri.conf.json"));
|
||||
if let Some(menu) = system_tray.menu {
|
||||
tray = tray.with_menu(menu);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
let tray = tray
|
||||
.with_icon_as_template(system_tray_icon_as_template)
|
||||
.with_menu_on_left_click(system_tray_menu_on_left_click);
|
||||
|
||||
let tray_handler = app
|
||||
.runtime
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.system_tray(tray.into())
|
||||
.expect("failed to run tray");
|
||||
|
||||
let tray_handle = tray::SystemTrayHandle {
|
||||
ids: Arc::new(std::sync::Mutex::new(ids)),
|
||||
inner: tray_handler,
|
||||
};
|
||||
let ids = tray_handle.ids.clone();
|
||||
app.tray_handle.replace(tray_handle.clone());
|
||||
app.handle.tray_handle.replace(tray_handle);
|
||||
for listener in self.system_tray_event_listeners {
|
||||
let app_handle = app.handle();
|
||||
let ids = ids.clone();
|
||||
let listener = Arc::new(std::sync::Mutex::new(listener));
|
||||
app
|
||||
.runtime
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.on_system_tray_event(move |event| {
|
||||
let app_handle = app_handle.clone();
|
||||
let event = match event {
|
||||
RuntimeSystemTrayEvent::MenuItemClick(id) => tray::SystemTrayEvent::MenuItemClick {
|
||||
id: ids.lock().unwrap().get(id).unwrap().clone(),
|
||||
},
|
||||
RuntimeSystemTrayEvent::LeftClick { position, size } => {
|
||||
tray::SystemTrayEvent::LeftClick {
|
||||
position: *position,
|
||||
size: *size,
|
||||
}
|
||||
}
|
||||
RuntimeSystemTrayEvent::RightClick { position, size } => {
|
||||
tray::SystemTrayEvent::RightClick {
|
||||
position: *position,
|
||||
size: *size,
|
||||
}
|
||||
}
|
||||
RuntimeSystemTrayEvent::DoubleClick { position, size } => {
|
||||
tray::SystemTrayEvent::DoubleClick {
|
||||
position: *position,
|
||||
size: *size,
|
||||
}
|
||||
}
|
||||
};
|
||||
let listener = listener.clone();
|
||||
listener.lock().unwrap()(&app_handle, event);
|
||||
.on_system_tray_event(move |tray_id, event| {
|
||||
if let Some((tray_id, tray)) = app_handle.manager().get_tray_by_runtime_id(tray_id) {
|
||||
let app_handle = app_handle.clone();
|
||||
let event = tray::SystemTrayEvent::from_runtime_event(event, tray_id, &tray.ids);
|
||||
let listener = listener.clone();
|
||||
listener.lock().unwrap()(&app_handle, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,26 @@ pub use crate::{
|
||||
MenuHash, MenuId, MenuIdRef, MenuUpdate, SystemTrayMenu, SystemTrayMenuEntry, TrayHandle,
|
||||
},
|
||||
window::dpi::{PhysicalPosition, PhysicalSize},
|
||||
RuntimeHandle, SystemTrayEvent as RuntimeSystemTrayEvent,
|
||||
},
|
||||
Icon, Runtime,
|
||||
};
|
||||
use crate::{sealed::RuntimeOrDispatch, Manager};
|
||||
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use tauri_macros::default_runtime;
|
||||
use tauri_runtime::TrayId;
|
||||
use tauri_utils::debug_eprintln;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{hash_map::DefaultHasher, HashMap},
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
type TrayEventHandler = dyn Fn(SystemTrayEvent) + Send + Sync + 'static;
|
||||
|
||||
pub(crate) fn get_menu_ids(map: &mut HashMap<MenuHash, MenuId>, menu: &SystemTrayMenu) {
|
||||
for item in &menu.items {
|
||||
match item {
|
||||
@@ -33,9 +41,11 @@ pub(crate) fn get_menu_ids(map: &mut HashMap<MenuHash, MenuId>, menu: &SystemTra
|
||||
}
|
||||
|
||||
/// Represents a System Tray instance.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct SystemTray {
|
||||
/// The tray identifier. Defaults to a random string.
|
||||
pub id: String,
|
||||
/// The tray icon.
|
||||
pub icon: Option<tauri_runtime::Icon>,
|
||||
/// The tray menu.
|
||||
@@ -46,10 +56,62 @@ pub struct SystemTray {
|
||||
/// Whether the menu should appear when the tray receives a left click. Defaults to `true`
|
||||
#[cfg(target_os = "macos")]
|
||||
pub menu_on_left_click: bool,
|
||||
on_event: Option<Arc<TrayEventHandler>>,
|
||||
// TODO: icon_as_template and menu_on_left_click should be an Option instead :(
|
||||
#[cfg(target_os = "macos")]
|
||||
menu_on_left_click_set: bool,
|
||||
#[cfg(target_os = "macos")]
|
||||
icon_as_template_set: bool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for SystemTray {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("SystemTray");
|
||||
d.field("id", &self.id)
|
||||
.field("icon", &self.icon)
|
||||
.field("menu", &self.menu);
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
d.field("icon_as_template", &self.icon_as_template)
|
||||
.field("menu_on_left_click", &self.menu_on_left_click);
|
||||
}
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SystemTray {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: Alphanumeric.sample_string(&mut rand::thread_rng(), 16),
|
||||
icon: None,
|
||||
menu: None,
|
||||
on_event: None,
|
||||
#[cfg(target_os = "macos")]
|
||||
icon_as_template: false,
|
||||
#[cfg(target_os = "macos")]
|
||||
menu_on_left_click: false,
|
||||
#[cfg(target_os = "macos")]
|
||||
icon_as_template_set: false,
|
||||
#[cfg(target_os = "macos")]
|
||||
menu_on_left_click_set: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SystemTray {
|
||||
/// Creates a new system tray that only renders an icon.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::SystemTray;
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let tray_handle = SystemTray::new().build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
@@ -58,7 +120,43 @@ impl SystemTray {
|
||||
self.menu.as_ref()
|
||||
}
|
||||
|
||||
/// Sets the tray icon.
|
||||
/// Sets the tray identifier, used to retrieve its handle and to identify a tray event source.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::SystemTray;
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let tray_handle = SystemTray::new()
|
||||
/// .with_id("tray-id")
|
||||
/// .build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn with_id<I: Into<String>>(mut self, id: I) -> Self {
|
||||
self.id = id.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the tray [`Icon`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::{Icon, SystemTray};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let tray_handle = SystemTray::new()
|
||||
/// // dummy and invalid Rgba icon; see the Icon documentation for more information
|
||||
/// .with_icon(Icon::Rgba { rgba: Vec::new(), width: 0, height: 0 })
|
||||
/// .build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn with_icon<I: TryInto<tauri_runtime::Icon>>(mut self, icon: I) -> Self
|
||||
where
|
||||
@@ -79,50 +177,230 @@ impl SystemTray {
|
||||
///
|
||||
/// Images you mark as template images should consist of only black and clear colors.
|
||||
/// You can use the alpha channel in the image to adjust the opacity of black content.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::SystemTray;
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let mut tray_builder = SystemTray::new();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// {
|
||||
/// tray_builder = tray_builder.with_icon_as_template(true);
|
||||
/// }
|
||||
/// let tray_handle = tray_builder.build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[cfg(target_os = "macos")]
|
||||
#[must_use]
|
||||
pub fn with_icon_as_template(mut self, is_template: bool) -> Self {
|
||||
self.icon_as_template_set = true;
|
||||
self.icon_as_template = is_template;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the menu should appear when the tray receives a left click. Defaults to `true`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::SystemTray;
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let mut tray_builder = SystemTray::new();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// {
|
||||
/// tray_builder = tray_builder.with_menu_on_left_click(false);
|
||||
/// }
|
||||
/// let tray_handle = tray_builder.build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[cfg(target_os = "macos")]
|
||||
#[must_use]
|
||||
pub fn with_menu_on_left_click(mut self, menu_on_left_click: bool) -> Self {
|
||||
self.menu_on_left_click_set = true;
|
||||
self.menu_on_left_click = menu_on_left_click;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the event listener for this system tray.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::{Icon, Manager, SystemTray, SystemTrayEvent};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let handle = app.handle();
|
||||
/// let id = "tray-id";
|
||||
/// SystemTray::new()
|
||||
/// .with_id(id)
|
||||
/// .on_event(move |event| {
|
||||
/// let tray_handle = handle.tray_handle_by_id(id).unwrap();
|
||||
/// match event {
|
||||
/// // show window with id "main" when the tray is left clicked
|
||||
/// SystemTrayEvent::LeftClick { .. } => {
|
||||
/// let window = handle.get_window("main").unwrap();
|
||||
/// window.show().unwrap();
|
||||
/// window.set_focus().unwrap();
|
||||
/// }
|
||||
/// _ => {}
|
||||
/// }
|
||||
/// })
|
||||
/// .build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn on_event<F: Fn(SystemTrayEvent) + Send + Sync + 'static>(mut self, f: F) -> Self {
|
||||
self.on_event.replace(Arc::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the menu to show when the system tray is right clicked.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let tray_handle = SystemTray::new()
|
||||
/// .with_menu(
|
||||
/// SystemTrayMenu::new()
|
||||
/// .add_item(CustomMenuItem::new("quit", "Quit"))
|
||||
/// .add_item(CustomMenuItem::new("open", "Open"))
|
||||
/// )
|
||||
/// .build(app)?;
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn with_menu(mut self, menu: SystemTrayMenu) -> Self {
|
||||
self.menu.replace(menu);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SystemTray> for tauri_runtime::SystemTray {
|
||||
fn from(tray: SystemTray) -> Self {
|
||||
let mut t = tauri_runtime::SystemTray::new();
|
||||
if let Some(i) = tray.icon {
|
||||
t = t.with_icon(i);
|
||||
/// Builds and shows the system tray.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
/// .setup(|app| {
|
||||
/// let tray_handle = SystemTray::new()
|
||||
/// .with_menu(
|
||||
/// SystemTrayMenu::new()
|
||||
/// .add_item(CustomMenuItem::new("quit", "Quit"))
|
||||
/// .add_item(CustomMenuItem::new("open", "Open"))
|
||||
/// )
|
||||
/// .build(app)?;
|
||||
///
|
||||
/// tray_handle.get_item("quit").set_enabled(false);
|
||||
/// Ok(())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn build<R: Runtime, M: Manager<R>>(
|
||||
mut self,
|
||||
manager: &M,
|
||||
) -> crate::Result<SystemTrayHandle<R>> {
|
||||
let mut ids = HashMap::new();
|
||||
if let Some(menu) = self.menu() {
|
||||
get_menu_ids(&mut ids, menu);
|
||||
}
|
||||
let ids = Arc::new(Mutex::new(ids));
|
||||
|
||||
if self.icon.is_none() {
|
||||
if let Some(tray_icon) = &manager.manager().inner.tray_icon {
|
||||
self = self.with_icon(tray_icon.clone());
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if !self.icon_as_template_set {
|
||||
self.icon_as_template = manager
|
||||
.config()
|
||||
.tauri
|
||||
.system_tray
|
||||
.as_ref()
|
||||
.map_or(false, |t| t.icon_as_template);
|
||||
}
|
||||
if !self.menu_on_left_click_set {
|
||||
self.menu_on_left_click = manager
|
||||
.config()
|
||||
.tauri
|
||||
.system_tray
|
||||
.as_ref()
|
||||
.map_or(false, |t| t.menu_on_left_click);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(menu) = tray.menu {
|
||||
t = t.with_menu(menu);
|
||||
let tray_id = self.id.clone();
|
||||
|
||||
let mut runtime_tray = tauri_runtime::SystemTray::new();
|
||||
runtime_tray = runtime_tray.with_id(hash(&self.id));
|
||||
if let Some(i) = self.icon {
|
||||
runtime_tray = runtime_tray.with_icon(i);
|
||||
}
|
||||
|
||||
if let Some(menu) = self.menu {
|
||||
runtime_tray = runtime_tray.with_menu(menu);
|
||||
}
|
||||
|
||||
if let Some(on_event) = self.on_event {
|
||||
let ids_ = ids.clone();
|
||||
let tray_id_ = tray_id.clone();
|
||||
runtime_tray = runtime_tray.on_event(move |event| {
|
||||
on_event(SystemTrayEvent::from_runtime_event(
|
||||
event,
|
||||
tray_id_.clone(),
|
||||
&ids_,
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
t = t.with_icon_as_template(tray.icon_as_template);
|
||||
t = t.with_menu_on_left_click(tray.menu_on_left_click);
|
||||
runtime_tray = runtime_tray.with_icon_as_template(self.icon_as_template);
|
||||
runtime_tray = runtime_tray.with_menu_on_left_click(self.menu_on_left_click);
|
||||
}
|
||||
|
||||
t
|
||||
let id = runtime_tray.id;
|
||||
let tray_handler = match manager.runtime() {
|
||||
RuntimeOrDispatch::Runtime(r) => r.system_tray(runtime_tray),
|
||||
RuntimeOrDispatch::RuntimeHandle(h) => h.system_tray(runtime_tray),
|
||||
RuntimeOrDispatch::Dispatch(_) => manager
|
||||
.app_handle()
|
||||
.runtime_handle
|
||||
.system_tray(runtime_tray),
|
||||
}?;
|
||||
|
||||
let tray_handle = SystemTrayHandle {
|
||||
id,
|
||||
ids,
|
||||
inner: tray_handler,
|
||||
};
|
||||
manager.manager().attach_tray(tray_id, tray_handle.clone());
|
||||
|
||||
Ok(tray_handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(id: &str) -> MenuHash {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
id.hash(&mut hasher);
|
||||
hasher.finish() as MenuHash
|
||||
}
|
||||
|
||||
/// System tray event.
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
|
||||
#[non_exhaustive]
|
||||
@@ -130,6 +408,8 @@ pub enum SystemTrayEvent {
|
||||
/// Tray context menu item was clicked.
|
||||
#[non_exhaustive]
|
||||
MenuItemClick {
|
||||
/// The tray id.
|
||||
tray_id: String,
|
||||
/// The id of the menu item.
|
||||
id: MenuId,
|
||||
},
|
||||
@@ -140,6 +420,8 @@ pub enum SystemTrayEvent {
|
||||
/// - **Linux:** Unsupported
|
||||
#[non_exhaustive]
|
||||
LeftClick {
|
||||
/// The tray id.
|
||||
tray_id: String,
|
||||
/// The position of the tray icon.
|
||||
position: PhysicalPosition<f64>,
|
||||
/// The size of the tray icon.
|
||||
@@ -153,6 +435,8 @@ pub enum SystemTrayEvent {
|
||||
/// - **macOS:** `Ctrl` + `Left click` fire this event.
|
||||
#[non_exhaustive]
|
||||
RightClick {
|
||||
/// The tray id.
|
||||
tray_id: String,
|
||||
/// The position of the tray icon.
|
||||
position: PhysicalPosition<f64>,
|
||||
/// The size of the tray icon.
|
||||
@@ -166,6 +450,8 @@ pub enum SystemTrayEvent {
|
||||
///
|
||||
#[non_exhaustive]
|
||||
DoubleClick {
|
||||
/// The tray id.
|
||||
tray_id: String,
|
||||
/// The position of the tray icon.
|
||||
position: PhysicalPosition<f64>,
|
||||
/// The size of the tray icon.
|
||||
@@ -173,10 +459,41 @@ pub enum SystemTrayEvent {
|
||||
},
|
||||
}
|
||||
|
||||
impl SystemTrayEvent {
|
||||
pub(crate) fn from_runtime_event(
|
||||
event: &RuntimeSystemTrayEvent,
|
||||
tray_id: String,
|
||||
menu_ids: &Arc<Mutex<HashMap<u16, String>>>,
|
||||
) -> Self {
|
||||
match event {
|
||||
RuntimeSystemTrayEvent::MenuItemClick(id) => Self::MenuItemClick {
|
||||
tray_id,
|
||||
id: menu_ids.lock().unwrap().get(id).unwrap().clone(),
|
||||
},
|
||||
RuntimeSystemTrayEvent::LeftClick { position, size } => Self::LeftClick {
|
||||
tray_id,
|
||||
position: *position,
|
||||
size: *size,
|
||||
},
|
||||
RuntimeSystemTrayEvent::RightClick { position, size } => Self::RightClick {
|
||||
tray_id,
|
||||
position: *position,
|
||||
size: *size,
|
||||
},
|
||||
RuntimeSystemTrayEvent::DoubleClick { position, size } => Self::DoubleClick {
|
||||
tray_id,
|
||||
position: *position,
|
||||
size: *size,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to a system tray. Allows updating the context menu items.
|
||||
#[default_runtime(crate::Wry, wry)]
|
||||
#[derive(Debug)]
|
||||
pub struct SystemTrayHandle<R: Runtime> {
|
||||
pub(crate) id: TrayId,
|
||||
pub(crate) ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
|
||||
pub(crate) inner: R::TrayHandler,
|
||||
}
|
||||
@@ -184,6 +501,7 @@ pub struct SystemTrayHandle<R: Runtime> {
|
||||
impl<R: Runtime> Clone for SystemTrayHandle<R> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
id: self.id,
|
||||
ids: self.ids.clone(),
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
@@ -245,6 +563,11 @@ impl<R: Runtime> SystemTrayHandle<R> {
|
||||
.set_icon_as_template(is_template)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Destroys this system tray.
|
||||
pub fn destroy(&self) -> crate::Result<()> {
|
||||
self.inner.destroy().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> SystemTrayMenuItemHandle<R> {
|
||||
|
||||
@@ -196,6 +196,8 @@ fn replace_csp_nonce(
|
||||
#[default_runtime(crate::Wry, wry)]
|
||||
pub struct InnerWindowManager<R: Runtime> {
|
||||
windows: Mutex<HashMap<String, Window<R>>>,
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
pub(crate) trays: Mutex<HashMap<String, crate::SystemTrayHandle<R>>>,
|
||||
pub(crate) plugins: Mutex<PluginStore<R>>,
|
||||
listeners: Listeners,
|
||||
pub(crate) state: Arc<StateManager>,
|
||||
@@ -210,6 +212,7 @@ pub struct InnerWindowManager<R: Runtime> {
|
||||
assets: Arc<dyn Assets>,
|
||||
pub(crate) default_window_icon: Option<Icon>,
|
||||
pub(crate) app_icon: Option<Vec<u8>>,
|
||||
pub(crate) tray_icon: Option<Icon>,
|
||||
|
||||
package_info: PackageInfo,
|
||||
/// The webview protocols protocols available to all windows.
|
||||
@@ -236,6 +239,7 @@ impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
|
||||
.field("config", &self.config)
|
||||
.field("default_window_icon", &self.default_window_icon)
|
||||
.field("app_icon", &self.app_icon)
|
||||
.field("tray_icon", &self.tray_icon)
|
||||
.field("package_info", &self.package_info)
|
||||
.field("menu", &self.menu)
|
||||
.field("pattern", &self.pattern)
|
||||
@@ -300,6 +304,8 @@ impl<R: Runtime> WindowManager<R> {
|
||||
Self {
|
||||
inner: Arc::new(InnerWindowManager {
|
||||
windows: Mutex::default(),
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
trays: Default::default(),
|
||||
plugins: Mutex::new(plugins),
|
||||
listeners: Listeners::default(),
|
||||
state: Arc::new(state),
|
||||
@@ -309,6 +315,7 @@ impl<R: Runtime> WindowManager<R> {
|
||||
assets: context.assets,
|
||||
default_window_icon: context.default_window_icon,
|
||||
app_icon: context.app_icon,
|
||||
tray_icon: context.system_tray_icon,
|
||||
package_info: context.package_info,
|
||||
pattern: context.pattern,
|
||||
uri_scheme_protocols,
|
||||
@@ -1289,6 +1296,33 @@ impl<R: Runtime> WindowManager<R> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tray APIs
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
impl<R: Runtime> WindowManager<R> {
|
||||
pub fn get_tray(&self, id: &str) -> Option<crate::SystemTrayHandle<R>> {
|
||||
self.inner.trays.lock().unwrap().get(id).cloned()
|
||||
}
|
||||
|
||||
pub fn trays(&self) -> HashMap<String, crate::SystemTrayHandle<R>> {
|
||||
self.inner.trays.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn attach_tray(&self, id: String, tray: crate::SystemTrayHandle<R>) {
|
||||
self.inner.trays.lock().unwrap().insert(id, tray);
|
||||
}
|
||||
|
||||
pub fn get_tray_by_runtime_id(&self, id: u16) -> Option<(String, crate::SystemTrayHandle<R>)> {
|
||||
let trays = self.inner.trays.lock().unwrap();
|
||||
let iter = trays.iter();
|
||||
for (tray_id, tray) in iter {
|
||||
if tray.id == id {
|
||||
return Some((tray_id.clone(), tray.clone()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn on_window_event<R: Runtime>(
|
||||
window: &Window<R>,
|
||||
manager: &WindowManager<R>,
|
||||
|
||||
@@ -18,7 +18,7 @@ use tauri_runtime::{
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
use tauri_runtime::{
|
||||
menu::{SystemTrayMenu, TrayHandle},
|
||||
SystemTray, SystemTrayEvent,
|
||||
SystemTray, SystemTrayEvent, TrayId,
|
||||
};
|
||||
use tauri_utils::{config::WindowConfig, Theme};
|
||||
use uuid::Uuid;
|
||||
@@ -80,10 +80,13 @@ impl<T: UserEvent> RuntimeHandle<T> for MockRuntimeHandle {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(all(windows, feature = "system-tray"))))]
|
||||
fn remove_system_tray(&self) -> Result<()> {
|
||||
Ok(())
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "system-tray"))))]
|
||||
fn system_tray(
|
||||
&self,
|
||||
system_tray: SystemTray,
|
||||
) -> Result<<Self::Runtime as Runtime<T>>::TrayHandler> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
|
||||
@@ -531,6 +534,10 @@ impl TrayHandle for MockTrayHandler {
|
||||
fn set_icon_as_template(&self, is_template: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -636,9 +643,7 @@ impl<T: UserEvent> Runtime<T> for MockRuntime {
|
||||
|
||||
#[cfg(all(desktop, feature = "system-tray"))]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
|
||||
fn on_system_tray_event<F: Fn(&SystemTrayEvent) + Send + 'static>(&mut self, f: F) -> Uuid {
|
||||
Uuid::new_v4()
|
||||
}
|
||||
fn on_system_tray_event<F: Fn(TrayId, &SystemTrayEvent) + Send + 'static>(&mut self, f: F) {}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
|
||||
|
||||
5
examples/api/src-tauri/Cargo.lock
generated
5
examples/api/src-tauri/Cargo.lock
generated
@@ -3037,9 +3037,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.13.1"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6901eece433f5ce79a60c2660e204d8b2ff4f76668db9aedb8ae6b2c5a57ae43"
|
||||
checksum = "8ad691ca9fca6c2c76c09ffcddf6ae6593fba65d95477cf31780910ed272f5b8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cairo-rs",
|
||||
@@ -3217,6 +3217,7 @@ dependencies = [
|
||||
"http",
|
||||
"http-range",
|
||||
"infer 0.7.0",
|
||||
"rand 0.8.5",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -31,7 +31,10 @@ struct Reply {
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
let mut builder = tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
.setup(move |app| {
|
||||
#[cfg(desktop)]
|
||||
create_tray(app)?;
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut window_builder = WindowBuilder::new(app, "main", WindowUrl::default())
|
||||
.title("Tauri API Validation")
|
||||
@@ -100,99 +103,6 @@ fn main() {
|
||||
builder = builder.menu(tauri::Menu::os_default("Tauri API Validation"));
|
||||
}
|
||||
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
let tray_menu1 = SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new("toggle", "Toggle"))
|
||||
.add_item(CustomMenuItem::new("new", "New window"))
|
||||
.add_item(CustomMenuItem::new("icon_1", "Tray Icon 1"))
|
||||
.add_item(CustomMenuItem::new("icon_2", "Tray Icon 2"))
|
||||
.add_item(CustomMenuItem::new("switch_menu", "Switch Menu"))
|
||||
.add_item(CustomMenuItem::new("exit_app", "Quit"));
|
||||
let tray_menu2 = SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new("toggle", "Toggle"))
|
||||
.add_item(CustomMenuItem::new("new", "New window"))
|
||||
.add_item(CustomMenuItem::new("switch_menu", "Switch Menu"))
|
||||
.add_item(CustomMenuItem::new("exit_app", "Quit"));
|
||||
let is_menu1 = AtomicBool::new(true);
|
||||
|
||||
builder = builder
|
||||
.system_tray(SystemTray::new().with_menu(tray_menu1.clone()))
|
||||
.on_system_tray_event(move |app, event| match event {
|
||||
SystemTrayEvent::LeftClick {
|
||||
position: _,
|
||||
size: _,
|
||||
..
|
||||
} => {
|
||||
let window = app.get_window("main").unwrap();
|
||||
window.show().unwrap();
|
||||
window.set_focus().unwrap();
|
||||
}
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => {
|
||||
let item_handle = app.tray_handle().get_item(&id);
|
||||
match id.as_str() {
|
||||
"exit_app" => {
|
||||
// exit the app
|
||||
app.exit(0);
|
||||
}
|
||||
"toggle" => {
|
||||
let window = app.get_window("main").unwrap();
|
||||
let new_title = if window.is_visible().unwrap() {
|
||||
window.hide().unwrap();
|
||||
"Show"
|
||||
} else {
|
||||
window.show().unwrap();
|
||||
"Hide"
|
||||
};
|
||||
item_handle.set_title(new_title).unwrap();
|
||||
}
|
||||
"new" => {
|
||||
WindowBuilder::new(app, "new", WindowUrl::App("index.html".into()))
|
||||
.title("Tauri")
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
"icon_1" => {
|
||||
#[cfg(target_os = "macos")]
|
||||
app.tray_handle().set_icon_as_template(true).unwrap();
|
||||
|
||||
app
|
||||
.tray_handle()
|
||||
.set_icon(tauri::Icon::Raw(
|
||||
include_bytes!("../../../.icons/tray_icon_with_transparency.png").to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
"icon_2" => {
|
||||
#[cfg(target_os = "macos")]
|
||||
app.tray_handle().set_icon_as_template(true).unwrap();
|
||||
|
||||
app
|
||||
.tray_handle()
|
||||
.set_icon(tauri::Icon::Raw(
|
||||
include_bytes!("../../../.icons/icon.ico").to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
"switch_menu" => {
|
||||
let flag = is_menu1.load(Ordering::Relaxed);
|
||||
app
|
||||
.tray_handle()
|
||||
.set_menu(if flag {
|
||||
tray_menu2.clone()
|
||||
} else {
|
||||
tray_menu1.clone()
|
||||
})
|
||||
.unwrap();
|
||||
is_menu1.store(!flag, Ordering::Relaxed);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut app = builder
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
@@ -259,3 +169,106 @@ fn main() {
|
||||
_ => {}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(desktop)]
|
||||
fn create_tray(app: &tauri::App) -> tauri::Result<()> {
|
||||
let tray_menu1 = SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new("toggle", "Toggle"))
|
||||
.add_item(CustomMenuItem::new("new", "New window"))
|
||||
.add_item(CustomMenuItem::new("icon_1", "Tray Icon 1"))
|
||||
.add_item(CustomMenuItem::new("icon_2", "Tray Icon 2"))
|
||||
.add_item(CustomMenuItem::new("switch_menu", "Switch Menu"))
|
||||
.add_item(CustomMenuItem::new("exit_app", "Quit"))
|
||||
.add_item(CustomMenuItem::new("destroy", "Destroy"));
|
||||
let tray_menu2 = SystemTrayMenu::new()
|
||||
.add_item(CustomMenuItem::new("toggle", "Toggle"))
|
||||
.add_item(CustomMenuItem::new("new", "New window"))
|
||||
.add_item(CustomMenuItem::new("switch_menu", "Switch Menu"))
|
||||
.add_item(CustomMenuItem::new("exit_app", "Quit"))
|
||||
.add_item(CustomMenuItem::new("destroy", "Destroy"));
|
||||
let is_menu1 = AtomicBool::new(true);
|
||||
|
||||
let handle = app.handle();
|
||||
let tray_id = "my-tray".to_string();
|
||||
SystemTray::new()
|
||||
.with_id(&tray_id)
|
||||
.with_menu(tray_menu1.clone())
|
||||
.on_event(move |event| {
|
||||
let tray_handle = handle.tray_handle_by_id(&tray_id).unwrap();
|
||||
match event {
|
||||
SystemTrayEvent::LeftClick {
|
||||
position: _,
|
||||
size: _,
|
||||
..
|
||||
} => {
|
||||
let window = handle.get_window("main").unwrap();
|
||||
window.show().unwrap();
|
||||
window.set_focus().unwrap();
|
||||
}
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => {
|
||||
let item_handle = tray_handle.get_item(&id);
|
||||
match id.as_str() {
|
||||
"exit_app" => {
|
||||
// exit the app
|
||||
handle.exit(0);
|
||||
}
|
||||
"destroy" => {
|
||||
tray_handle.destroy().unwrap();
|
||||
}
|
||||
"toggle" => {
|
||||
let window = handle.get_window("main").unwrap();
|
||||
let new_title = if window.is_visible().unwrap() {
|
||||
window.hide().unwrap();
|
||||
"Show"
|
||||
} else {
|
||||
window.show().unwrap();
|
||||
"Hide"
|
||||
};
|
||||
item_handle.set_title(new_title).unwrap();
|
||||
}
|
||||
"new" => {
|
||||
WindowBuilder::new(&handle, "new", WindowUrl::App("index.html".into()))
|
||||
.title("Tauri")
|
||||
.build()
|
||||
.unwrap();
|
||||
}
|
||||
"icon_1" => {
|
||||
#[cfg(target_os = "macos")]
|
||||
tray_handle.set_icon_as_template(true).unwrap();
|
||||
|
||||
tray_handle
|
||||
.set_icon(tauri::Icon::Raw(
|
||||
include_bytes!("../../../.icons/tray_icon_with_transparency.png").to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
"icon_2" => {
|
||||
#[cfg(target_os = "macos")]
|
||||
tray_handle.set_icon_as_template(true).unwrap();
|
||||
|
||||
tray_handle
|
||||
.set_icon(tauri::Icon::Raw(
|
||||
include_bytes!("../../../.icons/icon.ico").to_vec(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
"switch_menu" => {
|
||||
let flag = is_menu1.load(Ordering::Relaxed);
|
||||
tray_handle
|
||||
.set_menu(if flag {
|
||||
tray_menu2.clone()
|
||||
} else {
|
||||
tray_menu1.clone()
|
||||
})
|
||||
.unwrap();
|
||||
is_menu1.store(!flag, Ordering::Relaxed);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.build(app)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user