mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-02-06 13:57:16 +00:00
fix: use app's resource table for storing tray icons (#13316)
* Use app's resource table for storing tray icons * Clean up * Move remove tray logic to Resource::close
This commit is contained in:
parent
b985eaf0a2
commit
4221124c4e
@ -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()
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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<R: Runtime> {
|
||||
pub(crate) icon: Option<Image<'static>>,
|
||||
/// Tray icons
|
||||
pub(crate) icons: Mutex<Vec<TrayIcon<R>>>,
|
||||
pub(crate) icons: Mutex<Vec<(TrayIconId, ResourceId)>>,
|
||||
/// Global Tray icon event listeners.
|
||||
pub(crate) global_event_listeners: Mutex<Vec<GlobalTrayIconEventListener<AppHandle<R>>>>,
|
||||
/// Tray icon event listeners.
|
||||
@ -41,30 +45,46 @@ impl<R: Runtime> TrayManager<R> {
|
||||
.push(Box::new(handler));
|
||||
}
|
||||
|
||||
pub fn tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
|
||||
pub fn tray_by_id<'a, I>(&self, app: &AppHandle<R>, id: &'a I) -> Option<TrayIcon<R>>
|
||||
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::<TrayIcon<R>>(*rid).ok()?;
|
||||
Some(Arc::unwrap_or_clone(icon))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option<TrayIcon<R>>
|
||||
pub fn tray_resource_by_id<'a, I>(&self, id: &'a I) -> Option<ResourceId>
|
||||
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 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, app: &AppHandle<R>, id: &'a I) -> Option<TrayIcon<R>>
|
||||
where
|
||||
I: ?Sized,
|
||||
TrayIconId: PartialEq<&'a I>,
|
||||
{
|
||||
let rid = self.tray_resource_by_id(id)?;
|
||||
let icon = app.resources_table().take::<TrayIcon<R>>(rid).ok()?;
|
||||
let icon_to_return = icon.clone();
|
||||
icon.close();
|
||||
Some(Arc::unwrap_or_clone(icon_to_return))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<R: Runtime> TrayIconBuilder<R> {
|
||||
self.inner.id()
|
||||
}
|
||||
|
||||
/// Builds and adds a new [`TrayIcon`] to the system tray.
|
||||
pub fn build<M: Manager<R>>(self, manager: &M) -> crate::Result<TrayIcon<R>> {
|
||||
pub(crate) fn build_inner(
|
||||
self,
|
||||
app_handle: &AppHandle<R>,
|
||||
) -> crate::Result<(TrayIcon<R>, ResourceId)> {
|
||||
let id = self.id().clone();
|
||||
|
||||
// SAFETY:
|
||||
@ -368,8 +370,7 @@ impl<R: Runtime> TrayIconBuilder<R> {
|
||||
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<R: Runtime> TrayIconBuilder<R> {
|
||||
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<M: Manager<R>>(self, manager: &M) -> crate::Result<TrayIcon<R>> {
|
||||
let (icon, _rid) = self.build_inner(manager.app_handle())?;
|
||||
Ok(icon)
|
||||
}
|
||||
}
|
||||
@ -426,7 +433,7 @@ impl<R: Runtime> TrayIcon<R> {
|
||||
app_handle: &AppHandle<R>,
|
||||
on_menu_event: Option<GlobalMenuEventListener<AppHandle<R>>>,
|
||||
on_tray_icon_event: Option<GlobalTrayIconEventListener<TrayIcon<R>>>,
|
||||
) {
|
||||
) -> ResourceId {
|
||||
if let Some(handler) = on_menu_event {
|
||||
app_handle
|
||||
.manager
|
||||
@ -447,13 +454,15 @@ impl<R: Runtime> TrayIcon<R> {
|
||||
.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<R: Runtime> TrayIcon<R> {
|
||||
|
||||
impl<R: Runtime> Resource for TrayIcon<R> {
|
||||
fn close(self: std::sync::Arc<Self>) {
|
||||
self
|
||||
.app_handle
|
||||
.state::<plugin::TrayIcons>()
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<HashMap<String, ResourceId>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct TrayIconOptions {
|
||||
@ -40,7 +37,6 @@ struct TrayIconOptions {
|
||||
#[command(root = "crate")]
|
||||
fn new<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
icons: State<'_, TrayIcons>,
|
||||
options: TrayIconOptions,
|
||||
handler: Channel<TrayIconEvent>,
|
||||
) -> crate::Result<(ResourceId, String)> {
|
||||
@ -54,7 +50,7 @@ fn new<R: Runtime>(
|
||||
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<R: Runtime>(
|
||||
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<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
webview: Webview<R>,
|
||||
icons: State<'_, TrayIcons>,
|
||||
id: &str,
|
||||
) -> crate::Result<Option<ResourceId>> {
|
||||
// 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<R: Runtime>(app: AppHandle<R>, id: &str) -> Option<ResourceId> {
|
||||
app.manager.tray.tray_resource_by_id(id)
|
||||
}
|
||||
|
||||
#[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)
|
||||
.with_context(|| format!("Can't find a tray associated with this id: {id}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_icon<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
icon: Option<JsImage>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = webview.resources_table();
|
||||
let resources_table = app.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
let icon = match icon {
|
||||
Some(i) => Some(i.into_img(&resources_table)?.as_ref().clone()),
|
||||
@ -154,11 +124,11 @@ fn set_icon<R: Runtime>(
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_menu<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
app: AppHandle<R>,
|
||||
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::<TrayIcon<R>>(rid)?;
|
||||
if let Some((rid, kind)) = menu {
|
||||
match kind {
|
||||
@ -180,78 +150,68 @@ fn set_menu<R: Runtime>(
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_tooltip<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
tooltip: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = webview.resources_table();
|
||||
let resources_table = app.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_tooltip(tooltip)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_title<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
title: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = webview.resources_table();
|
||||
let resources_table = app.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_title(title)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_visible<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
rid: ResourceId,
|
||||
visible: bool,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = webview.resources_table();
|
||||
fn set_visible<R: Runtime>(app: AppHandle<R>, rid: ResourceId, visible: bool) -> crate::Result<()> {
|
||||
let resources_table = app.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_visible(visible)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_temp_dir_path<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
path: Option<PathBuf>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = webview.resources_table();
|
||||
let resources_table = app.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_temp_dir_path(path)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_icon_as_template<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
as_template: bool,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = webview.resources_table();
|
||||
let resources_table = app.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_icon_as_template(as_template)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_show_menu_on_left_click<R: Runtime>(
|
||||
webview: Webview<R>,
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
on_left: bool,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = webview.resources_table();
|
||||
let resources_table = app.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_show_menu_on_left_click(on_left)
|
||||
}
|
||||
|
||||
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("tray")
|
||||
.setup(|app, _api| {
|
||||
app.manage(TrayIcons {
|
||||
icons: Default::default(),
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(crate::generate_handler![
|
||||
#![plugin(tray)]
|
||||
new,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user