Browse Source

feat(api/tray): add `TrayIcon.getById/removeById` (#9155)

* feat(api/tray): add `TrayIcon.getById/removeById`

closes #9135

* generate

* add permissions

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Amr Bashir 1 year ago
parent
commit
acdd76833d

+ 5 - 0
.changes/api-tray-by-id.md

@@ -0,0 +1,5 @@
+---
+'@tauri-apps/api': 'patch:feat'
+---
+
+Add `TrayIcon.getById` and `TrayIcon.removeById` static methods.

+ 5 - 0
.changes/core-app-tray-remove-tray-apis-removed.md

@@ -0,0 +1,5 @@
+---
+'tauri': 'patch:breaking'
+---
+
+Removed `App/AppHandle::tray` and `App/AppHandle::remove_tray`, use `App/AppHandle::tray_by_id` and `App/AppHandle::remove_tray_by_id` instead. If these APIs were used to access tray icon configured in `tauri.conf.json`, you can use `App/AppHandle::tray_by_id` with ID `main` or the configured value.

+ 2 - 0
core/tauri/build.rs

@@ -180,6 +180,8 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
     "tray",
     &[
       ("new", false),
+      ("get_by_id", false),
+      ("remove_by_id", false),
       ("set_icon", false),
       ("set_menu", false),
       ("set_tooltip", false),

+ 4 - 0
core/tauri/permissions/tray/autogenerated/reference.md

@@ -1,7 +1,11 @@
 | Permission | Description |
 |------|-----|
+|`allow-get-by-id`|Enables the get_by_id command without any pre-configured scope.|
+|`deny-get-by-id`|Denies the get_by_id command without any pre-configured scope.|
 |`allow-new`|Enables the new command without any pre-configured scope.|
 |`deny-new`|Denies the new command without any pre-configured scope.|
+|`allow-remove-by-id`|Enables the remove_by_id command without any pre-configured scope.|
+|`deny-remove-by-id`|Denies the remove_by_id command without any pre-configured scope.|
 |`allow-set-icon`|Enables the set_icon command without any pre-configured scope.|
 |`deny-set-icon`|Denies the set_icon command without any pre-configured scope.|
 |`allow-set-icon-as-template`|Enables the set_icon_as_template command without any pre-configured scope.|

File diff suppressed because it is too large
+ 0 - 0
core/tauri/scripts/bundle.global.js


+ 6 - 52
core/tauri/src/app.rs

@@ -509,13 +509,7 @@ macro_rules! shared_app_impl {
         &self,
         handler: F,
       ) {
-        self
-          .manager
-          .menu
-          .global_event_listeners
-          .lock()
-          .unwrap()
-          .push(Box::new(handler));
+        self.manager.menu.on_menu_event(handler)
       }
 
       /// Registers a global tray icon menu event listener.
@@ -525,35 +519,7 @@ macro_rules! shared_app_impl {
         &self,
         handler: F,
       ) {
-        self
-          .manager
-          .tray
-          .global_event_listeners
-          .lock()
-          .unwrap()
-          .push(Box::new(handler));
-      }
-
-      /// Gets the first tray icon registered,
-      /// usually the one configured in the Tauri configuration file.
-      #[cfg(all(desktop, feature = "tray-icon"))]
-      #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
-      pub fn tray(&self) -> Option<TrayIcon<R>> {
-        self.manager.tray.icons.lock().unwrap().first().cloned()
-      }
-
-      /// Removes the first tray icon registered, usually the one configured in
-      /// tauri config file, from tauri's internal state and returns it.
-      ///
-      /// Note that dropping the returned icon, will cause the tray icon to disappear.
-      #[cfg(all(desktop, feature = "tray-icon"))]
-      #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
-      pub fn remove_tray(&self) -> Option<TrayIcon<R>> {
-        let mut icons = self.manager.tray.icons.lock().unwrap();
-        if !icons.is_empty() {
-          return Some(icons.swap_remove(0));
-        }
-        None
+        self.manager.tray.on_tray_icon_event(handler)
       }
 
       /// Gets a tray icon using the provided id.
@@ -564,20 +530,13 @@ macro_rules! shared_app_impl {
         I: ?Sized,
         TrayIconId: PartialEq<&'a I>,
       {
-        self
-          .manager
-          .tray
-          .icons
-          .lock()
-          .unwrap()
-          .iter()
-          .find(|t| t.id() == &id)
-          .cloned()
+        self.manager.tray.tray_by_id(id)
       }
 
       /// Removes a tray icon using the provided id from tauri's internal state and returns it.
       ///
-      /// Note that dropping the returned icon, will cause the tray icon to disappear.
+      /// Note that dropping the returned icon, may cause the tray icon to disappear
+      /// if it wasn't cloned somewhere else or referenced by JS.
       #[cfg(all(desktop, feature = "tray-icon"))]
       #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
       pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
@@ -585,12 +544,7 @@ macro_rules! shared_app_impl {
         I: ?Sized,
         TrayIconId: PartialEq<&'a I>,
       {
-        let mut icons = self.manager.tray.icons.lock().unwrap();
-        let idx = icons.iter().position(|t| t.id() == &id);
-        if let Some(idx) = idx {
-          return Some(icons.swap_remove(idx));
-        }
-        None
+        self.manager.tray.remove_tray_by_id(id)
       }
 
       /// Gets the app's configuration, defined on the `tauri.conf.json` file.

+ 9 - 1
core/tauri/src/manager/menu.rs

@@ -8,7 +8,7 @@ use std::{
 };
 
 use crate::{
-  menu::{Menu, MenuId},
+  menu::{Menu, MenuEvent, MenuId},
   AppHandle, Runtime, Window,
 };
 
@@ -87,4 +87,12 @@ impl<R: Runtime> MenuManager<R> {
       None
     }
   }
+
+  pub fn on_menu_event<F: Fn(&AppHandle<R>, MenuEvent) + Send + Sync + 'static>(&self, handler: F) {
+    self
+      .global_event_listeners
+      .lock()
+      .unwrap()
+      .push(Box::new(handler));
+  }
 }

+ 41 - 1
core/tauri/src/manager/tray.rs

@@ -7,7 +7,7 @@ use std::{collections::HashMap, fmt, sync::Mutex};
 use crate::{
   app::GlobalTrayIconEventListener,
   image::Image,
-  tray::{TrayIcon, TrayIconId},
+  tray::{TrayIcon, TrayIconEvent, TrayIconId},
   AppHandle, Runtime,
 };
 
@@ -28,3 +28,43 @@ impl<R: Runtime> fmt::Debug for TrayManager<R> {
       .finish()
   }
 }
