feat: expose Manager::resources_table (#8276)

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
This commit is contained in:
Amr Bashir 2023-11-21 12:57:35 +02:00 committed by GitHub
parent 32bf201655
commit bf095df55a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 146 additions and 58 deletions

View File

@ -0,0 +1,5 @@
---
'tauri': 'patch:feat'
---
Exposed `Manager::resources_table` to access the resources table used by tauri, which could be used by plugins or app authors to store their resources and retrieve it later using an id.

View File

@ -114,14 +114,14 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre
/// ```ignore
/// let rid = 23;
/// let kind = ItemKind::Check;
/// let resources_table = app.manager.resources_table();
/// let resources_table = app.resources_table();
/// do_menu_item!(|i| i.set_text(text))
/// ```
/// which will expand into:
/// ```ignore
/// let rid = 23;
/// let kind = ItemKind::Check;
/// let resources_table = app.manager.resources_table();
/// let resources_table = app.resources_table();
/// match kind {
/// ItemKind::Submenu => {
/// let i = resources_table.get::<Submenu<R>>(rid)?;

View File

@ -773,7 +773,7 @@ macro_rules! shared_app_impl {
pub fn cleanup_before_exit(&self) {
#[cfg(all(desktop, feature = "tray-icon"))]
self.manager.tray.icons.lock().unwrap().clear();
self.manager.resources_table().clear();
self.resources_table().clear();
}
}
};

View File

