Skip to content

Commit acdd768

Browse files
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>
1 parent e227fe0 commit acdd768

10 files changed

Lines changed: 111 additions & 55 deletions

File tree

.changes/api-tray-by-id.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tauri-apps/api': 'patch:feat'
3+
---
4+
5+
Add `TrayIcon.getById` and `TrayIcon.removeById` static methods.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'tauri': 'patch:breaking'
3+
---
4+
5+
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.

core/tauri/build.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
180180
"tray",
181181
&[
182182
("new", false),
183+
("get_by_id", false),
184+
("remove_by_id", false),
183185
("set_icon", false),
184186
("set_menu", false),
185187
("set_tooltip", false),

core/tauri/permissions/tray/autogenerated/reference.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
| Permission | Description |
22
|------|-----|
3+
|`allow-get-by-id`|Enables the get_by_id command without any pre-configured scope.|
4+
|`deny-get-by-id`|Denies the get_by_id command without any pre-configured scope.|
35
|`allow-new`|Enables the new command without any pre-configured scope.|
46
|`deny-new`|Denies the new command without any pre-configured scope.|
7+
|`allow-remove-by-id`|Enables the remove_by_id command without any pre-configured scope.|
8+
|`deny-remove-by-id`|Denies the remove_by_id command without any pre-configured scope.|
59
|`allow-set-icon`|Enables the set_icon command without any pre-configured scope.|
610
|`deny-set-icon`|Denies the set_icon command without any pre-configured scope.|
711
|`allow-set-icon-as-template`|Enables the set_icon_as_template command without any pre-configured scope.|

core/tauri/scripts/bundle.global.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/tauri/src/app.rs

Lines changed: 6 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -509,13 +509,7 @@ macro_rules! shared_app_impl {
509509
&self,
510510
handler: F,
511511
) {
512-
self
513-
.manager
514-
.menu
515-
.global_event_listeners
516-
.lock()
517-
.unwrap()
518-
.push(Box::new(handler));
512+
self.manager.menu.on_menu_event(handler)
519513
}
520514

521515
/// Registers a global tray icon menu event listener.
@@ -525,35 +519,7 @@ macro_rules! shared_app_impl {
525519
&self,
526520
handler: F,
527521
) {
528-
self
529-
.manager
530-
.tray
531-
.global_event_listeners
532-
.lock()
533-
.unwrap()
534-
.push(Box::new(handler));
535-
}
536-
537-
/// Gets the first tray icon registered,
538-
/// usually the one configured in the Tauri configuration file.
539-
#[cfg(all(desktop, feature = "tray-icon"))]
540-
#[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
541-
pub fn tray(&self) -> Option<TrayIcon<R>> {
542-
self.manager.tray.icons.lock().unwrap().first().cloned()
543-
}
544-
545-
/// Removes the first tray icon registered, usually the one configured in
546-
/// tauri config file, from tauri's internal state and returns it.
547-
///
548-
/// Note that dropping the returned icon, will cause the tray icon to disappear.
549-
#[cfg(all(desktop, feature = "tray-icon"))]
550-
#[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
551-
pub fn remove_tray(&self) -> Option<TrayIcon<R>> {
552-
let mut icons = self.manager.tray.icons.lock().unwrap();
553-
if !icons.is_empty() {
554-
return Some(icons.swap_remove(0));
555-
}
556-
None
522+
self.manager.tray.on_tray_icon_event(handler)
557523
}
558524

559525
/// Gets a tray icon using the provided id.
@@ -564,33 +530,21 @@ macro_rules! shared_app_impl {
564530
I: ?Sized,
565531
TrayIconId: PartialEq<&'a I>,
566532
{
567-
self
568-
.manager
569-
.tray
570-
.icons
571-
.lock()
572-
.unwrap()
573-
.iter()
574-
.find(|t| t.id() == &id)
575-
.cloned()
533+
self.manager.tray.tray_by_id(id)
576534
}
577535

578536
/// Removes a tray icon using the provided id from tauri's internal state and returns it.
579537
///
580-
/// Note that dropping the returned icon, will cause the tray icon to disappear.
538+
/// Note that dropping the returned icon, may cause the tray icon to disappear
539+
/// if it wasn't cloned somewhere else or referenced by JS.
581540
#[cfg(all(desktop, feature = "tray-icon"))]
582541
#[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))]
583542
pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
584543
where
585544
I: ?Sized,
586545
TrayIconId: PartialEq<&'a I>,
587546
{
588-
let mut icons = self.manager.tray.icons.lock().unwrap();
589-
let idx = icons.iter().position(|t| t.id() == &id);
590-
if let Some(idx) = idx {
591-
return Some(icons.swap_remove(idx));
592-
}
593-
None
547+
self.manager.tray.remove_tray_by_id(id)
594548
}
595549

596550
/// Gets the app's configuration, defined on the `tauri.conf.json` file.

core/tauri/src/manager/menu.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
};
99

1010
use crate::{
11-
menu::{Menu, MenuId},
11+
menu::{Menu, MenuEvent, MenuId},
1212
AppHandle, Runtime, Window,
1313
};
1414

@@ -87,4 +87,12 @@ impl<R: Runtime> MenuManager<R> {
8787
None
8888
}
8989
}
90+
91+
pub fn on_menu_event<F: Fn(&AppHandle<R>, MenuEvent) + Send + Sync + 'static>(&self, handler: F) {
92+
self
93+
.global_event_listeners
94+
.lock()
95+
.unwrap()
96+
.push(Box::new(handler));
97+
}
9098
}

