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>
This commit is contained in:
Amr Bashir 2024-03-12 18:07:51 +02:00 committed by GitHub
parent e227fe02f9
commit acdd76833d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 111 additions and 55 deletions

View File

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

View File

@ -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.

View File

@ -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),

View File

@ -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 one or more lines are too long

View File

@ -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.

View File

@ -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));
}
}

View File

@ -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
}
}

View File

@ -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,

View File

@ -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}
*