@ -65,6 +65,7 @@ pub use cocoa;
#[doc(hidden)]
pub use embed_plist;
pub use error::{Error, Result};
pub use resources::{Resource, ResourceId, ResourceTable};
#[cfg(target_os = "ios")]
#[doc(hidden)]
pub use swift_rs;
@ -165,6 +166,7 @@ use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::{self, Debug},
sync::MutexGuard,
};
#[cfg(feature = "wry")]
@ -809,6 +811,11 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
self.manager().state.try_get()
}
/// Get a reference to the resources table.
fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
self.manager().resources_table()
}
/// Gets the managed [`Env`].
fn env(&self) -> Env {
self.state::<Env>().inner().clone()

View File

@ -334,7 +334,7 @@ fn new<R: Runtime>(
handler: Channel,
) -> crate::Result<(ResourceId, MenuId)> {
let options = options.unwrap_or_default();
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
let (rid, id) = match kind {
ItemKind::Menu => {
@ -439,7 +439,7 @@ fn append<R: Runtime>(
kind: ItemKind,
items: Vec<MenuItemPayloadKind>,
) -> crate::Result<()> {
let resources_table = window.manager.resources_table();
let resources_table = window.resources_table();
match kind {
ItemKind::Menu => {
let menu = resources_table.get::<Menu<R>>(rid)?;
@ -466,7 +466,7 @@ fn prepend<R: Runtime>(
kind: ItemKind,
items: Vec<MenuItemPayloadKind>,
) -> crate::Result<()> {
let resources_table = window.manager.resources_table();
let resources_table = window.resources_table();
match kind {
ItemKind::Menu => {
let menu = resources_table.get::<Menu<R>>(rid)?;
@ -494,7 +494,7 @@ fn insert<R: Runtime>(
items: Vec<MenuItemPayloadKind>,
mut position: usize,
) -> crate::Result<()> {
let resources_table = window.manager.resources_table();
let resources_table = window.resources_table();
match kind {
ItemKind::Menu => {
let menu = resources_table.get::<Menu<R>>(rid)?;
@ -523,7 +523,7 @@ fn remove<R: Runtime>(
menu_kind: ItemKind,
item: (ResourceId, ItemKind),
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let (rid, kind) = item;
match menu_kind {
ItemKind::Menu => {
@ -561,7 +561,7 @@ fn remove_at<R: Runtime>(
kind: ItemKind,
position: usize,
) -> crate::Result<Option<(ResourceId, MenuId, ItemKind)>> {
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
match kind {
ItemKind::Menu => {
let menu = resources_table.get::<Menu<R>>(rid)?;
@ -587,7 +587,7 @@ fn items<R: Runtime>(
rid: ResourceId,
kind: ItemKind,
) -> crate::Result<Vec<(ResourceId, MenuId, ItemKind)>> {
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
let items = match kind {
ItemKind::Menu => resources_table.get::<Menu<R>>(rid)?.items()?,
ItemKind::Submenu => resources_table.get::<Submenu<R>>(rid)?.items()?,
@ -609,7 +609,7 @@ fn get<R: Runtime>(
kind: ItemKind,
id: MenuId,
) -> crate::Result<Option<(ResourceId, MenuId, ItemKind)>> {
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
match kind {
ItemKind::Menu => {
let menu = resources_table.get::<Menu<R>>(rid)?;
@ -643,7 +643,7 @@ async fn popup<R: Runtime>(
.unwrap_or(Some(current_window));
if let Some(window) = window {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
match kind {
ItemKind::Menu => {
let menu = resources_table.get::<Menu<R>>(rid)?;
@ -662,7 +662,7 @@ async fn popup<R: Runtime>(
#[command(root = "crate")]
fn default<R: Runtime>(app: AppHandle<R>) -> crate::Result<(ResourceId, MenuId)> {
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
let menu = Menu::default(&app)?;
let id = menu.id().clone();
let rid = resources_table.add(menu);
@ -674,7 +674,7 @@ async fn set_as_app_menu<R: Runtime>(
app: AppHandle<R>,
rid: ResourceId,
) -> crate::Result<Option<(ResourceId, MenuId)>> {
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
let menu = resources_table.get::<Menu<R>>(rid)?;
if let Some(menu) = menu.set_as_app_menu()? {
let id = menu.id().clone();
@ -696,7 +696,7 @@ async fn set_as_window_menu<R: Runtime>(
.unwrap_or(Some(current_window));
if let Some(window) = window {
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
let menu = resources_table.get::<Menu<R>>(rid)?;
if let Some(menu) = menu.set_as_window_menu(&window)? {
let id = menu.id().clone();
@ -709,7 +709,7 @@ async fn set_as_window_menu<R: Runtime>(
#[command(root = "crate")]
fn text<R: Runtime>(app: AppHandle<R>, rid: ResourceId, kind: ItemKind) -> crate::Result<String> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
do_menu_item!(resources_table, rid, kind, |i| i.text())
}
@ -720,7 +720,7 @@ fn set_text<R: Runtime>(
kind: ItemKind,
text: String,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
do_menu_item!(resources_table, rid, kind, |i| i.set_text(text))
}
@ -730,7 +730,7 @@ fn is_enabled<R: Runtime>(
rid: ResourceId,
kind: ItemKind,
) -> crate::Result<bool> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
do_menu_item!(resources_table, rid, kind, |i| i.is_enabled(), !Predefined)
}
@ -741,7 +741,7 @@ fn set_enabled<R: Runtime>(
kind: ItemKind,
enabled: bool,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
do_menu_item!(
resources_table,
rid,
@ -758,7 +758,7 @@ fn set_accelerator<R: Runtime>(
kind: ItemKind,
accelerator: Option<String>,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
do_menu_item!(
resources_table,
rid,
@ -775,7 +775,7 @@ fn set_as_windows_menu_for_nsapp<R: Runtime>(
) -> crate::Result<()> {
#[cfg(target_os = "macos")]
{
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let submenu = resources_table.get::<Submenu<R>>(rid)?;
submenu.set_as_help_menu_for_nsapp()?;
}
@ -789,7 +789,7 @@ fn set_as_windows_menu_for_nsapp<R: Runtime>(
fn set_as_help_menu_for_nsapp<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<()> {
#[cfg(target_os = "macos")]
{
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let submenu = resources_table.get::<Submenu<R>>(rid)?;
submenu.set_as_help_menu_for_nsapp()?;
}
@ -802,14 +802,14 @@ fn set_as_help_menu_for_nsapp<R: Runtime>(app: AppHandle<R>, rid: ResourceId) ->
#[command(root = "crate")]
fn is_checked<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<bool> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let check_item = resources_table.get::<CheckMenuItem<R>>(rid)?;
check_item.is_checked()
}
#[command(root = "crate")]
fn set_checked<R: Runtime>(app: AppHandle<R>, rid: ResourceId, checked: bool) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let check_item = resources_table.get::<CheckMenuItem<R>>(rid)?;
check_item.set_checked(checked)
}
@ -820,7 +820,7 @@ fn set_icon<R: Runtime>(
rid: ResourceId,
icon: Option<Icon>,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let icon_item = resources_table.get::<IconMenuItem<R>>(rid)?;
match icon {
Some(Icon::Native(icon)) => icon_item.set_native_icon(Some(icon)),

View File

@ -24,7 +24,7 @@ use std::{
/// cloned and passed around. When the last reference is dropped, the resource
/// is automatically closed. As long as the resource exists in the resource
/// table, the reference count is at least 1.
pub(crate) trait Resource: Any + 'static + Send + Sync {
pub trait Resource: Any + 'static + Send + Sync {
/// Returns a string representation of the resource. The default implementation
/// returns the Rust type name, but specific resource types may override this
/// trait method.
@ -60,9 +60,8 @@ impl dyn Resource {
/// A `ResourceId` is an integer value referencing a resource. It could be
/// considered to be the tauri equivalent of a `file descriptor` in POSIX like
/// operating systems. Elsewhere in the code base it is commonly abbreviated
/// to `rid`.
pub(crate) type ResourceId = u32;
/// operating systems.
pub type ResourceId = u32;
/// Map-like data structure storing Tauri's resources (equivalent to file
/// descriptors).
@ -74,9 +73,9 @@ pub(crate) type ResourceId = u32;
/// Each resource is identified through a _resource ID (rid)_, which acts as
/// the key in the map.
#[derive(Default)]
pub(crate) struct ResourceTable {
pub(crate) index: BTreeMap<ResourceId, Arc<dyn Resource>>,
pub(crate) next_rid: ResourceId,
pub struct ResourceTable {
index: BTreeMap<ResourceId, Arc<dyn Resource>>,
next_rid: ResourceId,
}
impl ResourceTable {
@ -86,7 +85,7 @@ impl ResourceTable {
/// when retrieving it through `get()`.
///
/// Returns a unique resource ID, which acts as a key for this resource.
pub(crate) fn add<T: Resource>(&mut self, resource: T) -> ResourceId {
pub fn add<T: Resource>(&mut self, resource: T) -> ResourceId {
self.add_arc(Arc::new(resource))
}
@ -96,24 +95,34 @@ impl ResourceTable {
/// when retrieving it through `get()`.
///
/// Returns a unique resource ID, which acts as a key for this resource.
pub(crate) fn add_arc<T: Resource>(&mut self, resource: Arc<T>) -> ResourceId {
pub fn add_arc<T: Resource>(&mut self, resource: Arc<T>) -> ResourceId {
let resource = resource as Arc<dyn Resource>;
self.add_arc_dyn(resource)
}
pub(crate) fn add_arc_dyn(&mut self, resource: Arc<dyn Resource>) -> ResourceId {
/// Inserts a `Arc`-wrapped resource into the resource table.
///
/// The resource type is erased at runtime and must be statically known
/// when retrieving it through `get()`.
///
/// Returns a unique resource ID, which acts as a key for this resource.
pub fn add_arc_dyn(&mut self, resource: Arc<dyn Resource>) -> ResourceId {
let rid = self.next_rid;
let removed_resource = self.index.insert(rid, resource);
// TODO: add replace method if needed
assert!(removed_resource.is_none());
self.next_rid += 1;
rid
}
/// Returns true if any resource with the given `rid` exists.
pub fn has(&self, rid: ResourceId) -> bool {
self.index.contains_key(&rid)
}
/// Returns a reference counted pointer to the resource of type `T` with the
/// given `rid`. If `rid` is not present or has a type different than `T`,
/// this function returns [`Error::BadResourceId`].
pub(crate) fn get<T: Resource>(&self, rid: ResourceId) -> Result<Arc<T>, Error> {
pub fn get<T: Resource>(&self, rid: ResourceId) -> Result<Arc<T>, Error> {
self
.index
.get(&rid)
@ -122,13 +131,75 @@ impl ResourceTable {
.ok_or_else(|| Error::BadResourceId(rid))
}
/// Returns a reference counted pointer to the resource of the given `rid`.
/// If `rid` is not present, this function returns [`Error::BadResourceId`].
pub fn get_any(&self, rid: ResourceId) -> Result<Arc<dyn Resource>, Error> {
self
.index
.get(&rid)
.map(Clone::clone)
.ok_or_else(|| Error::BadResourceId(rid))
}
/// Replaces a resource with a new resource.
///
/// Panics if the resource does not exist.
pub fn replace<T: Resource>(&mut self, rid: ResourceId, resource: T) {
let result = self
.index
.insert(rid, Arc::new(resource) as Arc<dyn Resource>);
assert!(result.is_some());
}
/// Removes a resource of type `T` from the resource table and returns it.
/// If a resource with the given `rid` exists but its type does not match `T`,
/// it is not removed from the resource table. Note that the resource's
/// `close()` method is *not* called.
///
/// Also note that there might be a case where
/// the returned `Arc<T>` is referenced by other variables. That is, we cannot
/// assume that `Arc::strong_count(&returned_arc)` is always equal to 1 on success.
/// In particular, be really careful when you want to extract the inner value of
/// type `T` from `Arc<T>`.
pub fn take<T: Resource>(&mut self, rid: ResourceId) -> Result<Arc<T>, Error> {
let resource = self.get::<T>(rid)?;
self.index.remove(&rid);
Ok(resource)
}
/// Removes a resource from the resource table and returns it. Note that the
/// resource's `close()` method is *not* called.
///
/// Also note that there might be a
/// case where the returned `Arc<T>` is referenced by other variables. That is,
/// we cannot assume that `Arc::strong_count(&returned_arc)` is always equal to 1
/// on success. In particular, be really careful when you want to extract the
/// inner value of type `T` from `Arc<T>`.
pub fn take_any(&mut self, rid: ResourceId) -> Result<Arc<dyn Resource>, Error> {
self
.index
.remove(&rid)
.ok_or_else(|| Error::BadResourceId(rid))
}
/// Returns an iterator that yields a `(id, name)` pair for every resource
/// that's currently in the resource table. This can be used for debugging
/// purposes. Note that the order in
/// which items appear is not specified.
pub fn names(&self) -> impl Iterator<Item = (ResourceId, Cow<'_, str>)> {
self
.index
.iter()
.map(|(&id, resource)| (id, resource.name()))
}
/// Removes the resource with the given `rid` from the resource table. If the
/// only reference to this resource existed in the resource table, this will
/// cause the resource to be dropped. However, since resources are reference
/// counted, therefore pending ops are not automatically cancelled. A resource
/// may implement the `close()` method to perform clean-ups such as canceling
/// ops.
pub(crate) fn close(&mut self, rid: ResourceId) -> Result<(), Error> {
pub fn close(&mut self, rid: ResourceId) -> Result<(), Error> {
self
.index
.remove(&rid)
@ -136,7 +207,8 @@ impl ResourceTable {
.map(|resource| resource.close())
}
/// Removes and frees all resources stored.
/// Removes and frees all resources stored. Note that the
/// resource's `close()` method is *not* called.
pub(crate) fn clear(&mut self) {
self.index.clear()
}

View File

@ -5,14 +5,14 @@
use crate::{
command,
plugin::{Builder, TauriPlugin},
AppHandle, Runtime,
AppHandle, Manager, Runtime,
};
use super::ResourceId;
#[command(root = "crate")]
fn close<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<()> {
app.manager.resources_table().close(rid)
app.resources_table().close(rid)
}
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {

View File

@ -72,7 +72,6 @@ impl StateManager {
/// Gets the state associated with the specified type.
pub fn get<T: Send + Sync + 'static>(&self) -> State<'_, T> {
self.0.get::<T>();
State(
self
.0

View File

@ -13,7 +13,7 @@ use crate::{
plugin::{Builder, TauriPlugin},
resources::ResourceId,
tray::TrayIconBuilder,
AppHandle, IconDto, Runtime,
AppHandle, IconDto, Manager, Runtime,
};
use super::TrayIcon;
@ -47,7 +47,7 @@ fn new<R: Runtime>(
let _ = handler.send(e);
});
let mut resources_table = app.manager.resources_table();
let mut resources_table = app.resources_table();
if let Some((rid, kind)) = options.menu {
match kind {
@ -94,7 +94,7 @@ fn set_icon<R: Runtime>(
rid: ResourceId,
icon: Option<IconDto>,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
tray.set_icon(icon.map(Into::into))
}
@ -105,7 +105,7 @@ fn set_menu<R: Runtime>(
rid: ResourceId,
menu: Option<(ResourceId, ItemKind)>,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
if let Some((rid, kind)) = menu {
match kind {
@ -131,7 +131,7 @@ fn set_tooltip<R: Runtime>(
rid: ResourceId,
tooltip: Option<String>,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
tray.set_tooltip(tooltip)
}
@ -142,14 +142,14 @@ fn set_title<R: Runtime>(
rid: ResourceId,
title: Option<String>,
) -> crate::Result<()> {
let resources_table = app.manager.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>(app: AppHandle<R>, rid: ResourceId, visible: bool) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
tray.set_visible(visible)
}
@ -160,7 +160,7 @@ fn set_temp_dir_path<R: Runtime>(
rid: ResourceId,
path: Option<PathBuf>,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
tray.set_temp_dir_path(path)
}
@ -171,7 +171,7 @@ fn set_icon_as_template<R: Runtime>(
rid: ResourceId,
as_template: bool,
) -> crate::Result<()> {
let resources_table = app.manager.resources_table();
let resources_table = app.resources_table();
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
tray.set_icon_as_template(as_template)
}
@ -182,7 +182,7 @@ fn set_show_menu_on_left_click<R: Runtime>(
rid: ResourceId,
on_left: bool,
) -> crate::Result<()> {
let resources_table = app.manager.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)
}

View File

@ -1,5 +1,5 @@
<script>
import { onMount } from 'svelte'
import { onMount, tick } from "svelte";
import { writable } from 'svelte/store'
import { invoke } from '@tauri-apps/api/core'
@ -81,30 +81,35 @@
// Console
let messages = writable([])
function onMessage(value) {
let consoleTextEl;
async function onMessage(value) {
messages.update((r) => [
...r
{
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
(typeof value === 'string' ? value : JSON.stringify(value, null, 1)) +
'</pre>'
},
...r
])
await tick();
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight;
}
// this function is renders HTML without sanitizing it so it's insecure
// we only use it with our own input data
function insecureRenderHtml(html) {
async function insecureRenderHtml(html) {
messages.update((r) => [
...r
{
html:
`<pre><strong class="text-accent dark:text-darkAccent">[${new Date().toLocaleTimeString()}]:</strong> ` +
html +
'</pre>'
},
...r
])
await tick();
if (consoleTextEl) consoleTextEl.scrollTop = consoleTextEl.scrollHeight;
}
function clear() {
@ -330,7 +335,7 @@
<div class="i-codicon-clear-all" />
</div>
</div>
<div class="px-2 overflow-y-auto all:font-mono code-block all:text-xs">
<div bind:this={consoleTextEl} class="px-2 overflow-y-auto all:font-mono code-block all:text-xs select-text mr-2">
{#each $messages as r}
{@html r.html}
{/each}