core/tauri/src/manager/tray.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{collections::HashMap, fmt, sync::Mutex};
77
use crate::{
88
app::GlobalTrayIconEventListener,
99
image::Image,
10-
tray::{TrayIcon, TrayIconId},
10+
tray::{TrayIcon, TrayIconEvent, TrayIconId},
1111
AppHandle, Runtime,
1212
};
1313

@@ -28,3 +28,43 @@ impl<R: Runtime> fmt::Debug for TrayManager<R> {
2828
.finish()
2929
}
3030
}
31+
32+
impl<R: Runtime> TrayManager<R> {
33+
pub fn on_tray_icon_event<F: Fn(&AppHandle<R>, TrayIconEvent) + Send + Sync + 'static>(
34+
&self,
35+
handler: F,
36+
) {
37+
self
38+
.global_event_listeners
39+
.lock()
40+
.unwrap()
41+
.push(Box::new(handler));
42+
}
43+
44+
pub fn tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
45+
where
46+
I: ?Sized,
47+
TrayIconId: PartialEq<&'a I>,
48+
{
49+
self
50+
.icons
51+
.lock()
52+
.unwrap()
53+
.iter()
54+
.find(|t| t.id() == &id)
55+
.cloned()
56+
}
57+
58+
pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
59+
where
60+
I: ?Sized,
61+
TrayIconId: PartialEq<&'a I>,
62+
{
63+
let mut icons = self.icons.lock().unwrap();
64+
let idx = icons.iter().position(|t| t.id() == &id);
65+
if let Some(idx) = idx {
66+
return Some(icons.swap_remove(idx));
67+
}
68+
None
69+
}
70+
}

core/tauri/src/tray/plugin.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,25 @@ fn new<R: Runtime>(
8989
Ok((rid, id))
9090
}
9191

92+
#[command(root = "crate")]
93+
fn get_by_id<R: Runtime>(app: AppHandle<R>, id: &str) -> crate::Result<Option<ResourceId>> {
94+
let tray = app.tray_by_id(id);
95+
let maybe_rid = tray.map(|tray| {
96+
let mut resources_table = app.resources_table();
97+
resources_table.add(tray)
98+
});
99+
Ok(maybe_rid)
100+
}
101+
102+
#[command(root = "crate")]
103+
fn remove_by_id<R: Runtime>(app: AppHandle<R>, id: &str) -> crate::Result<()> {
104+
app
105+
.remove_tray_by_id(id)
106+
.ok_or_else(|| anyhow::anyhow!("Can't find a tray associated with this id: {id}"))
107+
.map(|_| ())
108+
.map_err(Into::into)
109+
}
110+
92111
#[command(root = "crate")]
93112
fn set_icon<R: Runtime>(
94113
app: AppHandle<R>,
@@ -196,6 +215,8 @@ pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
196215
Builder::new("tray")
197216
.invoke_handler(crate::generate_handler![
198217
new,
218+
get_by_id,
219+
remove_by_id,
199220
set_icon,
200221
set_menu,
201222
set_tooltip,

tooling/api/src/tray.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,23 @@ export class TrayIcon extends Resource {
119119
this.id = id
120120
}
121121

122+
/** Gets a tray icon using the provided id. */
123+
static async getById(id: string): Promise<TrayIcon | null> {
124+
return invoke<number>('plugin:tray|get_by_id', { id }).then((rid) =>
125+
rid ? new TrayIcon(rid, id) : null
126+
)
127+
}
128+
129+
/**
130+
* Removes a tray icon using the provided id from tauri's internal state.
131+
*
132+
* Note that this may cause the tray icon to disappear
133+
* if it wasn't cloned somewhere else or referenced by JS.
134+
*/
135+
static async removeById(id: string): Promise<void> {
136+
return invoke('plugin:tray|remove_by_id', { id })
137+
}
138+
122139
/**
123140
* Creates a new {@linkcode TrayIcon}
124141
*

0 commit comments

Comments
 (0)