diff --git a/crates/tauri-cli/src/interface/rust.rs b/crates/tauri-cli/src/interface/rust.rs index 1638693af134..5706fe4247e4 100644 --- a/crates/tauri-cli/src/interface/rust.rs +++ b/crates/tauri-cli/src/interface/rust.rs @@ -1036,7 +1036,7 @@ impl RustAppSettings { .product_name .clone() .unwrap_or_else(|| cargo_package_settings.name.clone()), - version: version.clone(), + version, description: cargo_package_settings .description .clone() diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index d61a22cc5941..9f3e97ab61e6 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -741,7 +741,7 @@ macro_rules! shared_app_impl { I: ?Sized, TrayIconId: PartialEq<&'a I>, { - self.manager.tray.tray_by_id(id) + self.manager.tray.tray_by_id(self.app_handle(), id) } /// Removes a tray icon using the provided id from tauri's internal state and returns it. @@ -755,7 +755,7 @@ macro_rules! shared_app_impl { I: ?Sized, TrayIconId: PartialEq<&'a I>, { - self.manager.tray.remove_tray_by_id(id) + self.manager.tray.remove_tray_by_id(self.app_handle(), id) } /// Gets the app's configuration, defined on the `tauri.conf.json` file. diff --git a/crates/tauri/src/manager/tray.rs b/crates/tauri/src/manager/tray.rs index 4522438cd235..a3da8715a8a0 100644 --- a/crates/tauri/src/manager/tray.rs +++ b/crates/tauri/src/manager/tray.rs @@ -2,19 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use std::{collections::HashMap, fmt, sync::Mutex}; +use std::{ + collections::HashMap, + fmt, + sync::{Arc, Mutex}, +}; use crate::{ app::GlobalTrayIconEventListener, image::Image, tray::{TrayIcon, TrayIconEvent, TrayIconId}, - AppHandle, Runtime, + AppHandle, Manager, Resource, ResourceId, Runtime, }; pub struct TrayManager { pub(crate) icon: Option>, /// Tray icons - pub(crate) icons: Mutex>>, + pub(crate) icons: Mutex>, /// Global Tray icon event listeners. pub(crate) global_event_listeners: Mutex>>>, /// Tray icon event listeners. @@ -41,30 +45,46 @@ impl TrayManager { .push(Box::new(handler)); } - pub fn tray_by_id<'a, I>(&self, id: &'a I) -> Option> + pub fn tray_by_id<'a, I>(&self, app: &AppHandle, id: &'a I) -> Option> where I: ?Sized, TrayIconId: PartialEq<&'a I>, { - self - .icons - .lock() - .unwrap() - .iter() - .find(|t| t.id() == &id) - .cloned() + let icons = self.icons.lock().unwrap(); + icons.iter().find_map(|(tray_icon_id, rid)| { + if tray_icon_id == &id { + let icon = app.resources_table().get::>(*rid).ok()?; + Some(Arc::unwrap_or_clone(icon)) + } else { + None + } + }) + } + + pub fn tray_resource_by_id<'a, I>(&self, id: &'a I) -> Option + where + I: ?Sized, + TrayIconId: PartialEq<&'a I>, + { + let icons = self.icons.lock().unwrap(); + icons.iter().find_map(|(tray_icon_id, rid)| { + if tray_icon_id == &id { + Some(*rid) + } else { + None + } + }) } - pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option> + pub fn remove_tray_by_id<'a, I>(&self, app: &AppHandle, id: &'a I) -> Option> where I: ?Sized, TrayIconId: PartialEq<&'a I>, { - let mut icons = self.icons.lock().unwrap(); - let idx = icons.iter().position(|t| t.id() == &id); - if let Some(idx) = idx { - return Some(icons.swap_remove(idx)); - } - None + let rid = self.tray_resource_by_id(id)?; + let icon = app.resources_table().take::>(rid).ok()?; + let icon_to_return = icon.clone(); + icon.close(); + Some(Arc::unwrap_or_clone(icon_to_return)) } } diff --git a/crates/tauri/src/tray/mod.rs b/crates/tauri/src/tray/mod.rs index 472f42bfc2f4..59997f22228d 100644 --- a/crates/tauri/src/tray/mod.rs +++ b/crates/tauri/src/tray/mod.rs @@ -10,10 +10,10 @@ use crate::app::{GlobalMenuEventListener, GlobalTrayIconEventListener}; use crate::menu::ContextMenu; use crate::menu::MenuEvent; use crate::resources::Resource; -use crate::UnsafeSend; use crate::{ image::Image, menu::run_item_main_thread, AppHandle, Manager, PhysicalPosition, Rect, Runtime, }; +use crate::{ResourceId, UnsafeSend}; use serde::Serialize; use std::path::Path; pub use tray_icon::TrayIconId; @@ -358,8 +358,10 @@ impl TrayIconBuilder { self.inner.id() } - /// Builds and adds a new [`TrayIcon`] to the system tray. - pub fn build>(self, manager: &M) -> crate::Result> { + pub(crate) fn build_inner( + self, + app_handle: &AppHandle, + ) -> crate::Result<(TrayIcon, ResourceId)> { let id = self.id().clone(); // SAFETY: @@ -368,8 +370,7 @@ impl TrayIconBuilder { let unsafe_builder = UnsafeSend(self.inner); let (tx, rx) = std::sync::mpsc::channel(); - let unsafe_tray = manager - .app_handle() + let unsafe_tray = app_handle .run_on_main_thread(move || { // SAFETY: will only be accessed on main thread let _ = tx.send(unsafe_builder.take().build().map(UnsafeSend)); @@ -379,15 +380,21 @@ impl TrayIconBuilder { let icon = TrayIcon { id, inner: unsafe_tray.take(), - app_handle: manager.app_handle().clone(), + app_handle: app_handle.clone(), }; - icon.register( + let rid = icon.register( &icon.app_handle, self.on_menu_event, self.on_tray_icon_event, ); + Ok((icon, rid)) + } + + /// Builds and adds a new [`TrayIcon`] to the system tray. + pub fn build>(self, manager: &M) -> crate::Result> { + let (icon, _rid) = self.build_inner(manager.app_handle())?; Ok(icon) } } @@ -426,7 +433,7 @@ impl TrayIcon { app_handle: &AppHandle, on_menu_event: Option>>, on_tray_icon_event: Option>>, - ) { + ) -> ResourceId { if let Some(handler) = on_menu_event { app_handle .manager @@ -447,13 +454,15 @@ impl TrayIcon { .insert(self.id.clone(), handler); } + let rid = app_handle.resources_table().add(self.clone()); app_handle .manager .tray .icons .lock() .unwrap() - .push(self.clone()); + .push((self.id().clone(), rid)); + rid } /// The application handle associated with this type. @@ -598,14 +607,13 @@ impl TrayIcon { impl Resource for TrayIcon { fn close(self: std::sync::Arc) { - self - .app_handle - .state::() - .icons - .lock() - .unwrap() - .remove(&self.id.0); - self.app_handle.remove_tray_by_id(&self.id); + let mut icons = self.app_handle.manager.tray.icons.lock().unwrap(); + for (i, (tray_icon_id, _rid)) in icons.iter_mut().enumerate() { + if tray_icon_id == &self.id { + icons.swap_remove(i); + return; + } + } } } diff --git a/crates/tauri/src/tray/plugin.rs b/crates/tauri/src/tray/plugin.rs index 1e1a057f380c..32d33c9f0686 100644 --- a/crates/tauri/src/tray/plugin.rs +++ b/crates/tauri/src/tray/plugin.rs @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use std::{collections::HashMap, path::PathBuf, sync::Mutex}; +use std::path::PathBuf; +use anyhow::Context; use serde::Deserialize; use crate::{ @@ -14,15 +15,11 @@ use crate::{ plugin::{Builder, TauriPlugin}, resources::ResourceId, tray::TrayIconBuilder, - AppHandle, Manager, Runtime, State, Webview, + AppHandle, Manager, Runtime, Webview, }; use super::{TrayIcon, TrayIconEvent}; -pub(crate) struct TrayIcons { - pub(crate) icons: Mutex>, -} - #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct TrayIconOptions { @@ -40,7 +37,6 @@ struct TrayIconOptions { #[command(root = "crate")] fn new( webview: Webview, - icons: State<'_, TrayIcons>, options: TrayIconOptions, handler: Channel, ) -> crate::Result<(ResourceId, String)> { @@ -54,7 +50,7 @@ fn new( let _ = handler.send(e); }); - let mut resources_table = webview.resources_table(); + let resources_table = webview.resources_table(); if let Some((rid, kind)) = options.menu { match kind { @@ -92,58 +88,32 @@ fn new( builder = builder.show_menu_on_left_click(show_menu_on_left_click); } - let tray = builder.build(&webview)?; + let (tray, rid) = builder.build_inner(webview.app_handle())?; let id = tray.id().as_ref().to_string(); - let rid = resources_table.add(tray); - - icons.icons.lock().unwrap().insert(id.clone(), rid); Ok((rid, id)) } #[command(root = "crate")] -fn get_by_id( - app: AppHandle, - webview: Webview, - icons: State<'_, TrayIcons>, - id: &str, -) -> crate::Result> { - // if the icon was created by this plugin, return the resource id - // this lets a getById call match the rid of a TrayIcon.new call - // which allows it to close a previously created icon - if let Some(rid) = icons.icons.lock().unwrap().get(id) { - return Ok(Some(*rid)); - } - - let tray = app.tray_by_id(id); - let maybe_rid = tray.map(|tray| { - let mut resources_table = webview.resources_table(); - resources_table.add(tray) - }); - - if let Some(rid) = maybe_rid { - icons.icons.lock().unwrap().insert(id.to_string(), rid); - } - - Ok(maybe_rid) +fn get_by_id(app: AppHandle, id: &str) -> Option { + app.manager.tray.tray_resource_by_id(id) } #[command(root = "crate")] fn remove_by_id(app: AppHandle, id: &str) -> crate::Result<()> { app .remove_tray_by_id(id) - .ok_or_else(|| anyhow::anyhow!("Can't find a tray associated with this id: {id}")) - .map(|_| ()) - .map_err(Into::into) + .with_context(|| format!("Can't find a tray associated with this id: {id}"))?; + Ok(()) } #[command(root = "crate")] fn set_icon( - webview: Webview, + app: AppHandle, rid: ResourceId, icon: Option, ) -> crate::Result<()> { - let resources_table = webview.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; let icon = match icon { Some(i) => Some(i.into_img(&resources_table)?.as_ref().clone()), @@ -154,11 +124,11 @@ fn set_icon( #[command(root = "crate")] fn set_menu( - webview: Webview, + app: AppHandle, rid: ResourceId, menu: Option<(ResourceId, ItemKind)>, ) -> crate::Result<()> { - let resources_table = webview.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; if let Some((rid, kind)) = menu { match kind { @@ -180,78 +150,68 @@ fn set_menu( #[command(root = "crate")] fn set_tooltip( - webview: Webview, + app: AppHandle, rid: ResourceId, tooltip: Option, ) -> crate::Result<()> { - let resources_table = webview.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_tooltip(tooltip) } #[command(root = "crate")] fn set_title( - webview: Webview, + app: AppHandle, rid: ResourceId, title: Option, ) -> crate::Result<()> { - let resources_table = webview.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_title(title) } #[command(root = "crate")] -fn set_visible( - webview: Webview, - rid: ResourceId, - visible: bool, -) -> crate::Result<()> { - let resources_table = webview.resources_table(); +fn set_visible(app: AppHandle, rid: ResourceId, visible: bool) -> crate::Result<()> { + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_visible(visible) } #[command(root = "crate")] fn set_temp_dir_path( - webview: Webview, + app: AppHandle, rid: ResourceId, path: Option, ) -> crate::Result<()> { - let resources_table = webview.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_temp_dir_path(path) } #[command(root = "crate")] fn set_icon_as_template( - webview: Webview, + app: AppHandle, rid: ResourceId, as_template: bool, ) -> crate::Result<()> { - let resources_table = webview.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_icon_as_template(as_template) } #[command(root = "crate")] fn set_show_menu_on_left_click( - webview: Webview, + app: AppHandle, rid: ResourceId, on_left: bool, ) -> crate::Result<()> { - let resources_table = webview.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_show_menu_on_left_click(on_left) } pub(crate) fn init() -> TauriPlugin { Builder::new("tray") - .setup(|app, _api| { - app.manage(TrayIcons { - icons: Default::default(), - }); - Ok(()) - }) .invoke_handler(crate::generate_handler![ #![plugin(tray)] new,