+
+impl<R: Runtime> TrayManager<R> {
+  pub fn on_tray_icon_event<F: Fn(&AppHandle<R>, TrayIconEvent) + Send + Sync + 'static>(
+    &self,
+    handler: F,
+  ) {
+    self
+      .global_event_listeners
+      .lock()
+      .unwrap()
+      .push(Box::new(handler));
+  }
+
+  pub fn tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
+  where
+    I: ?Sized,
+    TrayIconId: PartialEq<&'a I>,
+  {
+    self
+      .icons
+      .lock()
+      .unwrap()
+      .iter()
+      .find(|t| t.id() == &id)
+      .cloned()
+  }
+
+  pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
+  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
+  }
+}

+ 21 - 0
core/tauri/src/tray/plugin.rs

@@ -89,6 +89,25 @@ fn new<R: Runtime>(
   Ok((rid, id))
 }
 
+#[command(root = "crate")]
+fn get_by_id<R: Runtime>(app: AppHandle<R>, id: &str) -> crate::Result<Option<ResourceId>> {
+  let tray = app.tray_by_id(id);
+  let maybe_rid = tray.map(|tray| {
+    let mut resources_table = app.resources_table();
+    resources_table.add(tray)
+  });
+  Ok(maybe_rid)
+}
+
+#[command(root = "crate")]
+fn remove_by_id<R: Runtime>(app: AppHandle<R>, 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)
+}
+
 #[command(root = "crate")]
 fn set_icon<R: Runtime>(
   app: AppHandle<R>,
@@ -196,6 +215,8 @@ pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
   Builder::new("tray")
     .invoke_handler(crate::generate_handler![
       new,
+      get_by_id,
+      remove_by_id,
       set_icon,
       set_menu,
       set_tooltip,

+ 17 - 0
tooling/api/src/tray.ts

@@ -119,6 +119,23 @@ export class TrayIcon extends Resource {
     this.id = id
   }
 
+  /** Gets a tray icon using the provided id. */
+  static async getById(id: string): Promise<TrayIcon | null> {
+    return invoke<number>('plugin:tray|get_by_id', { id }).then((rid) =>
+      rid ? new TrayIcon(rid, id) : null
+    )
+  }
+
+  /**
+   * Removes a tray icon using the provided id from tauri's internal state.
+   *
+   * Note that this may cause the tray icon to disappear
+   * if it wasn't cloned somewhere else or referenced by JS.
+   */
+  static async removeById(id: string): Promise<void> {
+    return invoke('plugin:tray|remove_by_id', { id })
+  }
+
   /**
    * Creates a new {@linkcode TrayIcon}
    *

Some files were not shown because too many files changed in this diff