refactor(core): remove Params and replace with strings (#2191)

* refactor(core): remove `Params` and replace with strings

* add tauri-utils to changelog

* update default runtime macro to accept type and feature

* remove accidental default feature addition

* remove changefile todo items that have no futher action

* fix clippy warning

* update changefile

* finish change file

* fix splashscreen example

* fix markdown typo [skip ci]

* remove final uses of `Params`

* add license header to new runtime module in tauri-macros

* update plugin guide to use runtime instead of params
This commit is contained in:
chip 2021-07-15 03:05:29 -07:00 committed by GitHub
parent 074caa3247
commit fd8fab507c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 855 additions and 1409 deletions

50
.changes/weak-typing.md Normal file
View File

@ -0,0 +1,50 @@
---
"tauri": patch
"tauri-runtime": patch
"tauri-runtime-wry": patch
"tauri-macros": patch
"tauri-utils": patch
---
`Params` has been removed, along with all the associated types on it. Functions that previously accepted those
associated types now accept strings instead. Type that used a generic parameter `Params` now use `Runtime` instead. If
you use the `wry` feature, then types with a `Runtime` generic parameter should default to `Wry`, letting you omit the
explicit type and let the compiler infer it instead.
`tauri`:
* See `Params` note
* If you were using `Params` inside a function parameter or definition, all references to it have been replaced with a
simple runtime that defaults to `Wry`. If you are not using a custom runtime, just remove `Params` from the definition
of functions/items that previously took it. If you are using a custom runtime, you _may_ need to pass the runtime type
to these functions.
* If you were using custom types for `Params` (uncommon and if you don't understand you probably were not using it), all
methods that were previously taking the custom type now takes an `Into<String>` or a `&str`. The types were already
required to be string-able, so just make sure to convert it into a string before passing it in if this breaking change
affects you.
`tauri-macros`:
* (internal) Added private `default_runtime` proc macro to allow us to give item definitions a custom runtime only when
the specified feature is enabled.
`tauri-runtime`:
* See `Params` note
* Removed `Params`, `MenuId`, `Tag`, `TagRef`.
* Added `menu::{MenuHash, MenuId, MenuIdRef}` as type aliases for the internal type that menu types now use.
* All previous menu items that had a `MenuId` generic now use the underlying `MenuId` type without a generic.
* `Runtime`, `RuntimeHandle`, and `Dispatch` have no more generic parameter on `create_window(...)` and instead use the
`Runtime` type directly
* `Runtime::system_tray` has no more `MenuId` generic and uses the string based `SystemTray` type directly.
* (internal) `CustomMenuItem::id_value()` is now hashed on creation and exposed as the `id` field with type `MenuHash`.
`tauri-runtime-wry`:
* See `Params` note
* update menu and runtime related types to the ones changed in `tauri-runtime`.
`tauri-utils`:
* `Assets::get` signature has changed to take a `&AssetKey` instead of `impl Into<AssetKey>` to become trait object
safe.

View File

@ -15,7 +15,6 @@ members = [
"examples/helloworld/src-tauri",
"examples/multiwindow/src-tauri",
"examples/navigation/src-tauri",
"examples/params/src-tauri",
"examples/splashscreen/src-tauri",
"examples/state/src-tauri",
"examples/sidecar/src-tauri",

View File

@ -5,9 +5,10 @@
extern crate proc_macro;
use crate::context::ContextItems;
use proc_macro::TokenStream;
use syn::parse_macro_input;
use syn::{parse_macro_input, DeriveInput};
mod command;
mod runtime;
#[macro_use]
mod context;
@ -60,3 +61,16 @@ pub fn generate_context(items: TokenStream) -> TokenStream {
let path = parse_macro_input!(items as ContextItems);
context::generate_context(path).into()
}
/// Adds the default type for the last parameter (assumed to be runtime) for a specific feature.
///
/// e.g. To default the runtime generic to type `crate::Wry` when the `wry` feature is enabled, the
/// syntax would look like `#[default_runtime(crate::Wry, wry)`. This is **always** set for the last
/// generic, so make sure the last generic is the runtime when using this macro.
#[doc(hidden)]
#[proc_macro_attribute]
pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStream {
let attributes = parse_macro_input!(attributes as runtime::Attributes);
let input = parse_macro_input!(input as DeriveInput);
runtime::default_runtime(attributes, input).into()
}

View File

@ -0,0 +1,62 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{parse_quote, DeriveInput, GenericParam, Ident, Token, Type, TypeParam};
/// The default runtime type to enable when the provided feature is enabled.
pub(crate) struct Attributes {
default_type: Type,
feature: Ident,
}
impl Parse for Attributes {
fn parse(input: ParseStream) -> syn::Result<Self> {
let default_type = input.parse()?;
input.parse::<Token![,]>()?;
Ok(Attributes {
default_type,
feature: input.parse()?,
})
}
}
pub(crate) fn default_runtime(attributes: Attributes, input: DeriveInput) -> TokenStream {
// create a new copy to manipulate for the wry feature flag
let mut wry = input.clone();
let wry_runtime = wry
.generics
.params
.last_mut()
.expect("default_runtime requires the item to have at least 1 generic parameter");
// set the default value of the last generic parameter to the provided runtime type
match wry_runtime {
GenericParam::Type(
param @ TypeParam {
eq_token: None,
default: None,
..
},
) => {
param.eq_token = Some(parse_quote!(=));
param.default = Some(attributes.default_type);
}
_ => {
panic!("DefaultRuntime requires the last parameter to not have a default value")
}
};
let feature = attributes.feature.to_string();
quote!(
#[cfg(feature = #feature)]
#wry
#[cfg(not(feature = #feature))]
#input
)
}

View File

@ -13,8 +13,8 @@ use tauri_runtime::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow, WindowEvent,
},
ClipboardManager, Dispatch, Error, GlobalShortcutManager, Icon, Params, Result, RunEvent,
RunIteration, Runtime, RuntimeHandle, UserAttentionType,
ClipboardManager, Dispatch, Error, GlobalShortcutManager, Icon, Result, RunEvent, RunIteration,
Runtime, RuntimeHandle, UserAttentionType,
};
#[cfg(feature = "menu")]
@ -407,7 +407,7 @@ pub struct WindowBuilderWrapper {
inner: WryWindowBuilder,
center: bool,
#[cfg(feature = "menu")]
menu: Menu<u16>,
menu: Menu,
}
// safe since `menu_items` are read only here
@ -454,7 +454,7 @@ impl WindowBuilder for WindowBuilderWrapper {
}
#[cfg(feature = "menu")]
fn menu<I: MenuId>(mut self, menu: Menu<I>) -> Self {
fn menu(mut self, menu: Menu) -> Self {
self.menu = convert_menu_id(Menu::new(), menu);
self
}
@ -898,10 +898,10 @@ impl Dispatch for WryDispatcher {
// Creates a window by dispatching a message to the event loop.
// Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
fn create_window<P: Params<Runtime = Self::Runtime>>(
fn create_window(
&mut self,
pending: PendingWindow<P>,
) -> Result<DetachedWindow<P>> {
pending: PendingWindow<Self::Runtime>,
) -> Result<DetachedWindow<Self::Runtime>> {
let (tx, rx) = channel();
let label = pending.label.clone();
let context = self.context.clone();
@ -1203,10 +1203,10 @@ impl RuntimeHandle for WryHandle {
// Creates a window by dispatching a message to the event loop.
// Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
fn create_window<P: Params<Runtime = Self::Runtime>>(
fn create_window(
&self,
pending: PendingWindow<P>,
) -> Result<DetachedWindow<P>> {
pending: PendingWindow<Self::Runtime>,
) -> Result<DetachedWindow<Self::Runtime>> {
let (tx, rx) = channel();
let label = pending.label.clone();
let dispatcher_context = self.dispatcher_context.clone();
@ -1306,10 +1306,7 @@ impl Runtime for Wry {
self.clipboard_manager_handle.clone()
}
fn create_window<P: Params<Runtime = Self>>(
&self,
pending: PendingWindow<P>,
) -> Result<DetachedWindow<P>> {
fn create_window(&self, pending: PendingWindow<Self>) -> Result<DetachedWindow<Self>> {
let label = pending.label.clone();
let proxy = self.event_loop.create_proxy();
let webview = create_webview(
@ -1347,7 +1344,7 @@ impl Runtime for Wry {
}
#[cfg(feature = "system-tray")]
fn system_tray<I: MenuId>(&self, system_tray: SystemTray<I>) -> Result<Self::TrayHandler> {
fn system_tray(&self, system_tray: SystemTray) -> Result<Self::TrayHandler> {
let icon = system_tray
.icon
.expect("tray icon not set")
@ -1904,10 +1901,10 @@ fn center_window(window: &Window) -> Result<()> {
}
}
fn create_webview<P: Params<Runtime = Wry>>(
fn create_webview(
event_loop: &EventLoopWindowTarget<Message>,
context: DispatcherContext,
pending: PendingWindow<P>,
pending: PendingWindow<Wry>,
) -> Result<WebviewWrapper> {
#[allow(unused_mut)]
let PendingWindow {
@ -1983,7 +1980,7 @@ fn create_webview<P: Params<Runtime = Wry>>(
.map_err(|e| Error::CreateWebview(Box::new(e)))?;
Ok(WebviewWrapper {
label: format!("{}", label),
label,
inner: webview,
#[cfg(feature = "menu")]
menu_items,
@ -1993,10 +1990,10 @@ fn create_webview<P: Params<Runtime = Wry>>(
}
/// Create a wry rpc handler from a tauri rpc handler.
fn create_rpc_handler<P: Params<Runtime = Wry>>(
fn create_rpc_handler(
context: DispatcherContext,
label: P::Label,
handler: WebviewRpcHandler<P>,
label: String,
handler: WebviewRpcHandler<Wry>,
) -> Box<dyn Fn(&Window, WryRpcRequest) -> Option<RpcResponse> + 'static> {
Box::new(move |window, request| {
handler(
@ -2014,10 +2011,10 @@ fn create_rpc_handler<P: Params<Runtime = Wry>>(
}
/// Create a wry file drop handler from a tauri file drop handler.
fn create_file_drop_handler<P: Params<Runtime = Wry>>(
fn create_file_drop_handler(
context: DispatcherContext,
label: P::Label,
handler: FileDropHandler<P>,
label: String,
handler: FileDropHandler<Wry>,
) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
Box::new(move |window, event| {
handler(

View File

@ -8,7 +8,7 @@ pub use tauri_runtime::{
SystemTrayMenuEntry, SystemTrayMenuItem, TrayHandle,
},
window::MenuEvent,
Icon, MenuId, SystemTrayEvent,
Icon, SystemTrayEvent,
};
pub use wry::application::{
event::TrayEvent,
@ -31,6 +31,9 @@ pub use wry::application::platform::macos::{
#[cfg(feature = "system-tray")]
use crate::{Error, Message, Result, TrayMessage};
#[cfg(feature = "menu")]
use tauri_runtime::menu::MenuHash;
use uuid::Uuid;
use std::{
@ -145,12 +148,12 @@ impl From<NativeImage> for NativeImageWrapper {
pub struct MenuItemAttributesWrapper<'a>(pub WryMenuItemAttributes<'a>);
impl<'a, I: MenuId> From<&'a CustomMenuItem<I>> for MenuItemAttributesWrapper<'a> {
fn from(item: &'a CustomMenuItem<I>) -> Self {
impl<'a> From<&'a CustomMenuItem> for MenuItemAttributesWrapper<'a> {
fn from(item: &'a CustomMenuItem) -> Self {
let mut attributes = WryMenuItemAttributes::new(&item.title)
.with_enabled(item.enabled)
.with_selected(item.selected)
.with_id(WryMenuId(item.id_value()));
.with_id(WryMenuId(item.id));
if let Some(accelerator) = item.keyboard_accelerator.as_ref() {
attributes = attributes.with_accelerators(&accelerator.parse().expect("invalid accelerator"));
}
@ -195,11 +198,11 @@ impl From<SystemTrayMenuItem> for MenuItemWrapper {
}
#[cfg(feature = "menu")]
pub fn convert_menu_id<I: MenuId>(mut new_menu: Menu<u16>, menu: Menu<I>) -> Menu<u16> {
pub fn convert_menu_id(mut new_menu: Menu, menu: Menu) -> Menu {
for item in menu.items {
match item {
MenuEntry::CustomItem(c) => {
let mut item = CustomMenuItem::new(c.id_value(), c.title);
let mut item = CustomMenuItem::new(c.id_str, c.title);
#[cfg(target_os = "macos")]
if let Some(native_image) = c.native_image {
item = item.native_image(native_image);
@ -229,8 +232,8 @@ pub fn convert_menu_id<I: MenuId>(mut new_menu: Menu<u16>, menu: Menu<I>) -> Men
#[cfg(feature = "menu")]
pub fn to_wry_menu(
custom_menu_items: &mut HashMap<u16, WryCustomMenuItem>,
menu: Menu<u16>,
custom_menu_items: &mut HashMap<MenuHash, WryCustomMenuItem>,
menu: Menu,
) -> MenuBar {
let mut wry_menu = MenuBar::new();
for item in menu.items {
@ -262,9 +265,9 @@ pub fn to_wry_menu(
}
#[cfg(feature = "system-tray")]
pub fn to_wry_context_menu<I: MenuId>(
custom_menu_items: &mut HashMap<u16, WryCustomMenuItem>,
menu: SystemTrayMenu<I>,
pub fn to_wry_context_menu(
custom_menu_items: &mut HashMap<MenuHash, WryCustomMenuItem>,
menu: SystemTrayMenu,
) -> WryContextMenu {
let mut tray_menu = WryContextMenu::new();
for item in menu.items {
@ -272,12 +275,11 @@ pub fn to_wry_context_menu<I: MenuId>(
SystemTrayMenuEntry::CustomItem(c) => {
#[allow(unused_mut)]
let mut item = tray_menu.add_item(MenuItemAttributesWrapper::from(&c).0);
let id = c.id_value();
#[cfg(target_os = "macos")]
if let Some(native_image) = c.native_image {
item.set_native_image(NativeImageWrapper::from(native_image).0);
}
custom_menu_items.insert(id, item);
custom_menu_items.insert(c.id, item);
}
SystemTrayMenuEntry::NativeItem(i) => {
tray_menu.add_native_item(MenuItemWrapper::from(i).0);

View File

@ -6,11 +6,10 @@
#![cfg_attr(doc_cfg, feature(doc_cfg))]
use std::{fmt::Debug, hash::Hash, path::PathBuf, sync::mpsc::Sender};
use serde::{Deserialize, Serialize};
use tauri_utils::assets::Assets;
use serde::Deserialize;
use std::{fmt::Debug, path::PathBuf, sync::mpsc::Sender};
use uuid::Uuid;
#[cfg(windows)]
use winapi::shared::windef::HWND;
@ -20,32 +19,25 @@ use winapi::shared::windef::HWND;
pub mod menu;
/// Types useful for interacting with a user's monitors.
pub mod monitor;
pub mod tag;
pub mod webview;
pub mod window;
use monitor::Monitor;
use tag::Tag;
use webview::WindowBuilder;
use window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow, WindowEvent,
};
/// A type that can be derived into a menu id.
pub trait MenuId: Serialize + Hash + Eq + Debug + Clone + Send + Sync + 'static {}
impl<T> MenuId for T where T: Serialize + Hash + Eq + Debug + Clone + Send + Sync + 'static {}
#[cfg(feature = "system-tray")]
#[non_exhaustive]
pub struct SystemTray<I: MenuId> {
pub struct SystemTray {
pub icon: Option<Icon>,
pub menu: Option<menu::SystemTrayMenu<I>>,
pub menu: Option<menu::SystemTrayMenu>,
}
#[cfg(feature = "system-tray")]
impl<I: MenuId> Default for SystemTray<I> {
impl Default for SystemTray {
fn default() -> Self {
Self {
icon: None,
@ -55,13 +47,13 @@ impl<I: MenuId> Default for SystemTray<I> {
}
#[cfg(feature = "system-tray")]
impl<I: MenuId> SystemTray<I> {
impl SystemTray {
/// Creates a new system tray that only renders an icon.
pub fn new() -> Self {
Default::default()
}
pub fn menu(&self) -> Option<&menu::SystemTrayMenu<I>> {
pub fn menu(&self) -> Option<&menu::SystemTrayMenu> {
self.menu.as_ref()
}
@ -72,7 +64,7 @@ impl<I: MenuId> SystemTray<I> {
}
/// Sets the menu to show when the system tray is right clicked.
pub fn with_menu(mut self, menu: menu::SystemTrayMenu<I>) -> Self {
pub fn with_menu(mut self, menu: menu::SystemTrayMenu) -> Self {
self.menu.replace(menu);
self
}
@ -126,32 +118,6 @@ pub enum Error {
/// Result type.
pub type Result<T> = std::result::Result<T, Error>;
#[doc(hidden)]
pub mod private {
pub trait ParamsBase {}
}
/// Types associated with the running Tauri application.
pub trait Params: private::ParamsBase + 'static {
/// The event type used to create and listen to events.
type Event: Tag;
/// The type used to determine the name of windows.
type Label: Tag;
/// The type used to determine window menu ids.
type MenuId: MenuId;
/// The type used to determine system tray menu ids.
type SystemTrayMenuId: MenuId;
/// Assets that Tauri should serve from itself.
type Assets: Assets;
/// The underlying webview runtime used by the Tauri application.
type Runtime: Runtime;
}
/// A icon definition.
#[derive(Debug, Clone)]
#[non_exhaustive]
@ -231,10 +197,10 @@ pub struct RunIteration {
pub trait RuntimeHandle: Send + Sized + Clone + 'static {
type Runtime: Runtime<Handle = Self>;
/// Create a new webview window.
fn create_window<P: Params<Runtime = Self::Runtime>>(
fn create_window(
&self,
pending: PendingWindow<P>,
) -> crate::Result<DetachedWindow<P>>;
pending: PendingWindow<Self::Runtime>,
) -> crate::Result<DetachedWindow<Self::Runtime>>;
#[cfg(all(windows, feature = "system-tray"))]
#[cfg_attr(doc_cfg, doc(cfg(all(windows, feature = "system-tray"))))]
@ -326,15 +292,12 @@ pub trait Runtime: Sized + 'static {
fn clipboard_manager(&self) -> Self::ClipboardManager;
/// Create a new webview window.
fn create_window<P: Params<Runtime = Self>>(
&self,
pending: PendingWindow<P>,
) -> crate::Result<DetachedWindow<P>>;
fn create_window(&self, pending: PendingWindow<Self>) -> crate::Result<DetachedWindow<Self>>;
/// Adds the icon to the system tray with the specified menu items.
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
fn system_tray<I: MenuId>(&self, system_tray: SystemTray<I>) -> crate::Result<Self::TrayHandler>;
fn system_tray(&self, system_tray: SystemTray) -> crate::Result<Self::TrayHandler>;
/// Registers a system tray event handler.
#[cfg(feature = "system-tray")]
@ -449,10 +412,10 @@ pub trait Dispatch: Clone + Send + Sized + 'static {
fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> crate::Result<()>;
/// Create a new webview window.
fn create_window<P: Params<Runtime = Self::Runtime>>(
fn create_window(
&mut self,
pending: PendingWindow<P>,
) -> crate::Result<DetachedWindow<P>>;
pending: PendingWindow<Self::Runtime>,
) -> crate::Result<DetachedWindow<Self::Runtime>>;
/// Updates the window resizable flag.
fn set_resizable(&self, resizable: bool) -> crate::Result<()>;

View File

@ -2,9 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{collections::hash_map::DefaultHasher, hash::Hasher};
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
use super::MenuId;
pub type MenuHash = u16;
pub type MenuId = String;
pub type MenuIdRef<'a> = &'a str;
/// Named images defined by the system.
#[cfg(target_os = "macos")]
@ -148,21 +153,21 @@ pub trait TrayHandle {
/// A window menu.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Menu<I: MenuId> {
pub items: Vec<MenuEntry<I>>,
pub struct Menu {
pub items: Vec<MenuEntry>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Submenu<I: MenuId> {
pub struct Submenu {
pub title: String,
pub enabled: bool,
pub inner: Menu<I>,
pub inner: Menu,
}
impl<I: MenuId> Submenu<I> {
impl Submenu {
/// Creates a new submenu with the given title and menu items.
pub fn new<S: Into<String>>(title: S, menu: Menu<I>) -> Self {
pub fn new<S: Into<String>>(title: S, menu: Menu) -> Self {
Self {
title: title.into(),
enabled: true,
@ -171,20 +176,20 @@ impl<I: MenuId> Submenu<I> {
}
}
impl<I: MenuId> Default for Menu<I> {
impl Default for Menu {
fn default() -> Self {
Self { items: Vec::new() }
}
}
impl<I: MenuId> Menu<I> {
impl Menu {
/// Creates a new window menu.
pub fn new() -> Self {
Default::default()
}
/// Adds the custom menu item to the menu.
pub fn add_item(mut self, item: CustomMenuItem<I>) -> Self {
pub fn add_item(mut self, item: CustomMenuItem) -> Self {
self.items.push(MenuEntry::CustomItem(item));
self
}
@ -196,7 +201,7 @@ impl<I: MenuId> Menu<I> {
}
/// Adds an entry with submenu.
pub fn add_submenu(mut self, submenu: Submenu<I>) -> Self {
pub fn add_submenu(mut self, submenu: Submenu) -> Self {
self.items.push(MenuEntry::Submenu(submenu));
self
}
@ -205,8 +210,9 @@ impl<I: MenuId> Menu<I> {
/// A custom menu item.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct CustomMenuItem<I: MenuId> {
pub id: I,
pub struct CustomMenuItem {
pub id: MenuHash,
pub id_str: MenuId,
pub title: String,
pub keyboard_accelerator: Option<String>,
pub enabled: bool,
@ -215,11 +221,13 @@ pub struct CustomMenuItem<I: MenuId> {
pub native_image: Option<NativeImage>,
}
impl<I: MenuId> CustomMenuItem<I> {
impl CustomMenuItem {
/// Create new custom menu item.
pub fn new<T: Into<String>>(id: I, title: T) -> Self {
pub fn new<I: Into<String>, T: Into<String>>(id: I, title: T) -> Self {
let id_str = id.into();
Self {
id,
id: Self::hash(&id_str),
id_str,
title: title.into(),
keyboard_accelerator: None,
enabled: true,
@ -255,22 +263,21 @@ impl<I: MenuId> CustomMenuItem<I> {
self
}
#[doc(hidden)]
pub fn id_value(&self) -> u16 {
let mut s = DefaultHasher::new();
self.id.hash(&mut s);
s.finish() as u16
fn hash(id: &str) -> MenuHash {
let mut hasher = DefaultHasher::new();
id.hash(&mut hasher);
hasher.finish() as MenuHash
}
}
/// A system tray menu.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct SystemTrayMenu<I: MenuId> {
pub items: Vec<SystemTrayMenuEntry<I>>,
pub struct SystemTrayMenu {
pub items: Vec<SystemTrayMenuEntry>,
}
impl<I: MenuId> Default for SystemTrayMenu<I> {
impl Default for SystemTrayMenu {
fn default() -> Self {
Self { items: Vec::new() }
}
@ -278,15 +285,15 @@ impl<I: MenuId> Default for SystemTrayMenu<I> {
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct SystemTraySubmenu<I: MenuId> {
pub struct SystemTraySubmenu {
pub title: String,
pub enabled: bool,
pub inner: SystemTrayMenu<I>,
pub inner: SystemTrayMenu,
}
impl<I: MenuId> SystemTraySubmenu<I> {
impl SystemTraySubmenu {
/// Creates a new submenu with the given title and menu items.
pub fn new<S: Into<String>>(title: S, menu: SystemTrayMenu<I>) -> Self {
pub fn new<S: Into<String>>(title: S, menu: SystemTrayMenu) -> Self {
Self {
title: title.into(),
enabled: true,
@ -295,14 +302,14 @@ impl<I: MenuId> SystemTraySubmenu<I> {
}
}
impl<I: MenuId> SystemTrayMenu<I> {
impl SystemTrayMenu {
/// Creates a new system tray menu.
pub fn new() -> Self {
Default::default()
}
/// Adds the custom menu item to the system tray menu.
pub fn add_item(mut self, item: CustomMenuItem<I>) -> Self {
pub fn add_item(mut self, item: CustomMenuItem) -> Self {
self.items.push(SystemTrayMenuEntry::CustomItem(item));
self
}
@ -314,7 +321,7 @@ impl<I: MenuId> SystemTrayMenu<I> {
}
/// Adds an entry with submenu.
pub fn add_submenu(mut self, submenu: SystemTraySubmenu<I>) -> Self {
pub fn add_submenu(mut self, submenu: SystemTraySubmenu) -> Self {
self.items.push(SystemTrayMenuEntry::Submenu(submenu));
self
}
@ -322,13 +329,13 @@ impl<I: MenuId> SystemTrayMenu<I> {
/// An entry on the system tray menu.
#[derive(Debug, Clone)]
pub enum SystemTrayMenuEntry<I: MenuId> {
pub enum SystemTrayMenuEntry {
/// A custom item.
CustomItem(CustomMenuItem<I>),
CustomItem(CustomMenuItem),
/// A native item.
NativeItem(SystemTrayMenuItem),
/// An entry with submenu.
Submenu(SystemTraySubmenu<I>),
Submenu(SystemTraySubmenu),
}
/// System tray menu item.
@ -341,13 +348,13 @@ pub enum SystemTrayMenuItem {
/// An entry on the system tray menu.
#[derive(Debug, Clone)]
pub enum MenuEntry<I: MenuId> {
pub enum MenuEntry {
/// A custom item.
CustomItem(CustomMenuItem<I>),
CustomItem(CustomMenuItem),
/// A native item.
NativeItem(MenuItem),
/// An entry with submenu.
Submenu(Submenu<I>),
Submenu(Submenu),
}
/// A menu item, bound to a pre-defined action or `Custom` emit an event. Note that status bar only

View File

@ -1,131 +0,0 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! Working with "string-able" types.
use std::{
fmt::{Debug, Display},
hash::Hash,
str::FromStr,
};
/// Represents a "string-able" type.
///
/// The type is required to be able to be represented as a string [`Display`], along with knowing
/// how to be parsed from the string representation [`FromStr`]. To make sure things stay easy to
/// debug, both the [`Tag`] and the [`FromStr::Err`] must implement [`Debug`].
///
/// [`Clone`], [`Hash`], and [`Eq`] are needed so that it can represent un-hashable types.
///
/// [`Send`] and [`Sync`] and `'static` are current requirements due to how it is sometimes sent
/// across thread boundaries, although some of those constraints may relax in the future.
///
/// The simplest type that fits all these requirements is a [`String`](std::string::String).
///
/// # Handling Errors
///
/// Because we leave it up to the type to implement [`FromStr`], if an error is returned during
/// parsing then Tauri will [`std::panic!`] with the string it failed to parse.
///
/// To avoid Tauri panicking during the application runtime, have your type be able to handle
/// unknown events and never return an error in [`FromStr`]. Then it will be up to your own code
/// to handle the unknown event.
///
/// # Example
///
/// ```
/// use std::fmt;
/// use std::str::FromStr;
///
/// #[derive(Debug, Clone, Hash, Eq, PartialEq)]
/// enum Event {
/// Foo,
/// Bar,
/// Unknown(String),
/// }
///
/// impl fmt::Display for Event {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// f.write_str(match self {
/// Self::Foo => "foo",
/// Self::Bar => "bar",
/// Self::Unknown(s) => &s
/// })
/// }
/// }
///
/// impl FromStr for Event {
/// type Err = std::convert::Infallible;
///
/// fn from_str(s: &str) -> Result<Self, Self::Err> {
/// Ok(match s {
/// "foo" => Self::Foo,
/// "bar" => Self::Bar,
/// other => Self::Unknown(other.to_string())
/// })
/// }
/// }
///
/// // safe to unwrap because we know it's infallible due to our FromStr implementation.
/// let event: Event = "tauri://file-drop".parse().unwrap();
///
/// // show that this event type can be represented as a Tag, a requirement for using it in Tauri.
/// fn is_file_drop(tag: impl tauri_runtime::tag::Tag) {
/// assert_eq!("tauri://file-drop", tag.to_string());
/// }
///
/// is_file_drop(event);
/// ```
pub trait Tag: Hash + Eq + FromStr + Display + Debug + Clone + Send + Sync + 'static {}
/// Automatically implement [`Tag`] for all types that fit the requirements.
impl<T, E: Debug> Tag for T where
T: Hash + Eq + FromStr<Err = E> + Display + Debug + Clone + Send + Sync + 'static
{
}
/// A reference to a [`Tag`].
///
/// * [`Display`] so that we can still convert this tag to a JavaScript string.
/// * [`ToOwned`] to make sure we can clone it into the owned tag in specific cases.
/// * [`PartialEq`] so that we can compare refs to the owned tags easily.
/// * [`Hash`] + [`Eq`] because we want to be able to use a ref as a key to internal hashmaps.
pub trait TagRef<T: Tag>: Display + ToOwned<Owned = T> + PartialEq<T> + Eq + Hash
where
T: std::borrow::Borrow<Self>,
{
}
/// Automatically implement [`TagRef`] for all types that fit the requirements.
impl<T: Tag, R> TagRef<T> for R
where
T: std::borrow::Borrow<R>,
R: Display + ToOwned<Owned = T> + PartialEq<T> + Eq + Hash + ?Sized,
{
}
/// Private helper to turn [`Tag`] related things into JavaScript, safely.
///
/// The main concern is string escaping, so we rely on [`serde_json`] to handle all serialization
/// of the [`Tag`] as a string. We do this instead of requiring [`serde::Serialize`] on [`Tag`]
/// because it really represents a string, not any serializable data structure.
///
/// We don't want downstream users to implement this trait so that [`Tag`]s cannot be turned into
/// invalid JavaScript - regardless of their content.
pub trait ToJsString {
fn to_js_string(&self) -> crate::Result<String>;
}
impl<D: Display> ToJsString for D {
/// Turn any [`Tag`] into the JavaScript representation of a string.
fn to_js_string(&self) -> crate::Result<String> {
Ok(serde_json::to_string(&self.to_string())?)
}
}
/// Turn any collection of [`Tag`]s into a JavaScript array of strings.
pub fn tags_to_javascript_array(tags: &[impl Tag]) -> crate::Result<String> {
let tags: Vec<String> = tags.iter().map(ToString::to_string).collect();
Ok(serde_json::to_string(&tags)?)
}

View File

@ -7,7 +7,7 @@
use crate::{window::DetachedWindow, Icon};
#[cfg(feature = "menu")]
use crate::{menu::Menu, MenuId};
use crate::menu::Menu;
use serde::Deserialize;
use serde_json::Value as JsonValue;
@ -109,7 +109,7 @@ pub trait WindowBuilder: WindowBuilderBase {
/// Sets the menu for the window.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
fn menu<I: MenuId>(self, menu: Menu<I>) -> Self;
fn menu(self, menu: Menu) -> Self;
/// Show window in the center of the screen.
fn center(self) -> Self;
@ -216,11 +216,11 @@ pub enum FileDropEvent {
}
/// Rpc handler.
pub type WebviewRpcHandler<P> = Box<dyn Fn(DetachedWindow<P>, RpcRequest) + Send>;
pub type WebviewRpcHandler<R> = Box<dyn Fn(DetachedWindow<R>, RpcRequest) + Send>;
/// File drop handler callback
/// Return `true` in the callback to block the OS' default behavior of handling a file drop.
pub type FileDropHandler<P> = Box<dyn Fn(FileDropEvent, DetachedWindow<P>) -> bool + Send>;
pub type FileDropHandler<R> = Box<dyn Fn(FileDropEvent, DetachedWindow<R>) -> bool + Send>;
#[derive(Deserialize)]
pub struct InvokePayload {

View File

@ -6,7 +6,7 @@
use crate::{
webview::{FileDropHandler, WebviewAttributes, WebviewRpcHandler},
Dispatch, Params, Runtime, WindowBuilder,
Dispatch, Runtime, WindowBuilder,
};
use serde::Serialize;
use tauri_utils::config::WindowConfig;
@ -55,37 +55,37 @@ pub struct MenuEvent {
}
/// A webview window that has yet to be built.
pub struct PendingWindow<P: Params> {
pub struct PendingWindow<R: Runtime> {
/// The label that the window will be named.
pub label: P::Label,
pub label: String,
/// The [`WindowBuilder`] that the window will be created with.
pub window_builder: <<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
pub window_builder: <R::Dispatcher as Dispatch>::WindowBuilder,
/// The [`WebviewAttributes`] that the webview will be created with.
pub webview_attributes: WebviewAttributes,
/// How to handle RPC calls on the webview window.
pub rpc_handler: Option<WebviewRpcHandler<P>>,
pub rpc_handler: Option<WebviewRpcHandler<R>>,
/// How to handle a file dropping onto the webview window.
pub file_drop_handler: Option<FileDropHandler<P>>,
pub file_drop_handler: Option<FileDropHandler<R>>,
/// The resolved URL to load on the webview.
pub url: String,
}
impl<P: Params> PendingWindow<P> {
impl<R: Runtime> PendingWindow<R> {
/// Create a new [`PendingWindow`] with a label and starting url.
pub fn new(
window_builder: <<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
window_builder: <R::Dispatcher as Dispatch>::WindowBuilder,
webview_attributes: WebviewAttributes,
label: P::Label,
label: impl Into<String>,
) -> Self {
Self {
window_builder,
webview_attributes,
label,
label: label.into(),
rpc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(),
@ -96,15 +96,12 @@ impl<P: Params> PendingWindow<P> {
pub fn with_config(
window_config: WindowConfig,
webview_attributes: WebviewAttributes,
label: P::Label,
label: impl Into<String>,
) -> Self {
Self {
window_builder:
<<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder>::with_config(
window_config,
),
window_builder: <<R::Dispatcher as Dispatch>::WindowBuilder>::with_config(window_config),
webview_attributes,
label,
label: label.into(),
rpc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(),
@ -113,15 +110,15 @@ impl<P: Params> PendingWindow<P> {
}
/// A webview window that is not yet managed by Tauri.
pub struct DetachedWindow<P: Params> {
pub struct DetachedWindow<R: Runtime> {
/// Name of the window
pub label: P::Label,
pub label: String,
/// The [`Dispatch`](crate::Dispatch) associated with the window.
pub dispatcher: <P::Runtime as Runtime>::Dispatcher,
pub dispatcher: R::Dispatcher,
}
impl<P: Params> Clone for DetachedWindow<P> {
impl<R: Runtime> Clone for DetachedWindow<R> {
fn clone(&self) -> Self {
Self {
label: self.label.clone(),
@ -130,15 +127,15 @@ impl<P: Params> Clone for DetachedWindow<P> {
}
}
impl<P: Params> Hash for DetachedWindow<P> {
impl<R: Runtime> Hash for DetachedWindow<R> {
/// Only use the [`DetachedWindow`]'s label to represent its hash.
fn hash<H: Hasher>(&self, state: &mut H) {
self.label.hash(state)
}
}
impl<P: Params> Eq for DetachedWindow<P> {}
impl<P: Params> PartialEq for DetachedWindow<P> {
impl<R: Runtime> Eq for DetachedWindow<R> {}
impl<R: Runtime> PartialEq for DetachedWindow<R> {
/// Only use the [`DetachedWindow`]'s label to compare equality.
fn eq(&self, other: &Self) -> bool {
self.label.eq(&other.label)

View File

@ -76,7 +76,7 @@ impl<P: AsRef<Path>> From<P> for AssetKey {
/// Represents a container of file assets that are retrievable during runtime.
pub trait Assets: Send + Sync + 'static {
/// Get the content of the passed [`AssetKey`].
fn get<Key: Into<AssetKey>>(&self, key: Key) -> Option<Cow<'_, [u8]>>;
fn get(&self, key: &AssetKey) -> Option<Cow<'_, [u8]>>;
}
/// [`Assets`] implementation that only contains compile-time compressed and embedded assets.
@ -92,10 +92,10 @@ impl EmbeddedAssets {
}
impl Assets for EmbeddedAssets {
fn get<Key: Into<AssetKey>>(&self, key: Key) -> Option<Cow<'_, [u8]>> {
fn get(&self, key: &AssetKey) -> Option<Cow<'_, [u8]>> {
self
.0
.get(key.into().as_ref())
.get(key.as_ref())
.copied()
.map(zstd::decode_all)
.and_then(Result::ok)

View File

@ -11,18 +11,18 @@ use crate::{
api::config::{Config, WindowUrl},
command::{CommandArg, CommandItem},
hooks::{InvokeHandler, OnPageLoad, PageLoadPayload, SetupHook},
manager::{Args, WindowManager},
manager::WindowManager,
plugin::{Plugin, PluginStore},
runtime::{
tag::Tag,
webview::{CustomProtocol, WebviewAttributes, WindowBuilder},
window::{PendingWindow, WindowEvent},
Dispatch, MenuId, Params, RunEvent, Runtime,
Dispatch, RunEvent, Runtime,
},
sealed::{ManagerBase, RuntimeOrDispatch},
Context, Invoke, InvokeError, Manager, StateManager, Window,
};
use tauri_macros::default_runtime;
use tauri_utils::PackageInfo;
use std::{
@ -32,7 +32,7 @@ use std::{
};
#[cfg(feature = "menu")]
use crate::runtime::menu::Menu;
use crate::runtime::menu::{Menu, MenuId, MenuIdRef};
#[cfg(all(windows, feature = "system-tray"))]
use crate::runtime::RuntimeHandle;
@ -43,11 +43,10 @@ use crate::runtime::{Icon, SystemTrayEvent as RuntimeSystemTrayEvent};
use crate::updater;
#[cfg(feature = "menu")]
pub(crate) type GlobalMenuEventListener<P> = Box<dyn Fn(WindowMenuEvent<P>) + Send + Sync>;
pub(crate) type GlobalWindowEventListener<P> = Box<dyn Fn(GlobalWindowEvent<P>) + Send + Sync>;
pub(crate) type GlobalMenuEventListener<R> = Box<dyn Fn(WindowMenuEvent<R>) + Send + Sync>;
pub(crate) type GlobalWindowEventListener<R> = Box<dyn Fn(GlobalWindowEvent<R>) + Send + Sync>;
#[cfg(feature = "system-tray")]
type SystemTrayEventListener<P> =
Box<dyn Fn(&AppHandle<P>, tray::SystemTrayEvent<<P as Params>::SystemTrayMenuId>) + Send + Sync>;
type SystemTrayEventListener<R> = Box<dyn Fn(&AppHandle<R>, tray::SystemTrayEvent) + Send + Sync>;
/// Api exposed on the `CloseRequested` event.
pub struct CloseRequestApi(Sender<bool>);
@ -61,60 +60,58 @@ impl CloseRequestApi {
/// An application event, triggered from the event loop.
#[non_exhaustive]
pub enum Event<P: Params> {
pub enum Event {
/// Event loop is exiting.
Exit,
/// Window close was requested by the user.
#[non_exhaustive]
CloseRequested {
/// The window label.
label: P::Label,
label: String,
/// Event API.
api: CloseRequestApi,
},
/// Window closed.
WindowClosed(P::Label),
WindowClosed(String),
}
crate::manager::default_args! {
/// A menu event that was triggered on a window.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
pub struct WindowMenuEvent<P: Params> {
pub(crate) menu_item_id: P::MenuId,
pub(crate) window: Window<P>,
}
/// A menu event that was triggered on a window.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
#[default_runtime(crate::Wry, wry)]
pub struct WindowMenuEvent<R: Runtime> {
pub(crate) menu_item_id: MenuId,
pub(crate) window: Window<R>,
}
#[cfg(feature = "menu")]
impl<P: Params> WindowMenuEvent<P> {
impl<R: Runtime> WindowMenuEvent<R> {
/// The menu item id.
pub fn menu_item_id(&self) -> &P::MenuId {
pub fn menu_item_id(&self) -> MenuIdRef<'_> {
&self.menu_item_id
}
/// The window that the menu belongs to.
pub fn window(&self) -> &Window<P> {
pub fn window(&self) -> &Window<R> {
&self.window
}
}
crate::manager::default_args! {
/// A window event that was triggered on the specified window.
pub struct GlobalWindowEvent<P: Params> {
pub(crate) event: WindowEvent,
pub(crate) window: Window<P>,
}
/// A window event that was triggered on the specified window.
#[default_runtime(crate::Wry, wry)]
pub struct GlobalWindowEvent<R: Runtime> {
pub(crate) event: WindowEvent,
pub(crate) window: Window<R>,
}
impl<P: Params> GlobalWindowEvent<P> {
/// The eventpayload.
impl<R: Runtime> GlobalWindowEvent<R> {
/// The event payload.
pub fn event(&self) -> &WindowEvent {
&self.event
}
/// The window that the menu belongs to.
pub fn window(&self) -> &Window<P> {
pub fn window(&self) -> &Window<R> {
&self.window
}
}
@ -138,21 +135,20 @@ impl PathResolver {
}
}
crate::manager::default_args! {
/// A handle to the currently running application.
///
/// This type implements [`Manager`] which allows for manipulation of global application items.
pub struct AppHandle<P: Params> {
runtime_handle: <P::Runtime as Runtime>::Handle,
manager: WindowManager<P>,
global_shortcut_manager: <P::Runtime as Runtime>::GlobalShortcutManager,
clipboard_manager: <P::Runtime as Runtime>::ClipboardManager,
#[cfg(feature = "system-tray")]
tray_handle: Option<tray::SystemTrayHandle<P>>,
}
/// A handle to the currently running application.
///
/// This type implements [`Manager`] which allows for manipulation of global application items.
#[default_runtime(crate::Wry, wry)]
pub struct AppHandle<R: Runtime> {
runtime_handle: R::Handle,
manager: WindowManager<R>,
global_shortcut_manager: R::GlobalShortcutManager,
clipboard_manager: R::ClipboardManager,
#[cfg(feature = "system-tray")]
tray_handle: Option<tray::SystemTrayHandle<R>>,
}
impl<P: Params> Clone for AppHandle<P> {
impl<R: Runtime> Clone for AppHandle<R> {
fn clone(&self) -> Self {
Self {
runtime_handle: self.runtime_handle.clone(),
@ -165,14 +161,14 @@ impl<P: Params> Clone for AppHandle<P> {
}
}
impl<'de, P: Params> CommandArg<'de, P> for AppHandle<P> {
impl<'de, R: Runtime> CommandArg<'de, R> for AppHandle<R> {
/// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`AppHandle`]. This will never fail.
fn from_command(command: CommandItem<'de, P>) -> Result<Self, InvokeError> {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
Ok(command.message.window().app_handle)
}
}
impl<P: Params> AppHandle<P> {
impl<R: Runtime> AppHandle<R> {
/// Removes the system tray.
#[cfg(all(windows, feature = "system-tray"))]
#[cfg_attr(doc_cfg, doc(cfg(all(windows, feature = "system-tray"))))]
@ -181,67 +177,66 @@ impl<P: Params> AppHandle<P> {
}
}
impl<P: Params> Manager<P> for AppHandle<P> {}
impl<P: Params> ManagerBase<P> for AppHandle<P> {
fn manager(&self) -> &WindowManager<P> {
impl<R: Runtime> Manager<R> for AppHandle<R> {}
impl<R: Runtime> ManagerBase<R> for AppHandle<R> {
fn manager(&self) -> &WindowManager<R> {
&self.manager
}
fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
RuntimeOrDispatch::RuntimeHandle(self.runtime_handle.clone())
}
fn app_handle(&self) -> AppHandle<P> {
fn app_handle(&self) -> AppHandle<R> {
self.clone()
}
}
crate::manager::default_args! {
/// The instance of the currently running application.
///
/// This type implements [`Manager`] which allows for manipulation of global application items.
pub struct App<P: Params> {
runtime: Option<P::Runtime>,
manager: WindowManager<P>,
global_shortcut_manager: <P::Runtime as Runtime>::GlobalShortcutManager,
clipboard_manager: <P::Runtime as Runtime>::ClipboardManager,
#[cfg(feature = "system-tray")]
tray_handle: Option<tray::SystemTrayHandle<P>>,
handle: AppHandle<P>,
}
/// The instance of the currently running application.
///
/// This type implements [`Manager`] which allows for manipulation of global application items.
#[default_runtime(crate::Wry, wry)]
pub struct App<R: Runtime> {
runtime: Option<R>,
manager: WindowManager<R>,
global_shortcut_manager: R::GlobalShortcutManager,
clipboard_manager: R::ClipboardManager,
#[cfg(feature = "system-tray")]
tray_handle: Option<tray::SystemTrayHandle<R>>,
handle: AppHandle<R>,
}
impl<P: Params> Manager<P> for App<P> {}
impl<P: Params> ManagerBase<P> for App<P> {
fn manager(&self) -> &WindowManager<P> {
impl<R: Runtime> Manager<R> for App<R> {}
impl<R: Runtime> ManagerBase<R> for App<R> {
fn manager(&self) -> &WindowManager<R> {
&self.manager
}
fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
RuntimeOrDispatch::Runtime(self.runtime.as_ref().unwrap())
}
fn app_handle(&self) -> AppHandle<P> {
fn app_handle(&self) -> AppHandle<R> {
self.handle()
}
}
macro_rules! shared_app_impl {
($app: ty) => {
impl<P: Params> $app {
impl<R: Runtime> $app {
/// Creates a new webview window.
pub fn create_window<F>(&self, label: P::Label, url: WindowUrl, setup: F) -> crate::Result<()>
pub fn create_window<F>(&self, label: String, url: WindowUrl, setup: F) -> crate::Result<()>
where
F: FnOnce(
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
<R::Dispatcher as Dispatch>::WindowBuilder,
WebviewAttributes,
) -> (
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
<R::Dispatcher as Dispatch>::WindowBuilder,
WebviewAttributes,
),
{
let (window_builder, webview_attributes) = setup(
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder::new(),
<R::Dispatcher as Dispatch>::WindowBuilder::new(),
WebviewAttributes::new(url),
);
self.create_new_window(PendingWindow::new(
@ -255,7 +250,7 @@ macro_rules! shared_app_impl {
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
/// Gets a handle handle to the system tray.
pub fn tray_handle(&self) -> tray::SystemTrayHandle<P> {
pub fn tray_handle(&self) -> tray::SystemTrayHandle<R> {
self
.tray_handle
.clone()
@ -271,12 +266,12 @@ macro_rules! shared_app_impl {
}
/// Gets a copy of the global shortcut manager instance.
pub fn global_shortcut_manager(&self) -> <P::Runtime as Runtime>::GlobalShortcutManager {
pub fn global_shortcut_manager(&self) -> R::GlobalShortcutManager {
self.global_shortcut_manager.clone()
}
/// Gets a copy of the clipboard manager instance.
pub fn clipboard_manager(&self) -> <P::Runtime as Runtime>::ClipboardManager {
pub fn clipboard_manager(&self) -> R::ClipboardManager {
self.clipboard_manager.clone()
}
@ -293,17 +288,17 @@ macro_rules! shared_app_impl {
};
}
shared_app_impl!(App<P>);
shared_app_impl!(AppHandle<P>);
shared_app_impl!(App<R>);
shared_app_impl!(AppHandle<R>);
impl<P: Params> App<P> {
impl<R: Runtime> App<R> {
/// Gets a handle to the application instance.
pub fn handle(&self) -> AppHandle<P> {
pub fn handle(&self) -> AppHandle<R> {
self.handle.clone()
}
/// Runs the application.
pub fn run<F: Fn(&AppHandle<P>, Event<P>) + 'static>(mut self, callback: F) {
pub fn run<F: Fn(&AppHandle<R>, Event) + 'static>(mut self, callback: F) {
let app_handle = self.handle();
let manager = self.manager.clone();
self.runtime.take().unwrap().run(move |event| match event {
@ -369,9 +364,9 @@ impl<P: Params> App<P> {
}
#[cfg(feature = "updater")]
impl<P: Params> App<P> {
impl<R: Runtime> App<R> {
/// Runs the updater hook with built-in dialog.
fn run_updater_dialog(&self, window: Window<P>) {
fn run_updater_dialog(&self, window: Window<R>) {
let updater_config = self.manager.config().tauri.updater.clone();
let package_info = self.manager.package_info().clone();
crate::async_runtime::spawn(async move {
@ -380,12 +375,12 @@ impl<P: Params> App<P> {
}
/// Listen updater events when dialog are disabled.
fn listen_updater_events(&self, window: Window<P>) {
fn listen_updater_events(&self, window: Window<R>) {
let updater_config = self.manager.config().tauri.updater.clone();
updater::listener(updater_config, self.manager.package_info().clone(), &window);
}
fn run_updater(&self, main_window: Option<Window<P>>) {
fn run_updater(&self, main_window: Option<Window<R>>) {
if let Some(main_window) = main_window {
let event_window = main_window.clone();
let updater_config = self.manager.config().tauri.updater.clone();
@ -398,23 +393,18 @@ impl<P: Params> App<P> {
// When dialog is enabled, if user want to recheck
// if an update is available after first start
// invoke the Event `tauri://update` from JS or rust side.
main_window.listen(
updater::EVENT_CHECK_UPDATE
.parse::<P::Event>()
.unwrap_or_else(|_| panic!("bad label")),
move |_msg| {
let window = event_window.clone();
let package_info = package_info.clone();
let config = config.clone();
// re-spawn task inside tokyo to launch the download
// we don't need to emit anything as everything is handled
// by the process (user is asked to restart at the end)
// and it's handled by the updater
crate::async_runtime::spawn(async move {
updater::check_update_with_dialog(config, package_info, window).await
});
},
);
main_window.listen(updater::EVENT_CHECK_UPDATE, move |_msg| {
let window = event_window.clone();
let package_info = package_info.clone();
let config = config.clone();
// re-spawn task inside tokyo to launch the download
// we don't need to emit anything as everything is handled
// by the process (user is asked to restart at the end)
// and it's handled by the updater
crate::async_runtime::spawn(async move {
updater::check_update_with_dialog(config, package_info, window).await
});
});
} else if updater_config.active {
// we only listen for `tauri://update`
// once we receive the call, we check if an update is available or not
@ -429,29 +419,21 @@ impl<P: Params> App<P> {
/// Builds a Tauri application.
#[allow(clippy::type_complexity)]
pub struct Builder<E, L, MID, TID, A, R>
where
E: Tag,
L: Tag,
MID: MenuId,
TID: MenuId,
A: Assets,
R: Runtime,
{
pub struct Builder<R: Runtime> {
/// The JS message handler.
invoke_handler: Box<InvokeHandler<Args<E, L, MID, TID, A, R>>>,
invoke_handler: Box<InvokeHandler<R>>,
/// The setup hook.
setup: SetupHook<Args<E, L, MID, TID, A, R>>,
setup: SetupHook<R>,
/// Page load hook.
on_page_load: Box<OnPageLoad<Args<E, L, MID, TID, A, R>>>,
on_page_load: Box<OnPageLoad<R>>,
/// windows to create when starting up.
pending_windows: Vec<PendingWindow<Args<E, L, MID, TID, A, R>>>,
pending_windows: Vec<PendingWindow<R>>,
/// All passed plugins
plugins: PluginStore<Args<E, L, MID, TID, A, R>>,
plugins: PluginStore<R>,
/// The webview protocols available to all windows.
uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
@ -461,33 +443,25 @@ where
/// The menu set to all windows.
#[cfg(feature = "menu")]
menu: Option<Menu<MID>>,
menu: Option<Menu>,
/// Menu event handlers that listens to all windows.
#[cfg(feature = "menu")]
menu_event_listeners: Vec<GlobalMenuEventListener<Args<E, L, MID, TID, A, R>>>,
menu_event_listeners: Vec<GlobalMenuEventListener<R>>,
/// Window event handlers that listens to all windows.
window_event_listeners: Vec<GlobalWindowEventListener<Args<E, L, MID, TID, A, R>>>,
window_event_listeners: Vec<GlobalWindowEventListener<R>>,
/// The app system tray.
#[cfg(feature = "system-tray")]
system_tray: Option<tray::SystemTray<TID>>,
system_tray: Option<tray::SystemTray>,
/// System tray event handlers.
#[cfg(feature = "system-tray")]
system_tray_event_listeners: Vec<SystemTrayEventListener<Args<E, L, MID, TID, A, R>>>,
system_tray_event_listeners: Vec<SystemTrayEventListener<R>>,
}
impl<E, L, MID, TID, A, R> Builder<E, L, MID, TID, A, R>
where
E: Tag,
L: Tag,
MID: MenuId,
TID: MenuId,
A: Assets,
R: Runtime,
{
impl<R: Runtime> Builder<R> {
/// Creates a new App builder.
pub fn new() -> Self {
Self {
@ -513,7 +487,7 @@ where
/// Defines the JS message handler callback.
pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
where
F: Fn(Invoke<Args<E, L, MID, TID, A, R>>) + Send + Sync + 'static,
F: Fn(Invoke<R>) + Send + Sync + 'static,
{
self.invoke_handler = Box::new(invoke_handler);
self
@ -522,9 +496,7 @@ where
/// Defines the setup hook.
pub fn setup<F>(mut self, setup: F) -> Self
where
F: Fn(&mut App<Args<E, L, MID, TID, A, R>>) -> Result<(), Box<dyn std::error::Error + Send>>
+ Send
+ 'static,
F: Fn(&mut App<R>) -> Result<(), Box<dyn std::error::Error + Send>> + Send + 'static,
{
self.setup = Box::new(setup);
self
@ -533,14 +505,14 @@ where
/// Defines the page load hook.
pub fn on_page_load<F>(mut self, on_page_load: F) -> Self
where
F: Fn(Window<Args<E, L, MID, TID, A, R>>, PageLoadPayload) + Send + Sync + 'static,
F: Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static,
{
self.on_page_load = Box::new(on_page_load);
self
}
/// Adds a plugin to the runtime.
pub fn plugin<P: Plugin<Args<E, L, MID, TID, A, R>> + 'static>(mut self, plugin: P) -> Self {
pub fn plugin<P: Plugin<R> + 'static>(mut self, plugin: P) -> Self {
self.plugins.register(plugin);
self
}
@ -633,7 +605,7 @@ where
}
/// Creates a new webview window.
pub fn create_window<F>(mut self, label: L, url: WindowUrl, setup: F) -> Self
pub fn create_window<F>(mut self, label: impl Into<String>, url: WindowUrl, setup: F) -> Self
where
F: FnOnce(
<R::Dispatcher as Dispatch>::WindowBuilder,
@ -658,7 +630,7 @@ where
/// Adds the icon configured on `tauri.conf.json` to the system tray with the specified menu items.
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
pub fn system_tray(mut self, system_tray: tray::SystemTray<TID>) -> Self {
pub fn system_tray(mut self, system_tray: tray::SystemTray) -> Self {
self.system_tray.replace(system_tray);
self
}
@ -666,7 +638,7 @@ where
/// Sets the menu to use on all windows.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
pub fn menu(mut self, menu: Menu<MID>) -> Self {
pub fn menu(mut self, menu: Menu) -> Self {
self.menu.replace(menu);
self
}
@ -674,9 +646,7 @@ where
/// Registers a menu event handler for all windows.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
pub fn on_menu_event<
F: Fn(WindowMenuEvent<Args<E, L, MID, TID, A, R>>) + Send + Sync + 'static,
>(
pub fn on_menu_event<F: Fn(WindowMenuEvent<R>) + Send + Sync + 'static>(
mut self,
handler: F,
) -> Self {
@ -685,9 +655,7 @@ where
}
/// Registers a window event handler for all windows.
pub fn on_window_event<
F: Fn(GlobalWindowEvent<Args<E, L, MID, TID, A, R>>) + Send + Sync + 'static,
>(
pub fn on_window_event<F: Fn(GlobalWindowEvent<R>) + Send + Sync + 'static>(
mut self,
handler: F,
) -> Self {
@ -699,7 +667,7 @@ where
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
pub fn on_system_tray_event<
F: Fn(&AppHandle<Args<E, L, MID, TID, A, R>>, tray::SystemTrayEvent<TID>) + Send + Sync + 'static,
F: Fn(&AppHandle<R>, tray::SystemTrayEvent) + Send + Sync + 'static,
>(
mut self,
handler: F,
@ -736,7 +704,7 @@ where
/// Builds the application.
#[allow(clippy::type_complexity)]
pub fn build(mut self, context: Context<A>) -> crate::Result<App<Args<E, L, MID, TID, A, R>>> {
pub fn build<A: Assets>(mut self, context: Context<A>) -> crate::Result<App<R>> {
#[cfg(feature = "system-tray")]
let system_tray_icon = {
let icon = context.system_tray_icon.clone();
@ -779,11 +747,8 @@ where
// set up all the windows defined in the config
for config in manager.config().tauri.windows.clone() {
let url = config.url.clone();
let label = config.label.clone();
let file_drop_enabled = config.file_drop_enabled;
let label = config
.label
.parse()
.unwrap_or_else(|_| panic!("bad label found in config: {}", config.label));
let mut webview_attributes = WebviewAttributes::new(url);
if !file_drop_enabled {
@ -921,13 +886,13 @@ where
}
/// Runs the configured Tauri application.
pub fn run(self, context: Context<A>) -> crate::Result<()> {
pub fn run<A: Assets>(self, context: Context<A>) -> crate::Result<()> {
self.build(context)?.run(|_, _| {});
Ok(())
}
}
fn on_event_loop_event<P: Params>(event: &RunEvent, manager: &WindowManager<P>) {
fn on_event_loop_event<R: Runtime>(event: &RunEvent, manager: &WindowManager<R>) {
if let RunEvent::WindowClose(label) = event {
manager.on_window_close(label);
}
@ -935,14 +900,7 @@ fn on_event_loop_event<P: Params>(event: &RunEvent, manager: &WindowManager<P>)
/// Make `Wry` the default `Runtime` for `Builder`
#[cfg(feature = "wry")]
impl<A: Assets> Default for Builder<String, String, String, String, A, crate::Wry> {
fn default() -> Self {
Self::new()
}
}
#[cfg(not(feature = "wry"))]
impl<A: Assets, R: Runtime> Default for Builder<String, String, String, String, A, R> {
impl Default for Builder<crate::Wry> {
fn default() -> Self {
Self::new()
}

View File

@ -2,22 +2,23 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
pub use crate::{
runtime::{
menu::{MenuUpdate, SystemTrayMenu, SystemTrayMenuEntry, TrayHandle},
window::dpi::{PhysicalPosition, PhysicalSize},
Icon, MenuId, Runtime, SystemTray,
pub use crate::runtime::{
menu::{
MenuHash, MenuId, MenuIdRef, MenuUpdate, SystemTrayMenu, SystemTrayMenuEntry, TrayHandle,
},
Params,
window::dpi::{PhysicalPosition, PhysicalSize},
Icon, Runtime, SystemTray,
};
use tauri_macros::default_runtime;
use std::{collections::HashMap, sync::Arc};
pub(crate) fn get_menu_ids<I: MenuId>(map: &mut HashMap<u16, I>, menu: &SystemTrayMenu<I>) {
pub(crate) fn get_menu_ids(map: &mut HashMap<MenuHash, MenuId>, menu: &SystemTrayMenu) {
for item in &menu.items {
match item {
SystemTrayMenuEntry::CustomItem(c) => {
map.insert(c.id_value(), c.id.clone());
map.insert(c.id, c.id_str.clone());
}
SystemTrayMenuEntry::Submenu(s) => get_menu_ids(map, &s.inner),
_ => {}
@ -28,12 +29,12 @@ pub(crate) fn get_menu_ids<I: MenuId>(map: &mut HashMap<u16, I>, menu: &SystemTr
/// System tray event.
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
#[non_exhaustive]
pub enum SystemTrayEvent<I: MenuId> {
pub enum SystemTrayEvent {
/// Tray context menu item was clicked.
#[non_exhaustive]
MenuItemClick {
/// The id of the menu item.
id: I,
id: MenuId,
},
/// Tray icon received a left click.
///
@ -75,15 +76,14 @@ pub enum SystemTrayEvent<I: MenuId> {
},
}
crate::manager::default_args! {
/// A handle to a system tray. Allows updating the context menu items.
pub struct SystemTrayHandle<P: Params> {
pub(crate) ids: Arc<HashMap<u16, P::SystemTrayMenuId>>,
pub(crate) inner: <P::Runtime as Runtime>::TrayHandler,
}
/// A handle to a system tray. Allows updating the context menu items.
#[default_runtime(crate::Wry, wry)]
pub struct SystemTrayHandle<R: Runtime> {
pub(crate) ids: Arc<HashMap<MenuHash, MenuId>>,
pub(crate) inner: R::TrayHandler,
}
impl<P: Params> Clone for SystemTrayHandle<P> {
impl<R: Runtime> Clone for SystemTrayHandle<R> {
fn clone(&self) -> Self {
Self {
ids: self.ids.clone(),
@ -92,15 +92,14 @@ impl<P: Params> Clone for SystemTrayHandle<P> {
}
}
crate::manager::default_args! {
/// A handle to a system tray menu item.
pub struct SystemTrayMenuItemHandle<P: Params> {
id: u16,
tray_handler: <P::Runtime as Runtime>::TrayHandler,
}
/// A handle to a system tray menu item.
#[default_runtime(crate::Wry, wry)]
pub struct SystemTrayMenuItemHandle<R: Runtime> {
id: MenuHash,
tray_handler: R::TrayHandler,
}
impl<P: Params> Clone for SystemTrayMenuItemHandle<P> {
impl<R: Runtime> Clone for SystemTrayMenuItemHandle<R> {
fn clone(&self) -> Self {
Self {
id: self.id,
@ -109,8 +108,8 @@ impl<P: Params> Clone for SystemTrayMenuItemHandle<P> {
}
}
impl<P: Params> SystemTrayHandle<P> {
pub fn get_item(&self, id: &P::SystemTrayMenuId) -> SystemTrayMenuItemHandle<P> {
impl<R: Runtime> SystemTrayHandle<R> {
pub fn get_item(&self, id: MenuIdRef<'_>) -> SystemTrayMenuItemHandle<R> {
for (raw, item_id) in self.ids.iter() {
if item_id == id {
return SystemTrayMenuItemHandle {
@ -128,7 +127,7 @@ impl<P: Params> SystemTrayHandle<P> {
}
}
impl<P: Params> SystemTrayMenuItemHandle<P> {
impl<R: Runtime> SystemTrayMenuItemHandle<R> {
/// Modifies the enabled state of the menu item.
pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
self

View File

@ -5,12 +5,13 @@
//! Useful items for custom commands.
use crate::hooks::InvokeError;
use crate::{InvokeMessage, Params};
use crate::runtime::Runtime;
use crate::InvokeMessage;
use serde::de::Visitor;
use serde::{Deserialize, Deserializer};
/// Represents a custom command.
pub struct CommandItem<'a, P: Params> {
pub struct CommandItem<'a, R: Runtime> {
/// The name of the command, e.g. `handler` on `#[command] fn handler(value: u64)`
pub name: &'static str,
@ -18,7 +19,7 @@ pub struct CommandItem<'a, P: Params> {
pub key: &'static str,
/// The [`InvokeMessage`] that was passed to this command.
pub message: &'a InvokeMessage<P>,
pub message: &'a InvokeMessage<R>,
}
/// Trait implemented by command arguments to derive a value from a [`CommandItem`].
@ -36,16 +37,16 @@ pub struct CommandItem<'a, P: Params> {
/// * [`crate::State`]
/// * `T where T: serde::Deserialize`
/// * Any type that implements `Deserialize` can automatically be used as a [`CommandArg`].
pub trait CommandArg<'de, P: Params>: Sized {
pub trait CommandArg<'de, R: Runtime>: Sized {
/// Derives an instance of `Self` from the [`CommandItem`].
///
/// If the derivation fails, the corresponding message will be rejected using [`InvokeMessage#reject`].
fn from_command(command: CommandItem<'de, P>) -> Result<Self, InvokeError>;
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError>;
}
/// Automatically implement [`CommandArg`] for any type that can be deserialized.
impl<'de, D: Deserialize<'de>, P: Params> CommandArg<'de, P> for D {
fn from_command(command: CommandItem<'de, P>) -> Result<Self, InvokeError> {
impl<'de, D: Deserialize<'de>, R: Runtime> CommandArg<'de, R> for D {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
let arg = command.key;
Self::deserialize(command).map_err(|e| crate::Error::InvalidArgs(arg, e).into())
}
@ -84,7 +85,7 @@ macro_rules! pass {
/// If the key doesn't exist, an error will be returned if the deserialized type is not expecting
/// an optional item. If the key does exist, the value will be called with
/// [`Value`](serde_json::Value)'s [`Deserializer`] implementation.
impl<'de, P: Params> Deserializer<'de> for CommandItem<'de, P> {
impl<'de, R: Runtime> Deserializer<'de> for CommandItem<'de, R> {
type Error = serde_json::Error;
pass!(deserialize_any, visitor: V);
@ -146,9 +147,11 @@ impl<'de, P: Params> Deserializer<'de> for CommandItem<'de, P> {
}
/// [Autoref-based stable specialization](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md)
///
/// Nothing in this module is considered stable.
#[doc(hidden)]
pub mod private {
use crate::{InvokeError, InvokeResolver, Params};
use crate::{runtime::Runtime, InvokeError, InvokeResolver};
use futures::{FutureExt, TryFutureExt};
use serde::Serialize;
use serde_json::Value;
@ -174,9 +177,9 @@ pub mod private {
impl SerializeTag {
#[inline(always)]
pub fn block<P, T>(self, value: T, resolver: InvokeResolver<P>)
pub fn block<R, T>(self, value: T, resolver: InvokeResolver<R>)
where
P: Params,
R: Runtime,
T: Serialize,
{
resolver.respond(Ok(value))
@ -211,9 +214,9 @@ pub mod private {
impl ResultTag {
#[inline(always)]
pub fn block<P, T, E>(self, value: Result<T, E>, resolver: InvokeResolver<P>)
pub fn block<R, T, E>(self, value: Result<T, E>, resolver: InvokeResolver<R>)
where
P: Params,
R: Runtime,
T: Serialize,
E: Into<InvokeError>,
{

View File

@ -5,7 +5,8 @@
use crate::{
api::{config::Config, PackageInfo},
hooks::{InvokeError, InvokeMessage, InvokeResolver},
Invoke, Params, Window,
runtime::Runtime,
Invoke, Window,
};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
@ -59,10 +60,10 @@ enum Module {
}
impl Module {
fn run<P: Params>(
fn run<R: Runtime>(
self,
window: Window<P>,
resolver: InvokeResolver<P>,
window: Window<R>,
resolver: InvokeResolver<R>,
config: Arc<Config>,
package_info: PackageInfo,
) {
@ -164,9 +165,9 @@ impl Module {
}
}
pub(crate) fn handle<P: Params>(
pub(crate) fn handle<R: Runtime>(
module: String,
invoke: Invoke<P>,
invoke: Invoke<R>,
config: Arc<Config>,
package_info: &PackageInfo,
) {

View File

@ -4,7 +4,7 @@
use super::InvokeResponse;
use crate::{
runtime::{ClipboardManager, Params},
runtime::{ClipboardManager, Runtime},
window::Window,
};
use serde::Deserialize;
@ -20,7 +20,7 @@ pub enum Cmd {
}
impl Cmd {
pub fn run<P: Params>(self, window: Window<P>) -> crate::Result<InvokeResponse> {
pub fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
let mut clipboard = window.app_handle.clipboard_manager();
match self {
Self::WriteText(text) => Ok(clipboard.write_text(text)?.into()),

View File

@ -7,7 +7,8 @@ use super::InvokeResponse;
use crate::api::dialog::FileDialogBuilder;
use crate::{
api::dialog::{ask as ask_dialog, message as message_dialog, AskResponse},
Params, Window,
runtime::Runtime,
Window,
};
use serde::Deserialize;
@ -73,7 +74,7 @@ pub enum Cmd {
impl Cmd {
#[allow(unused_variables)]
pub fn run<P: Params>(self, window: Window<P>) -> crate::Result<InvokeResponse> {
pub fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
match self {
#[cfg(dialog_open)]
Self::OpenDialog { options } => open(window, options),
@ -159,7 +160,7 @@ unsafe impl raw_window_handle::HasRawWindowHandle for WindowParent {
}
#[cfg(all(windows, any(dialog_open, dialog_save)))]
fn parent<P: Params>(window: Window<P>) -> crate::Result<WindowParent> {
fn parent<R: Runtime>(window: Window<R>) -> crate::Result<WindowParent> {
Ok(WindowParent {
hwnd: window.hwnd()?,
})
@ -168,8 +169,8 @@ fn parent<P: Params>(window: Window<P>) -> crate::Result<WindowParent> {
/// Shows an open dialog.
#[cfg(dialog_open)]
#[allow(unused_variables)]
pub fn open<P: Params>(
window: Window<P>,
pub fn open<R: Runtime>(
window: Window<R>,
options: OpenDialogOptions,
) -> crate::Result<InvokeResponse> {
let mut dialog_builder = FileDialogBuilder::new();
@ -200,8 +201,8 @@ pub fn open<P: Params>(
/// Shows a save dialog.
#[cfg(dialog_save)]
#[allow(unused_variables)]
pub fn save<P: Params>(
window: Window<P>,
pub fn save<R: Runtime>(
window: Window<R>,
options: SaveDialogOptions,
) -> crate::Result<InvokeResponse> {
let mut dialog_builder = FileDialogBuilder::new();

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::{endpoints::InvokeResponse, sealed::ManagerBase, Manager, Params, Window};
use crate::{endpoints::InvokeResponse, runtime::Runtime, sealed::ManagerBase, Manager, Window};
use serde::Deserialize;
/// The API descriptor.
@ -25,7 +25,7 @@ pub enum Cmd {
}
impl Cmd {
pub fn run<P: Params>(self, window: Window<P>) -> crate::Result<InvokeResponse> {
pub fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
match self {
Self::Listen { event, handler } => {
let event_id = rand::random();
@ -41,23 +41,13 @@ impl Cmd {
window_label,
payload,
} => {
// Panic if the user's `Tag` type decided to return an error while parsing.
let e: P::Event = event
.parse()
.unwrap_or_else(|_| panic!("Event module received unhandled event: {}", event));
let window_label: Option<P::Label> = window_label.map(|l| {
l.parse()
.unwrap_or_else(|_| panic!("Event module received unhandled window: {}", l))
});
// dispatch the event to Rust listeners
window.trigger(&e, payload.clone());
window.trigger(&event, payload.clone());
if let Some(target) = window_label {
window.emit_to(&target, &e, payload)?;
window.emit_to(&target, &event, payload)?;
} else {
window.emit_all(&e, payload)?;
window.emit_all(&event, payload)?;
}
Ok(().into())
}
@ -65,7 +55,7 @@ impl Cmd {
}
}
pub fn unlisten_js<P: Params>(window: &Window<P>, event_id: u64) -> String {
pub fn unlisten_js<R: Runtime>(window: &Window<R>, event_id: u64) -> String {
format!(
"
for (var event in (window['{listeners}'] || {{}})) {{
@ -80,8 +70,8 @@ pub fn unlisten_js<P: Params>(window: &Window<P>, event_id: u64) -> String {
)
}
pub fn listen_js<P: Params>(
window: &Window<P>,
pub fn listen_js<R: Runtime>(
window: &Window<R>,
event: String,
event_id: u64,
handler: String,

View File

@ -3,11 +3,11 @@
// SPDX-License-Identifier: MIT
use super::InvokeResponse;
use crate::{Params, Window};
use crate::{Runtime, Window};
use serde::Deserialize;
#[cfg(global_shortcut_all)]
use crate::runtime::{GlobalShortcutManager, Runtime};
use crate::runtime::GlobalShortcutManager;
/// The API descriptor.
#[derive(Deserialize)]
@ -29,9 +29,9 @@ pub enum Cmd {
}
#[cfg(global_shortcut_all)]
fn register_shortcut<P: Params>(
window: Window<P>,
manager: &mut <P::Runtime as Runtime>::GlobalShortcutManager,
fn register_shortcut<R: Runtime>(
window: Window<R>,
manager: &mut R::GlobalShortcutManager,
shortcut: String,
handler: String,
) -> crate::Result<()> {
@ -46,7 +46,7 @@ fn register_shortcut<P: Params>(
#[cfg(not(global_shortcut_all))]
impl Cmd {
pub fn run<P: Params>(self, _window: Window<P>) -> crate::Result<InvokeResponse> {
pub fn run<R: Runtime>(self, _window: Window<R>) -> crate::Result<InvokeResponse> {
Err(crate::Error::ApiNotAllowlisted(
"globalShortcut > all".to_string(),
))
@ -55,7 +55,7 @@ impl Cmd {
#[cfg(global_shortcut_all)]
impl Cmd {
pub fn run<P: Params>(self, window: Window<P>) -> crate::Result<InvokeResponse> {
pub fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
match self {
Self::Register { shortcut, handler } => {
let mut manager = window.app_handle.global_shortcut_manager();

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT
use super::InvokeResponse;
use crate::{Params, Window};
use crate::{runtime::Runtime, Window};
use serde::Deserialize;
/// The API descriptor.
@ -14,7 +14,7 @@ pub enum Cmd {
}
impl Cmd {
pub fn run<P: Params>(self, window: Window<P>) -> crate::Result<InvokeResponse> {
pub fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
match self {
Self::ValidateSalt { salt } => Ok(window.verify_salt(salt).into()),
}

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::{endpoints::InvokeResponse, Params, Window};
use crate::{endpoints::InvokeResponse, runtime::Runtime, Window};
use serde::Deserialize;
#[cfg(shell_execute)]
@ -73,7 +73,7 @@ pub enum Cmd {
impl Cmd {
#[allow(unused_variables)]
pub fn run<P: Params>(self, window: Window<P>) -> crate::Result<InvokeResponse> {
pub fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
match self {
Self::Execute {
program,

View File

@ -3,15 +3,15 @@
// SPDX-License-Identifier: MIT
#[cfg(window_create)]
use crate::runtime::{webview::WindowBuilder, Dispatch, Runtime};
use crate::runtime::{webview::WindowBuilder, Dispatch};
use crate::{
api::config::WindowConfig,
endpoints::InvokeResponse,
runtime::{
window::dpi::{Position, Size},
UserAttentionType,
Runtime, UserAttentionType,
},
Manager, Params, Window,
Manager, Window,
};
use serde::Deserialize;
@ -103,7 +103,7 @@ struct WindowCreatedEvent {
impl Cmd {
#[allow(dead_code)]
pub async fn run<P: Params>(self, window: Window<P>) -> crate::Result<InvokeResponse> {
pub async fn run<R: Runtime>(self, window: Window<R>) -> crate::Result<InvokeResponse> {
match self {
#[cfg(not(window_create))]
Self::CreateWebview { .. } => {
@ -114,36 +114,21 @@ impl Cmd {
#[cfg(window_create)]
Self::CreateWebview { options } => {
let mut window = window;
// Panic if the user's `Tag` type decided to return an error while parsing.
let label: P::Label = options.label.parse().unwrap_or_else(|_| {
panic!(
"Window module received unknown window label: {}",
options.label
)
});
let label = options.label.clone();
let url = options.url.clone();
window
.create_window(label.clone(), url, |_, webview_attributes| {
(
<<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder>::with_config(
options,
),
<<R::Dispatcher as Dispatch>::WindowBuilder>::with_config(options),
webview_attributes,
)
})?
.emit_others(
&crate::manager::tauri_event::<P::Event>("tauri://window-created"),
Some(WindowCreatedEvent {
label: label.to_string(),
}),
)?;
.emit_others("tauri://window-created", Some(WindowCreatedEvent { label }))?;
}
Self::Manage { label, cmd } => {
let window = if let Some(l) = label {
window
.get_window(&l.parse().unwrap_or_else(|_| panic!("invalid label")))
.ok_or(crate::Error::WebviewNotFound)?
window.get_window(&l).ok_or(crate::Error::WebviewNotFound)?
} else {
window
};

View File

@ -2,9 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::runtime::tag::{Tag, TagRef};
use std::{
borrow::Borrow,
boxed::Box,
collections::HashMap,
fmt,
@ -43,36 +41,33 @@ impl Event {
}
/// What to do with the pending handler when resolving it?
enum Pending<Event: Tag, Window: Tag> {
enum Pending {
Unlisten(EventHandler),
Listen(EventHandler, Event, Handler<Window>),
Trigger(Event, Option<Window>, Option<String>),
Listen(EventHandler, String, Handler),
Trigger(String, Option<String>, Option<String>),
}
/// Stored in [`Listeners`] to be called upon when the event that stored it is triggered.
struct Handler<Window: Tag> {
window: Option<Window>,
struct Handler {
window: Option<String>,
callback: Box<dyn Fn(Event) + Send>,
}
/// A collection of handlers. Multiple handlers can represent the same event.
type Handlers<Event, Window> = HashMap<Event, HashMap<EventHandler, Handler<Window>>>;
/// Holds event handlers and pending event handlers, along with the salts associating them.
struct InnerListeners<Event: Tag, Window: Tag> {
handlers: Mutex<Handlers<Event, Window>>,
pending: Mutex<Vec<Pending<Event, Window>>>,
struct InnerListeners {
handlers: Mutex<HashMap<String, HashMap<EventHandler, Handler>>>,
pending: Mutex<Vec<Pending>>,
function_name: Uuid,
listeners_object_name: Uuid,
queue_object_name: Uuid,
}
/// A self-contained event manager.
pub(crate) struct Listeners<Event: Tag, Window: Tag> {
inner: Arc<InnerListeners<Event, Window>>,
pub(crate) struct Listeners {
inner: Arc<InnerListeners>,
}
impl<Event: Tag, Window: Tag> Default for Listeners<Event, Window> {
impl Default for Listeners {
fn default() -> Self {
Self {
inner: Arc::new(InnerListeners {
@ -86,7 +81,7 @@ impl<Event: Tag, Window: Tag> Default for Listeners<Event, Window> {
}
}
impl<Event: Tag, Window: Tag> Clone for Listeners<Event, Window> {
impl Clone for Listeners {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
@ -94,7 +89,7 @@ impl<Event: Tag, Window: Tag> Clone for Listeners<Event, Window> {
}
}
impl<Event: Tag, Window: Tag> Listeners<Event, Window> {
impl Listeners {
/// Randomly generated function name to represent the JavaScript event function.
pub(crate) fn function_name(&self) -> String {
self.inner.function_name.to_string()
@ -111,7 +106,7 @@ impl<Event: Tag, Window: Tag> Listeners<Event, Window> {
}
/// Insert a pending event action to the queue.
fn insert_pending(&self, action: Pending<Event, Window>) {
fn insert_pending(&self, action: Pending) {
self
.inner
.pending
@ -140,7 +135,7 @@ impl<Event: Tag, Window: Tag> Listeners<Event, Window> {
}
}
fn listen_(&self, id: EventHandler, event: Event, handler: Handler<Window>) {
fn listen_(&self, id: EventHandler, event: String, handler: Handler) {
match self.inner.handlers.try_lock() {
Err(_) => self.insert_pending(Pending::Listen(id, event, handler)),
Ok(mut lock) => {
@ -150,10 +145,10 @@ impl<Event: Tag, Window: Tag> Listeners<Event, Window> {
}
/// Adds an event listener for JS events.
pub(crate) fn listen<F: Fn(self::Event) + Send + 'static>(
pub(crate) fn listen<F: Fn(Event) + Send + 'static>(
&self,
event: Event,
window: Option<Window>,
event: String,
window: Option<String>,
handler: F,
) -> EventHandler {
let id = EventHandler(Uuid::new_v4());
@ -168,10 +163,10 @@ impl<Event: Tag, Window: Tag> Listeners<Event, Window> {
}
/// Listen to a JS event and immediately unlisten.
pub(crate) fn once<F: Fn(self::Event) + Send + 'static>(
pub(crate) fn once<F: Fn(Event) + Send + 'static>(
&self,
event: Event,
window: Option<Window>,
event: String,
window: Option<String>,
handler: F,
) -> EventHandler {
let self_ = self.clone();
@ -192,15 +187,7 @@ impl<Event: Tag, Window: Tag> Listeners<Event, Window> {
}
/// Triggers the given global event with its payload.
pub(crate) fn trigger<E: ?Sized>(
&self,
event: &E,
window: Option<Window>,
payload: Option<String>,
) where
Event: Borrow<E>,
E: TagRef<Event>,
{
pub(crate) fn trigger(&self, event: &str, window: Option<String>, payload: Option<String>) {
let mut maybe_pending = false;
match self.inner.handlers.try_lock() {
Err(_) => self.insert_pending(Pending::Trigger(event.to_owned(), window, payload)),
@ -241,7 +228,7 @@ mod test {
// check to see if listen() is properly passing keys into the LISTENERS map
#[test]
fn listeners_check_key(e in "[a-z]+") {
let listeners: Listeners<String, String> = Default::default();
let listeners: Listeners = Default::default();
// clone e as the key
let key = e.clone();
// pass e and an dummy func into listen
@ -257,7 +244,7 @@ mod test {
// check to see if listen inputs a handler function properly into the LISTENERS map.
#[test]
fn listeners_check_fn(e in "[a-z]+") {
let listeners: Listeners<String, String> = Default::default();
let listeners: Listeners = Default::default();
// clone e as the key
let key = e.clone();
// pass e and an dummy func into listen
@ -283,7 +270,7 @@ mod test {
// check to see if on_event properly grabs the stored function from listen.
#[test]
fn check_on_event(e in "[a-z]+", d in "[a-z]+") {
let listeners: Listeners<String, String> = Default::default();
let listeners: Listeners = Default::default();
// clone e as the key
let key = e.clone();
// call listen with e and the event_fn dummy func

View File

@ -5,21 +5,24 @@
use crate::{
api::rpc::{format_callback, format_callback_result},
app::App,
Params, StateManager, Window,
runtime::Runtime,
StateManager, Window,
};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::{future::Future, sync::Arc};
use tauri_macros::default_runtime;
/// A closure that is run when the Tauri application is setting up.
pub type SetupHook<P> =
Box<dyn Fn(&mut App<P>) -> Result<(), Box<dyn std::error::Error + Send>> + Send>;
pub type SetupHook<R> =
Box<dyn Fn(&mut App<R>) -> Result<(), Box<dyn std::error::Error + Send>> + Send>;
/// A closure that is run everytime Tauri receives a message it doesn't explicitly handle.
pub type InvokeHandler<P> = dyn Fn(Invoke<P>) + Send + Sync + 'static;
pub type InvokeHandler<R> = dyn Fn(Invoke<R>) + Send + Sync + 'static;
/// A closure that is run once every time a window is created and loaded.
pub type OnPageLoad<P> = dyn Fn(Window<P>, PageLoadPayload) + Send + Sync + 'static;
pub type OnPageLoad<R> = dyn Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static;
/// The payload for the [`OnPageLoad`] hook.
#[derive(Debug, Clone, Deserialize)]
@ -34,15 +37,14 @@ impl PageLoadPayload {
}
}
crate::manager::default_args! {
/// The message and resolver given to a custom command.
pub struct Invoke<P: Params> {
/// The message passed.
pub message: InvokeMessage<P>,
/// The message and resolver given to a custom command.
#[default_runtime(crate::Wry, wry)]
pub struct Invoke<R: Runtime> {
/// The message passed.
pub message: InvokeMessage<R>,
/// The resolver of the message.
pub resolver: InvokeResolver<P>,
}
/// The resolver of the message.
pub resolver: InvokeResolver<R>,
}
/// Error response from an [`InvokeMessage`].
@ -112,17 +114,16 @@ impl From<InvokeError> for InvokeResponse {
}
}
crate::manager::default_args! {
/// Resolver of a invoke message.
pub struct InvokeResolver<P: Params> {
window: Window<P>,
pub(crate) callback: String,
pub(crate) error: String,
}
/// Resolver of a invoke message.
#[default_runtime(crate::Wry, wry)]
pub struct InvokeResolver<R: Runtime> {
window: Window<R>,
pub(crate) callback: String,
pub(crate) error: String,
}
impl<P: Params> InvokeResolver<P> {
pub(crate) fn new(window: Window<P>, callback: String, error: String) -> Self {
impl<R: Runtime> InvokeResolver<R> {
pub(crate) fn new(window: Window<R>, callback: String, error: String) -> Self {
Self {
window,
callback,
@ -191,7 +192,7 @@ impl<P: Params> InvokeResolver<P> {
/// If the Result `is_ok()`, the callback will be the `success_callback` function name and the argument will be the Ok value.
/// If the Result `is_err()`, the callback will be the `error_callback` function name and the argument will be the Err value.
pub async fn return_task<T, F>(
window: Window<P>,
window: Window<R>,
task: F,
success_callback: String,
error_callback: String,
@ -204,7 +205,7 @@ impl<P: Params> InvokeResolver<P> {
}
pub(crate) fn return_closure<T: Serialize, F: FnOnce() -> Result<T, InvokeError>>(
window: Window<P>,
window: Window<R>,
f: F,
success_callback: String,
error_callback: String,
@ -213,7 +214,7 @@ impl<P: Params> InvokeResolver<P> {
}
pub(crate) fn return_result(
window: Window<P>,
window: Window<R>,
response: InvokeResponse,
success_callback: String,
error_callback: String,
@ -232,24 +233,23 @@ impl<P: Params> InvokeResolver<P> {
}
}
crate::manager::default_args! {
/// An invoke message.
pub struct InvokeMessage<P: Params> {
/// The window that received the invoke message.
pub(crate) window: Window<P>,
/// Application managed state.
pub(crate) state: Arc<StateManager>,
/// The RPC command.
pub(crate) command: String,
/// The JSON argument passed on the invoke message.
pub(crate) payload: JsonValue,
}
/// An invoke message.
#[default_runtime(crate::Wry, wry)]
pub struct InvokeMessage<R: Runtime> {
/// The window that received the invoke message.
pub(crate) window: Window<R>,
/// Application managed state.
pub(crate) state: Arc<StateManager>,
/// The RPC command.
pub(crate) command: String,
/// The JSON argument passed on the invoke message.
pub(crate) payload: JsonValue,
}
impl<P: Params> InvokeMessage<P> {
impl<R: Runtime> InvokeMessage<R> {
/// Create an new [`InvokeMessage`] from a payload send to a window.
pub(crate) fn new(
window: Window<P>,
window: Window<R>,
state: Arc<StateManager>,
command: String,
payload: JsonValue,
@ -270,13 +270,13 @@ impl<P: Params> InvokeMessage<P> {
/// The window that received the invoke.
#[inline(always)]
pub fn window(&self) -> Window<P> {
pub fn window(&self) -> Window<R> {
self.window.clone()
}
/// A reference to window that received the invoke.
#[inline(always)]
pub fn window_ref(&self) -> &Window<P> {
pub fn window_ref(&self) -> &Window<R> {
&self.window
}

View File

@ -63,7 +63,7 @@ use crate::{
runtime::window::PendingWindow,
};
use serde::Serialize;
use std::{borrow::Borrow, collections::HashMap, sync::Arc};
use std::{collections::HashMap, sync::Arc};
// Export types likely to be used by the application.
#[cfg(any(feature = "menu", feature = "system-tray"))]
@ -89,13 +89,12 @@ pub use {
PageLoadPayload, SetupHook,
},
self::runtime::{
tag::{Tag, TagRef},
webview::{WebviewAttributes, WindowBuilder},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
WindowEvent,
},
Icon, MenuId, Params, RunIteration, UserAttentionType,
Icon, RunIteration, Runtime, UserAttentionType,
},
self::state::{State, StateManager},
self::window::{Monitor, Window},
@ -246,42 +245,26 @@ impl<A: Assets> Context<A> {
// TODO: expand these docs
/// Manages a running application.
pub trait Manager<P: Params>: sealed::ManagerBase<P> {
pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
/// The [`Config`] the manager was created with.
fn config(&self) -> Arc<Config> {
self.manager().config()
}
/// Emits a event to all windows.
fn emit_all<E: ?Sized, S>(&self, event: &E, payload: S) -> Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize + Clone,
{
fn emit_all<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager().emit_filter(event, payload, |_| true)
}
/// Emits an event to a window with the specified label.
fn emit_to<E: ?Sized, L: ?Sized, S: Serialize + Clone>(
&self,
label: &L,
event: &E,
payload: S,
) -> Result<()>
where
P::Label: Borrow<L>,
P::Event: Borrow<E>,
L: TagRef<P::Label>,
E: TagRef<P::Event>,
{
fn emit_to<S: Serialize + Clone>(&self, label: &str, event: &str, payload: S) -> Result<()> {
self
.manager()
.emit_filter(event, payload, |w| label == w.label())
}
/// Listen to a global event.
fn listen_global<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
fn listen_global<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: Fn(EmittedEvent) + Send + 'static,
{
@ -289,7 +272,7 @@ pub trait Manager<P: Params>: sealed::ManagerBase<P> {
}
/// Listen to a global event only once.
fn once_global<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
fn once_global<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: Fn(EmittedEvent) + Send + 'static,
{
@ -297,11 +280,7 @@ pub trait Manager<P: Params>: sealed::ManagerBase<P> {
}
/// Trigger a global event.
fn trigger_global<E: ?Sized>(&self, event: &E, data: Option<String>)
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
{
fn trigger_global(&self, event: &str, data: Option<String>) {
self.manager().trigger(event, None, data)
}
@ -311,16 +290,12 @@ pub trait Manager<P: Params>: sealed::ManagerBase<P> {
}
/// Fetch a single window from the manager.
fn get_window<L: ?Sized>(&self, label: &L) -> Option<Window<P>>
where
P::Label: Borrow<L>,
L: TagRef<P::Label>,
{
fn get_window(&self, label: &str) -> Option<Window<R>> {
self.manager().get_window(label)
}
/// Fetch all managed windows.
fn windows(&self) -> HashMap<P::Label, Window<P>> {
fn windows(&self) -> HashMap<String, Window<R>> {
self.manager().windows()
}
@ -345,33 +320,33 @@ pub trait Manager<P: Params>: sealed::ManagerBase<P> {
/// Prevent implementation details from leaking out of the [`Manager`] trait.
pub(crate) mod sealed {
use crate::{app::AppHandle, manager::WindowManager};
use tauri_runtime::{Params, Runtime, RuntimeHandle};
use tauri_runtime::{Runtime, RuntimeHandle};
/// A running [`Runtime`] or a dispatcher to it.
pub enum RuntimeOrDispatch<'r, P: Params> {
pub enum RuntimeOrDispatch<'r, R: Runtime> {
/// Reference to the running [`Runtime`].
Runtime(&'r P::Runtime),
Runtime(&'r R),
/// Handle to the running [`Runtime`].
RuntimeHandle(<P::Runtime as Runtime>::Handle),
RuntimeHandle(R::Handle),
/// A dispatcher to the running [`Runtime`].
Dispatch(<P::Runtime as Runtime>::Dispatcher),
Dispatch(R::Dispatcher),
}
/// Managed handle to the application runtime.
pub trait ManagerBase<P: Params> {
pub trait ManagerBase<R: Runtime> {
/// The manager behind the [`Managed`] item.
fn manager(&self) -> &WindowManager<P>;
fn manager(&self) -> &WindowManager<R>;
fn runtime(&self) -> RuntimeOrDispatch<'_, P>;
fn app_handle(&self) -> AppHandle<P>;
fn runtime(&self) -> RuntimeOrDispatch<'_, R>;
fn app_handle(&self) -> AppHandle<R>;
/// Creates a new [`Window`] on the [`Runtime`] and attaches it to the [`Manager`].
fn create_new_window(
&self,
pending: crate::PendingWindow<P>,
) -> crate::Result<crate::Window<P>> {
pending: crate::PendingWindow<R>,
) -> crate::Result<crate::Window<R>> {
use crate::runtime::Dispatch;
let labels = self.manager().labels().into_iter().collect::<Vec<_>>();
let pending = self

View File

@ -2,9 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// we re-export the default_args! macro as pub(crate) so we can use it easily from other modules
#![allow(clippy::single_component_path_imports)]
use crate::{
api::{
assets::Assets,
@ -17,14 +14,12 @@ use crate::{
hooks::{InvokeHandler, OnPageLoad, PageLoadPayload},
plugin::PluginStore,
runtime::{
private::ParamsBase,
tag::{tags_to_javascript_array, Tag, TagRef, ToJsString},
webview::{
CustomProtocol, FileDropEvent, FileDropHandler, InvokePayload, WebviewRpcHandler,
WindowBuilder,
},
window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent},
Icon, MenuId, Params, Runtime,
Icon, Runtime,
},
App, Context, Invoke, StateManager, Window,
};
@ -34,20 +29,19 @@ use crate::app::{GlobalMenuEventListener, WindowMenuEvent};
#[cfg(feature = "menu")]
use crate::{
runtime::menu::{Menu, MenuEntry},
runtime::menu::{Menu, MenuEntry, MenuHash, MenuId},
MenuEvent,
};
use serde::Serialize;
use serde_json::Value as JsonValue;
use std::borrow::Borrow;
use std::marker::PhantomData;
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
fs::create_dir_all,
sync::{Arc, Mutex, MutexGuard},
};
use tauri_macros::default_runtime;
use uuid::Uuid;
const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
@ -60,162 +54,62 @@ const WINDOW_SCALE_FACTOR_CHANGED_EVENT: &str = "tauri://scale-change";
#[cfg(feature = "menu")]
const MENU_EVENT: &str = "tauri://menu";
/// Parse a string representing an internal tauri event into [`Params::Event`]
///
/// # Panics
///
/// This will panic if the `FromStr` implementation of [`Params::Event`] returns an error.
pub(crate) fn tauri_event<Event: Tag>(tauri_event: &str) -> Event {
tauri_event.parse().unwrap_or_else(|_| {
panic!(
"failed to parse internal tauri event into Params::Event: {}",
tauri_event
)
})
#[default_runtime(crate::Wry, wry)]
pub struct InnerWindowManager<R: Runtime> {
windows: Mutex<HashMap<String, Window<R>>>,
plugins: Mutex<PluginStore<R>>,
listeners: Listeners,
pub(crate) state: Arc<StateManager>,
/// The JS message handler.
invoke_handler: Box<InvokeHandler<R>>,
/// The page load hook, invoked when the webview performs a navigation.
on_page_load: Box<OnPageLoad<R>>,
config: Arc<Config>,
assets: Arc<dyn Assets>,
default_window_icon: Option<Vec<u8>>,
/// A list of salts that are valid for the current application.
salts: Mutex<HashSet<Uuid>>,
package_info: PackageInfo,
/// The webview protocols protocols available to all windows.
uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
/// The menu set to all windows.
#[cfg(feature = "menu")]
menu: Option<Menu>,
/// Maps runtime id to a strongly typed menu id.
#[cfg(feature = "menu")]
menu_ids: HashMap<MenuHash, MenuId>,
/// Menu event listeners to all windows.
#[cfg(feature = "menu")]
menu_event_listeners: Arc<Vec<GlobalMenuEventListener<R>>>,
/// Window event listeners to all windows.
window_event_listeners: Arc<Vec<GlobalWindowEventListener<R>>>,
}
crate::manager::default_args! {
pub struct InnerWindowManager<P: Params> {
windows: Mutex<HashMap<P::Label, Window<P>>>,
plugins: Mutex<PluginStore<P>>,
listeners: Listeners<P::Event, P::Label>,
pub(crate) state: Arc<StateManager>,
/// The JS message handler.
invoke_handler: Box<InvokeHandler<P>>,
/// The page load hook, invoked when the webview performs a navigation.
on_page_load: Box<OnPageLoad<P>>,
config: Arc<Config>,
assets: Arc<P::Assets>,
default_window_icon: Option<Vec<u8>>,
/// A list of salts that are valid for the current application.
salts: Mutex<HashSet<Uuid>>,
package_info: PackageInfo,
/// The webview protocols protocols available to all windows.
uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
/// The menu set to all windows.
#[cfg(feature = "menu")]
menu: Option<Menu<P::MenuId>>,
/// Maps runtime id to a strongly typed menu id.
#[cfg(feature = "menu")]
menu_ids: HashMap<u16, P::MenuId>,
/// Menu event listeners to all windows.
#[cfg(feature = "menu")]
menu_event_listeners: Arc<Vec<GlobalMenuEventListener<P>>>,
/// Window event listeners to all windows.
window_event_listeners: Arc<Vec<GlobalWindowEventListener<P>>>,
}
#[default_runtime(crate::Wry, wry)]
pub struct WindowManager<R: Runtime> {
pub inner: Arc<InnerWindowManager<R>>,
invoke_keys: Arc<Mutex<Vec<u32>>>,
}
/// struct declaration using params + default args which includes optional feature wry
macro_rules! default_args {
(
$(#[$attrs_struct:meta])*
$vis_struct:vis struct $name:ident<$p:ident: $params:ident> {
$(
$(#[$attrs_field:meta])*
$vis_field:vis $field:ident: $field_type:ty,
)*
}
) => {
$(#[$attrs_struct])*
#[cfg(feature = "wry")]
$vis_struct struct $name<$p: $params = crate::manager::DefaultArgs> {
$(
$(#[$attrs_field])*
$vis_field $field: $field_type,
)*
}
$(#[$attrs_struct])*
#[cfg(not(feature = "wry"))]
$vis_struct struct $name<$p: $params> {
$(
$(#[$attrs_field])*
$vis_field $field: $field_type,
)*
}
};
}
// export it to allow use from other modules
pub(crate) use default_args;
/// This type should always match `Builder::default()`, otherwise the default type is useless.
#[cfg(feature = "wry")]
pub(crate) type DefaultArgs =
Args<String, String, String, String, crate::api::assets::EmbeddedAssets, crate::Wry>;
/// A [Zero Sized Type] marker representing a full [`Params`].
///
/// [Zero Sized Type]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts
pub struct Args<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> {
_event: PhantomData<fn() -> E>,
_label: PhantomData<fn() -> L>,
_menu_id: PhantomData<fn() -> MID>,
_tray_menu_id: PhantomData<fn() -> TID>,
_assets: PhantomData<fn() -> A>,
_runtime: PhantomData<fn() -> R>,
}
impl<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> Default
for Args<E, L, MID, TID, A, R>
{
fn default() -> Self {
Self {
_event: PhantomData,
_label: PhantomData,
_menu_id: PhantomData,
_tray_menu_id: PhantomData,
_assets: PhantomData,
_runtime: PhantomData,
}
}
}
impl<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> ParamsBase
for Args<E, L, MID, TID, A, R>
{
}
impl<E: Tag, L: Tag, MID: MenuId, TID: MenuId, A: Assets, R: Runtime> Params
for Args<E, L, MID, TID, A, R>
{
type Event = E;
type Label = L;
type MenuId = MID;
type SystemTrayMenuId = TID;
type Assets = A;
type Runtime = R;
}
crate::manager::default_args! {
pub struct WindowManager<P: Params> {
pub inner: Arc<InnerWindowManager<P>>,
invoke_keys: Arc<Mutex<Vec<u32>>>,
#[allow(clippy::type_complexity)]
_marker: Args<P::Event, P::Label, P::MenuId, P::SystemTrayMenuId, P::Assets, P::Runtime>,
}
}
impl<P: Params> Clone for WindowManager<P> {
impl<R: Runtime> Clone for WindowManager<R> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
invoke_keys: self.invoke_keys.clone(),
_marker: Args::default(),
}
}
}
#[cfg(feature = "menu")]
fn get_menu_ids<I: MenuId>(map: &mut HashMap<u16, I>, menu: &Menu<I>) {
fn get_menu_ids(map: &mut HashMap<MenuHash, MenuId>, menu: &Menu) {
for item in &menu.items {
match item {
MenuEntry::CustomItem(c) => {
map.insert(c.id_value(), c.id.clone());
map.insert(c.id, c.id_str.clone());
}
MenuEntry::Submenu(s) => get_menu_ids(map, &s.inner),
_ => {}
@ -223,19 +117,19 @@ fn get_menu_ids<I: MenuId>(map: &mut HashMap<u16, I>, menu: &Menu<I>) {
}
}
impl<P: Params> WindowManager<P> {
impl<R: Runtime> WindowManager<R> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn with_handlers(
context: Context<P::Assets>,
plugins: PluginStore<P>,
invoke_handler: Box<InvokeHandler<P>>,
on_page_load: Box<OnPageLoad<P>>,
context: Context<impl Assets>,
plugins: PluginStore<R>,
invoke_handler: Box<InvokeHandler<R>>,
on_page_load: Box<OnPageLoad<R>>,
uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
state: StateManager,
window_event_listeners: Vec<GlobalWindowEventListener<P>>,
window_event_listeners: Vec<GlobalWindowEventListener<R>>,
#[cfg(feature = "menu")] (menu, menu_event_listeners): (
Option<Menu<P::MenuId>>,
Vec<GlobalMenuEventListener<P>>,
Option<Menu>,
Vec<GlobalMenuEventListener<R>>,
),
) -> Self {
Self {
@ -267,12 +161,11 @@ impl<P: Params> WindowManager<P> {
window_event_listeners: Arc::new(window_event_listeners),
}),
invoke_keys: Default::default(),
_marker: Args::default(),
}
}
/// Get a locked handle to the windows.
pub(crate) fn windows_lock(&self) -> MutexGuard<'_, HashMap<P::Label, Window<P>>> {
pub(crate) fn windows_lock(&self) -> MutexGuard<'_, HashMap<String, Window<R>>> {
self.inner.windows.lock().expect("poisoned window manager")
}
@ -283,7 +176,7 @@ impl<P: Params> WindowManager<P> {
/// Get the menu ids mapper.
#[cfg(feature = "menu")]
pub(crate) fn menu_ids(&self) -> HashMap<u16, P::MenuId> {
pub(crate) fn menu_ids(&self) -> HashMap<MenuHash, MenuId> {
self.inner.menu_ids.clone()
}
@ -319,10 +212,10 @@ impl<P: Params> WindowManager<P> {
fn prepare_pending_window(
&self,
mut pending: PendingWindow<P>,
label: P::Label,
pending_labels: &[P::Label],
) -> crate::Result<PendingWindow<P>> {
mut pending: PendingWindow<R>,
label: &str,
pending_labels: &[String],
) -> crate::Result<PendingWindow<R>> {
let is_init_global = self.inner.config.build.with_global_tauri;
let plugin_init = self
.inner
@ -339,8 +232,8 @@ impl<P: Params> WindowManager<P> {
window.__TAURI__.__windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }});
window.__TAURI__.__currentWindow = {{ label: {current_window_label} }}
"#,
window_labels_array = tags_to_javascript_array(pending_labels)?,
current_window_label = label.to_js_string()?,
window_labels_array = serde_json::to_string(pending_labels)?,
current_window_label = serde_json::to_string(&label)?,
));
#[cfg(dev)]
@ -403,7 +296,7 @@ impl<P: Params> WindowManager<P> {
Ok(pending)
}
fn prepare_rpc_handler(&self, app_handle: AppHandle<P>) -> WebviewRpcHandler<P> {
fn prepare_rpc_handler(&self, app_handle: AppHandle<R>) -> WebviewRpcHandler<R> {
let manager = self.clone();
Box::new(move |window, request| {
let window = Window::new(manager.clone(), window, app_handle.clone());
@ -462,11 +355,11 @@ impl<P: Params> WindowManager<P> {
let is_html = path.ends_with(".html");
let asset_response = assets
.get(&path)
.get(&path.as_str().into())
.or_else(|| {
#[cfg(debug_assertions)]
eprintln!("Asset `{}` not found; fallback to index.html", path); // TODO log::error!
assets.get("index.html")
assets.get(&"index.html".into())
})
.ok_or(crate::Error::AssetNotFound(path))
.map(Cow::into_owned);
@ -498,7 +391,7 @@ impl<P: Params> WindowManager<P> {
}
}
fn prepare_file_drop(&self, app_handle: AppHandle<P>) -> FileDropHandler<P> {
fn prepare_file_drop(&self, app_handle: AppHandle<R>) -> FileDropHandler<R> {
let manager = self.clone();
Box::new(move |event, window| {
let manager = manager.clone();
@ -506,17 +399,9 @@ impl<P: Params> WindowManager<P> {
crate::async_runtime::block_on(async move {
let window = Window::new(manager.clone(), window, app_handle);
let _ = match event {
FileDropEvent::Hovered(paths) => {
window.emit(&tauri_event::<P::Event>("tauri://file-drop"), Some(paths))
}
FileDropEvent::Dropped(paths) => window.emit(
&tauri_event::<P::Event>("tauri://file-drop-hover"),
Some(paths),
),
FileDropEvent::Cancelled => window.emit(
&tauri_event::<P::Event>("tauri://file-drop-cancelled"),
Some(()),
),
FileDropEvent::Hovered(paths) => window.emit("tauri://file-drop", Some(paths)),
FileDropEvent::Dropped(paths) => window.emit("tauri://file-drop-hover", Some(paths)),
FileDropEvent::Cancelled => window.emit("tauri://file-drop-cancelled", Some(())),
_ => unimplemented!(),
};
});
@ -601,24 +486,23 @@ impl<P: Params> WindowManager<P> {
#[cfg(test)]
mod test {
use super::{Args, WindowManager};
use super::WindowManager;
use crate::{generate_context, plugin::PluginStore, StateManager, Wry};
#[test]
fn check_get_url() {
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate);
let manager: WindowManager<Args<String, String, String, String, _, Wry>> =
WindowManager::with_handlers(
context,
PluginStore::default(),
Box::new(|_| ()),
Box::new(|_, _| ()),
Default::default(),
StateManager::new(),
Default::default(),
#[cfg(feature = "menu")]
Default::default(),
);
let manager: WindowManager<Wry> = WindowManager::with_handlers(
context,
PluginStore::default(),
Box::new(|_| ()),
Box::new(|_, _| ()),
Default::default(),
StateManager::new(),
Default::default(),
#[cfg(feature = "menu")]
Default::default(),
);
#[cfg(custom_protocol)]
assert_eq!(manager.get_url(), "tauri://localhost");
@ -628,12 +512,12 @@ mod test {
}
}
impl<P: Params> WindowManager<P> {
pub fn run_invoke_handler(&self, invoke: Invoke<P>) {
impl<R: Runtime> WindowManager<R> {
pub fn run_invoke_handler(&self, invoke: Invoke<R>) {
(self.inner.invoke_handler)(invoke);
}
pub fn run_on_page_load(&self, window: Window<P>, payload: PageLoadPayload) {
pub fn run_on_page_load(&self, window: Window<R>, payload: PageLoadPayload) {
(self.inner.on_page_load)(window.clone(), payload.clone());
self
.inner
@ -643,7 +527,7 @@ impl<P: Params> WindowManager<P> {
.on_page_load(window, payload);
}
pub fn extend_api(&self, invoke: Invoke<P>) {
pub fn extend_api(&self, invoke: Invoke<R>) {
self
.inner
.plugins
@ -652,7 +536,7 @@ impl<P: Params> WindowManager<P> {
.extend_api(invoke);
}
pub fn initialize_plugins(&self, app: &App<P>) -> crate::Result<()> {
pub fn initialize_plugins(&self, app: &App<R>) -> crate::Result<()> {
self
.inner
.plugins
@ -663,14 +547,12 @@ impl<P: Params> WindowManager<P> {
pub fn prepare_window(
&self,
app_handle: AppHandle<P>,
mut pending: PendingWindow<P>,
pending_labels: &[P::Label],
) -> crate::Result<PendingWindow<P>> {
app_handle: AppHandle<R>,
mut pending: PendingWindow<R>,
pending_labels: &[String],
) -> crate::Result<PendingWindow<R>> {
if self.windows_lock().contains_key(&pending.label) {
return Err(crate::Error::WindowLabelAlreadyExists(
pending.label.to_string(),
));
return Err(crate::Error::WindowLabelAlreadyExists(pending.label));
}
let (is_local, url) = match &pending.webview_attributes.url {
WindowUrl::App(path) => {
@ -691,7 +573,7 @@ impl<P: Params> WindowManager<P> {
if is_local {
let label = pending.label.clone();
pending = self.prepare_pending_window(pending, label, pending_labels)?;
pending = self.prepare_pending_window(pending, &label, pending_labels)?;
pending.rpc_handler = Some(self.prepare_rpc_handler(app_handle.clone()));
}
@ -703,7 +585,7 @@ impl<P: Params> WindowManager<P> {
Ok(pending)
}
pub fn attach_window(&self, app_handle: AppHandle<P>, window: DetachedWindow<P>) -> Window<P> {
pub fn attach_window(&self, app_handle: AppHandle<R>, window: DetachedWindow<R>) -> Window<R> {
let window = Window::new(self.clone(), window, app_handle);
let window_ = window.clone();
@ -737,7 +619,7 @@ impl<P: Params> WindowManager<P> {
{
self
.windows_lock()
.insert(window.label().clone(), window.clone());
.insert(window.label().to_string(), window.clone());
}
// let plugins know that a new window has been added to the manager
@ -754,17 +636,13 @@ impl<P: Params> WindowManager<P> {
}
pub(crate) fn on_window_close(&self, label: &str) {
self
.windows_lock()
.remove(&label.parse().unwrap_or_else(|_| panic!("bad label")));
self.windows_lock().remove(label);
}
pub fn emit_filter<E: ?Sized, S, F>(&self, event: &E, payload: S, filter: F) -> crate::Result<()>
pub fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize + Clone,
F: Fn(&Window<P>) -> bool,
F: Fn(&Window<R>) -> bool,
{
self
.windows_lock()
@ -773,7 +651,7 @@ impl<P: Params> WindowManager<P> {
.try_for_each(|window| window.emit(event, payload.clone()))
}
pub fn labels(&self) -> HashSet<P::Label> {
pub fn labels(&self) -> HashSet<String> {
self.windows_lock().keys().cloned().collect()
}
@ -789,26 +667,22 @@ impl<P: Params> WindowManager<P> {
self.inner.listeners.unlisten(handler_id)
}
pub fn trigger<E: ?Sized>(&self, event: &E, window: Option<P::Label>, data: Option<String>)
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
{
pub fn trigger(&self, event: &str, window: Option<String>, data: Option<String>) {
self.inner.listeners.trigger(event, window, data)
}
pub fn listen<F: Fn(Event) + Send + 'static>(
&self,
event: P::Event,
window: Option<P::Label>,
event: String,
window: Option<String>,
handler: F,
) -> EventHandler {
self.inner.listeners.listen(event, window, handler)
}
pub fn once<F: Fn(Event) + Send + 'static>(
&self,
event: P::Event,
window: Option<P::Label>,
event: String,
window: Option<String>,
handler: F,
) -> EventHandler {
self.inner.listeners.once(event, window, handler)
@ -848,52 +722,28 @@ impl<P: Params> WindowManager<P> {
.remove(&uuid)
}
pub fn get_window<L: ?Sized>(&self, label: &L) -> Option<Window<P>>
where
P::Label: Borrow<L>,
L: TagRef<P::Label>,
{
pub fn get_window(&self, label: &str) -> Option<Window<R>> {
self.windows_lock().get(label).cloned()
}
pub fn windows(&self) -> HashMap<P::Label, Window<P>> {
pub fn windows(&self) -> HashMap<String, Window<R>> {
self.windows_lock().clone()
}
}
fn on_window_event<P: Params>(
window: &Window<P>,
manager: &WindowManager<P>,
fn on_window_event<R: Runtime>(
window: &Window<R>,
manager: &WindowManager<R>,
event: &WindowEvent,
) -> crate::Result<()> {
match event {
WindowEvent::Resized(size) => window.emit(
&WINDOW_RESIZED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(size),
)?,
WindowEvent::Moved(position) => window.emit(
&WINDOW_MOVED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(position),
)?,
WindowEvent::Resized(size) => window.emit(WINDOW_RESIZED_EVENT, Some(size))?,
WindowEvent::Moved(position) => window.emit(WINDOW_MOVED_EVENT, Some(position))?,
WindowEvent::CloseRequested => {
window.emit(
&WINDOW_CLOSE_REQUESTED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(()),
)?;
window.emit(WINDOW_CLOSE_REQUESTED_EVENT, Some(()))?;
}
WindowEvent::Destroyed => {
window.emit(
&WINDOW_DESTROYED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(()),
)?;
window.emit(WINDOW_DESTROYED_EVENT, Some(()))?;
let label = window.label();
for window in manager.inner.windows.lock().unwrap().values() {
window.eval(&format!(
@ -903,14 +753,10 @@ fn on_window_event<P: Params>(
}
}
WindowEvent::Focused(focused) => window.emit(
&if *focused {
if *focused {
WINDOW_FOCUS_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event"))
} else {
WINDOW_BLUR_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event"))
},
Some(()),
)?,
@ -919,9 +765,7 @@ fn on_window_event<P: Params>(
new_inner_size,
..
} => window.emit(
&WINDOW_SCALE_FACTOR_CHANGED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
WINDOW_SCALE_FACTOR_CHANGED_EVENT,
Some(ScaleFactorChanged {
scale_factor: *scale_factor,
size: *new_inner_size,
@ -940,11 +784,6 @@ struct ScaleFactorChanged {
}
#[cfg(feature = "menu")]
fn on_menu_event<P: Params>(window: &Window<P>, event: &MenuEvent<P::MenuId>) -> crate::Result<()> {
window.emit(
&MENU_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(event.menu_item_id.clone()),
)
fn on_menu_event<R: Runtime>(window: &Window<R>, event: &MenuEvent) -> crate::Result<()> {
window.emit(MENU_EVENT, Some(event.menu_item_id.clone()))
}

View File

@ -4,21 +4,23 @@
//! Extend Tauri functionality.
use crate::{api::config::PluginConfig, App, Invoke, PageLoadPayload, Params, Window};
use crate::{api::config::PluginConfig, runtime::Runtime, App, Invoke, PageLoadPayload, Window};
use serde_json::Value as JsonValue;
use std::collections::HashMap;
use tauri_macros::default_runtime;
/// The plugin result type.
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
/// The plugin interface.
pub trait Plugin<P: Params>: Send {
pub trait Plugin<R: Runtime>: Send {
/// The plugin name. Used as key on the plugin config object.
fn name(&self) -> &'static str;
/// Initialize the plugin.
#[allow(unused_variables)]
fn initialize(&mut self, app: &App<P>, config: JsonValue) -> Result<()> {
fn initialize(&mut self, app: &App<R>, config: JsonValue) -> Result<()> {
Ok(())
}
@ -33,25 +35,24 @@ pub trait Plugin<P: Params>: Send {
/// Callback invoked when the webview is created.
#[allow(unused_variables)]
fn created(&mut self, window: Window<P>) {}
fn created(&mut self, window: Window<R>) {}
/// Callback invoked when the webview performs a navigation.
#[allow(unused_variables)]
fn on_page_load(&mut self, window: Window<P>, payload: PageLoadPayload) {}
fn on_page_load(&mut self, window: Window<R>, payload: PageLoadPayload) {}
/// Add invoke_handler API extension commands.
#[allow(unused_variables)]
fn extend_api(&mut self, invoke: Invoke<P>) {}
fn extend_api(&mut self, invoke: Invoke<R>) {}
}
crate::manager::default_args! {
/// Plugin collection type.
pub(crate) struct PluginStore<P: Params> {
store: HashMap<&'static str, Box<dyn Plugin<P>>>,
}
/// Plugin collection type.
#[default_runtime(crate::Wry, wry)]
pub(crate) struct PluginStore<R: Runtime> {
store: HashMap<&'static str, Box<dyn Plugin<R>>>,
}
impl<P: Params> Default for PluginStore<P> {
impl<R: Runtime> Default for PluginStore<R> {
fn default() -> Self {
Self {
store: HashMap::new(),
@ -59,16 +60,16 @@ impl<P: Params> Default for PluginStore<P> {
}
}
impl<P: Params> PluginStore<P> {
impl<R: Runtime> PluginStore<R> {
/// Adds a plugin to the store.
///
/// Returns `true` if a plugin with the same name is already in the store.
pub fn register<Plug: Plugin<P> + 'static>(&mut self, plugin: Plug) -> bool {
pub fn register<P: Plugin<R> + 'static>(&mut self, plugin: P) -> bool {
self.store.insert(plugin.name(), Box::new(plugin)).is_some()
}
/// Initializes all plugins in the store.
pub(crate) fn initialize(&mut self, app: &App<P>, config: &PluginConfig) -> crate::Result<()> {
pub(crate) fn initialize(&mut self, app: &App<R>, config: &PluginConfig) -> crate::Result<()> {
self.store.values_mut().try_for_each(|plugin| {
plugin
.initialize(
@ -91,7 +92,7 @@ impl<P: Params> PluginStore<P> {
}
/// Runs the created hook for all plugins in the store.
pub(crate) fn created(&mut self, window: Window<P>) {
pub(crate) fn created(&mut self, window: Window<R>) {
self
.store
.values_mut()
@ -99,14 +100,14 @@ impl<P: Params> PluginStore<P> {
}
/// Runs the on_page_load hook for all plugins in the store.
pub(crate) fn on_page_load(&mut self, window: Window<P>, payload: PageLoadPayload) {
pub(crate) fn on_page_load(&mut self, window: Window<R>, payload: PageLoadPayload) {
self
.store
.values_mut()
.for_each(|plugin| plugin.on_page_load(window.clone(), payload.clone()))
}
pub(crate) fn extend_api(&mut self, mut invoke: Invoke<P>) {
pub(crate) fn extend_api(&mut self, mut invoke: Invoke<R>) {
let command = invoke.message.command.replace("plugin:", "");
let mut tokens = command.split('|');
// safe to unwrap: split always has a least one item

View File

@ -2,8 +2,11 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::command::{CommandArg, CommandItem};
use crate::{InvokeError, Params};
use crate::{
command::{CommandArg, CommandItem},
runtime::Runtime,
InvokeError,
};
use state::Container;
/// A guard for a state value.
@ -34,9 +37,9 @@ impl<T: Send + Sync + 'static> Clone for State<'_, T> {
}
}
impl<'r, 'de: 'r, T: Send + Sync + 'static, P: Params> CommandArg<'de, P> for State<'r, T> {
impl<'r, 'de: 'r, T: Send + Sync + 'static, R: Runtime> CommandArg<'de, R> for State<'r, T> {
/// Grabs the [`State`] from the [`CommandItem`]. This will never fail.
fn from_command(command: CommandItem<'de, P>) -> Result<Self, InvokeError> {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
Ok(command.message.state_ref().get())
}
}

View File

@ -332,14 +332,14 @@ mod error;
pub use self::error::Error;
use crate::manager::tauri_event;
use crate::{
api::{
config::UpdaterConfig,
dialog::{ask, AskResponse},
process::restart,
},
Params, Window,
runtime::Runtime,
Window,
};
/// Check for new updates
@ -376,10 +376,10 @@ struct UpdateManifest {
}
/// Check if there is any new update with builtin dialog.
pub(crate) async fn check_update_with_dialog<P: Params>(
pub(crate) async fn check_update_with_dialog<R: Runtime>(
updater_config: UpdaterConfig,
package_info: crate::api::PackageInfo,
window: Window<P>,
window: Window<R>,
) {
if let Some(endpoints) = updater_config.endpoints.clone() {
// check updates
@ -418,106 +418,96 @@ pub(crate) async fn check_update_with_dialog<P: Params>(
/// Experimental listener
/// This function should be run on the main thread once.
pub(crate) fn listener<P: Params>(
pub(crate) fn listener<R: Runtime>(
updater_config: UpdaterConfig,
package_info: crate::api::PackageInfo,
window: &Window<P>,
window: &Window<R>,
) {
let isolated_window = window.clone();
// Wait to receive the event `"tauri://update"`
window.listen(
EVENT_CHECK_UPDATE
.parse::<P::Event>()
.unwrap_or_else(|_| panic!("bad label")),
move |_msg| {
let window = isolated_window.clone();
let package_info = package_info.clone();
window.listen(EVENT_CHECK_UPDATE, move |_msg| {
let window = isolated_window.clone();
let package_info = package_info.clone();
// prepare our endpoints
let endpoints = updater_config
.endpoints
.as_ref()
.expect("Something wrong with endpoints")
.clone();
// prepare our endpoints
let endpoints = updater_config
.endpoints
.as_ref()
.expect("Something wrong with endpoints")
.clone();
let pubkey = updater_config.pubkey.clone();
let pubkey = updater_config.pubkey.clone();
// check updates
crate::async_runtime::spawn(async move {
let window = window.clone();
let window_isolation = window.clone();
let pubkey = pubkey.clone();
// check updates
crate::async_runtime::spawn(async move {
let window = window.clone();
let window_isolation = window.clone();
let pubkey = pubkey.clone();
match self::core::builder()
.urls(&endpoints[..])
.current_version(&package_info.version)
.build()
.await
{
Ok(updater) => {
// send notification if we need to update
if updater.should_update {
let body = updater.body.clone().unwrap_or_else(|| String::from(""));
match self::core::builder()
.urls(&endpoints[..])
.current_version(&package_info.version)
.build()
.await
{
Ok(updater) => {
// send notification if we need to update
if updater.should_update {
let body = updater.body.clone().unwrap_or_else(|| String::from(""));
// Emit `tauri://update-available`
let _ = window.emit(
&tauri_event::<P::Event>(EVENT_UPDATE_AVAILABLE),
Some(UpdateManifest {
body,
date: updater.date.clone(),
version: updater.version.clone(),
}),
);
// Emit `tauri://update-available`
let _ = window.emit(
EVENT_UPDATE_AVAILABLE,
Some(UpdateManifest {
body,
date: updater.date.clone(),
version: updater.version.clone(),
}),
);
// Listen for `tauri://update-install`
window.once(
EVENT_INSTALL_UPDATE
.parse::<P::Event>()
.unwrap_or_else(|_| panic!("bad label")),
move |_msg| {
let window = window_isolation.clone();
let updater = updater.clone();
let pubkey = pubkey.clone();
// Listen for `tauri://update-install`
window.once(EVENT_INSTALL_UPDATE, move |_msg| {
let window = window_isolation.clone();
let updater = updater.clone();
let pubkey = pubkey.clone();
// Start installation
crate::async_runtime::spawn(async move {
// emit {"status": "PENDING"}
send_status_update(window.clone(), EVENT_STATUS_PENDING, None);
// Start installation
crate::async_runtime::spawn(async move {
// emit {"status": "PENDING"}
send_status_update(window.clone(), EVENT_STATUS_PENDING, None);
// Launch updater download process
// macOS we display the `Ready to restart dialog` asking to restart
// Windows is closing the current App and launch the downloaded MSI when ready (the process stop here)
// Linux we replace the AppImage by launching a new install, it start a new AppImage instance, so we're closing the previous. (the process stop here)
let update_result = updater.clone().download_and_install(pubkey.clone()).await;
// Launch updater download process
// macOS we display the `Ready to restart dialog` asking to restart
// Windows is closing the current App and launch the downloaded MSI when ready (the process stop here)
// Linux we replace the AppImage by launching a new install, it start a new AppImage instance, so we're closing the previous. (the process stop here)
let update_result = updater.clone().download_and_install(pubkey.clone()).await;
if let Err(err) = update_result {
// emit {"status": "ERROR", "error": "The error message"}
send_status_update(window.clone(), EVENT_STATUS_ERROR, Some(err.to_string()));
} else {
// emit {"status": "DONE"}
send_status_update(window.clone(), EVENT_STATUS_SUCCESS, None);
}
})
},
);
} else {
send_status_update(window.clone(), EVENT_STATUS_UPTODATE, None);
}
}
Err(e) => {
send_status_update(window.clone(), EVENT_STATUS_ERROR, Some(e.to_string()));
if let Err(err) = update_result {
// emit {"status": "ERROR", "error": "The error message"}
send_status_update(window.clone(), EVENT_STATUS_ERROR, Some(err.to_string()));
} else {
// emit {"status": "DONE"}
send_status_update(window.clone(), EVENT_STATUS_SUCCESS, None);
}
})
});
} else {
send_status_update(window.clone(), EVENT_STATUS_UPTODATE, None);
}
}
})
},
);
Err(e) => {
send_status_update(window.clone(), EVENT_STATUS_ERROR, Some(e.to_string()));
}
}
})
});
}
// Send a status update via `tauri://update-status` event.
fn send_status_update<P: Params>(window: Window<P>, status: &str, error: Option<String>) {
fn send_status_update<R: Runtime>(window: Window<R>, status: &str, error: Option<String>) {
let _ = window.emit(
&tauri_event::<P::Event>(EVENT_STATUS_UPDATE),
EVENT_STATUS_UPDATE,
Some(StatusEvent {
error,
status: String::from(status),

View File

@ -18,13 +18,12 @@ use crate::{
manager::WindowManager,
runtime::{
monitor::Monitor as RuntimeMonitor,
tag::{TagRef, ToJsString},
webview::{InvokePayload, WebviewAttributes, WindowBuilder},
window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow, WindowEvent,
},
Dispatch, Icon, Params, Runtime, UserAttentionType,
Dispatch, Icon, Runtime, UserAttentionType,
},
sealed::ManagerBase,
sealed::RuntimeOrDispatch,
@ -33,10 +32,9 @@ use crate::{
use serde::Serialize;
use std::{
borrow::Borrow,
hash::{Hash, Hasher},
};
use tauri_macros::default_runtime;
use std::hash::{Hash, Hasher};
/// Monitor descriptor.
#[derive(Debug, Clone, Serialize)]
@ -83,21 +81,20 @@ impl Monitor {
}
// TODO: expand these docs since this is a pretty important type
crate::manager::default_args! {
/// A webview window managed by Tauri.
///
/// This type also implements [`Manager`] which allows you to manage other windows attached to
/// the same application.
pub struct Window<P: Params> {
/// The webview window created by the runtime.
window: DetachedWindow<P>,
/// The manager to associate this webview window with.
manager: WindowManager<P>,
pub(crate) app_handle: AppHandle<P>,
}
/// A webview window managed by Tauri.
///
/// This type also implements [`Manager`] which allows you to manage other windows attached to
/// the same application.
#[default_runtime(crate::Wry, wry)]
pub struct Window<R: Runtime> {
/// The webview window created by the runtime.
window: DetachedWindow<R>,
/// The manager to associate this webview window with.
manager: WindowManager<R>,
pub(crate) app_handle: AppHandle<R>,
}
impl<P: Params> Clone for Window<P> {
impl<R: Runtime> Clone for Window<R> {
fn clone(&self) -> Self {
Self {
window: self.window.clone(),
@ -107,49 +104,49 @@ impl<P: Params> Clone for Window<P> {
}
}
impl<P: Params> Hash for Window<P> {
impl<R: Runtime> Hash for Window<R> {
/// Only use the [`Window`]'s label to represent its hash.
fn hash<H: Hasher>(&self, state: &mut H) {
self.window.label.hash(state)
}
}
impl<P: Params> Eq for Window<P> {}
impl<P: Params> PartialEq for Window<P> {
impl<R: Runtime> Eq for Window<R> {}
impl<R: Runtime> PartialEq for Window<R> {
/// Only use the [`Window`]'s label to compare equality.
fn eq(&self, other: &Self) -> bool {
self.window.label.eq(&other.window.label)
}
}
impl<P: Params> Manager<P> for Window<P> {}
impl<P: Params> ManagerBase<P> for Window<P> {
fn manager(&self) -> &WindowManager<P> {
impl<R: Runtime> Manager<R> for Window<R> {}
impl<R: Runtime> ManagerBase<R> for Window<R> {
fn manager(&self) -> &WindowManager<R> {
&self.manager
}
fn app_handle(&self) -> AppHandle<P> {
self.app_handle.clone()
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
RuntimeOrDispatch::Dispatch(self.dispatcher())
}
fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
RuntimeOrDispatch::Dispatch(self.dispatcher())
fn app_handle(&self) -> AppHandle<R> {
self.app_handle.clone()
}
}
impl<'de, P: Params> CommandArg<'de, P> for Window<P> {
impl<'de, R: Runtime> CommandArg<'de, R> for Window<R> {
/// Grabs the [`Window`] from the [`CommandItem`]. This will never fail.
fn from_command(command: CommandItem<'de, P>) -> Result<Self, InvokeError> {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
Ok(command.message.window())
}
}
impl<P: Params> Window<P> {
impl<R: Runtime> Window<R> {
/// Create a new window that is attached to the manager.
pub(crate) fn new(
manager: WindowManager<P>,
window: DetachedWindow<P>,
app_handle: AppHandle<P>,
manager: WindowManager<R>,
window: DetachedWindow<R>,
app_handle: AppHandle<R>,
) -> Self {
Self {
window,
@ -161,21 +158,21 @@ impl<P: Params> Window<P> {
/// Creates a new webview window.
pub fn create_window<F>(
&mut self,
label: P::Label,
label: String,
url: WindowUrl,
setup: F,
) -> crate::Result<Window<P>>
) -> crate::Result<Window<R>>
where
F: FnOnce(
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
<R::Dispatcher as Dispatch>::WindowBuilder,
WebviewAttributes,
) -> (
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
<R::Dispatcher as Dispatch>::WindowBuilder,
WebviewAttributes,
),
{
let (window_builder, webview_attributes) = setup(
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder::new(),
<R::Dispatcher as Dispatch>::WindowBuilder::new(),
WebviewAttributes::new(url),
);
self.create_new_window(PendingWindow::new(
@ -186,7 +183,7 @@ impl<P: Params> Window<P> {
}
/// The current window's dispatcher.
pub(crate) fn dispatcher(&self) -> <P::Runtime as Runtime>::Dispatcher {
pub(crate) fn dispatcher(&self) -> R::Dispatcher {
self.window.dispatcher.clone()
}
@ -238,21 +235,16 @@ impl<P: Params> Window<P> {
}
/// The label of this window.
pub fn label(&self) -> &P::Label {
pub fn label(&self) -> &str {
&self.window.label
}
/// Emits an event to the current window.
pub fn emit<E: ?Sized, S>(&self, event: &E, payload: S) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize,
{
pub fn emit<S: Serialize>(&self, event: &str, payload: S) -> crate::Result<()> {
self.eval(&format!(
"window['{}']({{event: {}, payload: {}}}, '{}')",
self.manager.event_emit_function_name(),
event.to_js_string()?,
serde_json::to_string(event)?,
serde_json::to_value(payload)?,
self.manager.generate_salt(),
))?;
@ -261,17 +253,12 @@ impl<P: Params> Window<P> {
}
/// Emits an event on all windows except this one.
pub fn emit_others<E: ?Sized, S>(&self, event: &E, payload: S) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize + Clone,
{
pub fn emit_others<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.manager.emit_filter(event, payload, |w| w != self)
}
/// Listen to an event on this window.
pub fn listen<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static,
{
@ -280,7 +267,7 @@ impl<P: Params> Window<P> {
}
/// Listen to a an event on this window a single time.
pub fn once<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static,
{
@ -289,11 +276,7 @@ impl<P: Params> Window<P> {
}
/// Triggers an event on this window.
pub fn trigger<E: ?Sized>(&self, event: &E, data: Option<String>)
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
{
pub fn trigger(&self, event: &str, data: Option<String>) {
let label = self.window.label.clone();
self.manager.trigger(event, Some(label), data)
}
@ -311,7 +294,7 @@ impl<P: Params> Window<P> {
/// Registers a menu event listener.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
pub fn on_menu_event<F: Fn(MenuEvent<P::MenuId>) + Send + 'static>(&self, f: F) -> uuid::Uuid {
pub fn on_menu_event<F: Fn(MenuEvent) + Send + 'static>(&self, f: F) -> uuid::Uuid {
let menu_ids = self.manager.menu_ids();
self.window.dispatcher.on_menu_event(move |event| {
f(MenuEvent {
@ -324,7 +307,7 @@ impl<P: Params> Window<P> {
/// Gets a handle to the window menu.
#[cfg(feature = "menu")]
pub fn menu_handle(&self) -> MenuHandle<P> {
pub fn menu_handle(&self) -> MenuHandle<R> {
MenuHandle {
ids: self.manager.menu_ids(),
dispatcher: self.dispatcher(),

View File

@ -2,37 +2,38 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::{
runtime::{menu::MenuUpdate, Dispatch, MenuId, Runtime},
Params,
use crate::runtime::{
menu::{MenuHash, MenuId, MenuIdRef, MenuUpdate},
Dispatch, Runtime,
};
use tauri_macros::default_runtime;
use std::collections::HashMap;
/// The window menu event.
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
#[derive(Debug, Clone)]
pub struct MenuEvent<I: MenuId> {
pub(crate) menu_item_id: I,
pub struct MenuEvent {
pub(crate) menu_item_id: MenuId,
}
#[cfg(feature = "menu")]
impl<I: MenuId> MenuEvent<I> {
impl MenuEvent {
/// The menu item id.
pub fn menu_item_id(&self) -> &I {
pub fn menu_item_id(&self) -> MenuIdRef<'_> {
&self.menu_item_id
}
}
crate::manager::default_args! {
/// A handle to a system tray. Allows updating the context menu items.
pub struct MenuHandle<P: Params> {
pub(crate) ids: HashMap<u16, P::MenuId>,
pub(crate) dispatcher: <P::Runtime as Runtime>::Dispatcher,
}
/// A handle to a system tray. Allows updating the context menu items.
#[default_runtime(crate::Wry, wry)]
pub struct MenuHandle<R: Runtime> {
pub(crate) ids: HashMap<MenuHash, MenuId>,
pub(crate) dispatcher: R::Dispatcher,
}
impl<P: Params> Clone for MenuHandle<P> {
impl<R: Runtime> Clone for MenuHandle<R> {
fn clone(&self) -> Self {
Self {
ids: self.ids.clone(),
@ -41,15 +42,14 @@ impl<P: Params> Clone for MenuHandle<P> {
}
}
crate::manager::default_args! {
/// A handle to a system tray menu item.
pub struct MenuItemHandle<P: Params> {
id: u16,
dispatcher: <P::Runtime as Runtime>::Dispatcher,
}
/// A handle to a system tray menu item.
#[default_runtime(crate::Wry, wry)]
pub struct MenuItemHandle<R: Runtime> {
id: u16,
dispatcher: R::Dispatcher,
}
impl<P: Params> Clone for MenuItemHandle<P> {
impl<R: Runtime> Clone for MenuItemHandle<R> {
fn clone(&self) -> Self {
Self {
id: self.id,
@ -58,9 +58,9 @@ impl<P: Params> Clone for MenuItemHandle<P> {
}
}
impl<P: Params> MenuHandle<P> {
impl<R: Runtime> MenuHandle<R> {
/// Gets a handle to the menu item that has the specified `id`.
pub fn get_item(&self, id: &P::MenuId) -> MenuItemHandle<P> {
pub fn get_item(&self, id: MenuIdRef<'_>) -> MenuItemHandle<R> {
for (raw, item_id) in self.ids.iter() {
if item_id == id {
return MenuItemHandle {
@ -107,7 +107,7 @@ impl<P: Params> MenuHandle<P> {
}
}
impl<P: Params> MenuItemHandle<P> {
impl<R: Runtime> MenuItemHandle<R> {
/// Modifies the enabled state of the menu item.
pub fn set_enabled(&self, enabled: bool) -> crate::Result<()> {
self

View File

@ -17,10 +17,10 @@ Plugins allow you to hook into the Tauri application lifecycle and introduce new
To write a plugin you just need to implement the `tauri::plugin::Plugin` trait:
```rust
use tauri::{plugin::{Plugin, Result as PluginResult}, PageLoadPayload, Params, Window, Invoke, App};
use tauri::{plugin::{Plugin, Result as PluginResult}, runtime::Runtime, PageLoadPayload, Window, Invoke, App};
struct MyAwesomePlugin<P: Params> {
invoke_handler: Box<dyn Fn(Invoke<P>) + Send + Sync>,
struct MyAwesomePlugin<R: Runtime> {
invoke_handler: Box<dyn Fn(Invoke<R>) + Send + Sync>,
// plugin state, configuration fields
}
@ -34,7 +34,7 @@ fn initialize() {}
// this will be accessible with `invoke('plugin:awesome|do_something')`.
fn do_something() {}
impl<P: Params> MyAwesomePlugin<P> {
impl<R: Runtime> MyAwesomePlugin<R> {
// you can add configuration fields here,
// see https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
pub fn new() -> Self {
@ -44,7 +44,7 @@ impl<P: Params> MyAwesomePlugin<P> {
}
}
impl<P: Params> Plugin<P> for MyAwesomePlugin<P> {
impl<R: Runtime> Plugin<R> for MyAwesomePlugin<R> {
/// The plugin name. Must be defined and used on the `invoke` calls.
fn name(&self) -> &'static str {
"awesome"
@ -59,18 +59,18 @@ impl<P: Params> Plugin<P> for MyAwesomePlugin<P> {
}
/// initialize plugin with the config provided on `tauri.conf.json > plugins > $yourPluginName` or the default value.
fn initialize(&mut self, app: &App<P>, config: serde_json::Value) -> PluginResult<()> {
fn initialize(&mut self, app: &App<R>, config: serde_json::Value) -> PluginResult<()> {
Ok(())
}
/// Callback invoked when the Window is created.
fn created(&mut self, window: Window<P>) {}
fn created(&mut self, window: Window<R>) {}
/// Callback invoked when the webview performs a navigation.
fn on_page_load(&mut self, window: Window<P>, payload: PageLoadPayload) {}
fn on_page_load(&mut self, window: Window<R>, payload: PageLoadPayload) {}
/// Extend the invoke handler.
fn extend_api(&mut self, message: Invoke<P>) {
fn extend_api(&mut self, message: Invoke<R>) {
(self.invoke_handler)(message)
}
}

View File

@ -52,8 +52,8 @@ fn main() {
.system_tray(
SystemTray::new().with_menu(
SystemTrayMenu::new()
.add_item(CustomMenuItem::new("toggle".into(), "Toggle"))
.add_item(CustomMenuItem::new("new".into(), "New window")),
.add_item(CustomMenuItem::new("toggle", "Toggle"))
.add_item(CustomMenuItem::new("new", "New window")),
),
)
.on_system_tray_event(|app, event| match event {

View File

@ -4,12 +4,12 @@
use tauri::{CustomMenuItem, Menu, MenuItem, Submenu};
pub fn get_menu() -> Menu<String> {
pub fn get_menu() -> Menu {
#[allow(unused_mut)]
let mut disable_item =
CustomMenuItem::new("disable-menu".into(), "Disable menu").accelerator("CmdOrControl+D");
CustomMenuItem::new("disable-menu", "Disable menu").accelerator("CmdOrControl+D");
#[allow(unused_mut)]
let mut test_item = CustomMenuItem::new("test".into(), "Test").accelerator("CmdOrControl+T");
let mut test_item = CustomMenuItem::new("test", "Test").accelerator("CmdOrControl+T");
#[cfg(target_os = "macos")]
{
disable_item = disable_item.native_image(tauri::NativeImage::MenuOnState);
@ -25,7 +25,7 @@ pub fn get_menu() -> Menu<String> {
let test_menu = Menu::new()
.add_item(CustomMenuItem::new(
"selected/disabled".into(),
"selected/disabled",
"Selected and disabled",
))
.add_native_item(MenuItem::Separator)

View File

@ -16,7 +16,7 @@ mod commands;
use commands::{cmd, invoke, message, resolver};
use serde::Deserialize;
use tauri::{command, Params, State, Window};
use tauri::{command, State, Window};
#[derive(Debug)]
pub struct MyState {
@ -125,7 +125,7 @@ async fn async_stateful_command_with_result(
// Non-Ident command function arguments
#[command]
fn command_arguments_wild<P: Params>(_: Window<P>) {
fn command_arguments_wild(_: Window) {
println!("we saw the wildcard!")
}

View File

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Params</title>
</head>
<body>
<h1>Simple custom `Params` types check.</h1>
</body>
</html>

View File

@ -1,7 +0,0 @@
{
"name": "params",
"version": "1.0.0",
"scripts": {
"tauri": "node ../../tooling/cli.js/bin/tauri"
}
}

View File

@ -1,10 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
# These are backup files generated by rustfmt
**/*.rs.bk
config.json
bundle.json

View File

@ -1,3 +0,0 @@
// Copyright {20\d{2}(-20\d{2})?} Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

View File

@ -1,16 +0,0 @@
[package]
name = "params"
version = "0.1.0"
description = "A simple Tauri Application showcasing custom Params types"
edition = "2018"
[build-dependencies]
tauri-build = { path = "../../../core/tauri-build", features = [ "codegen" ] }
[dependencies]
serde = { version = "1", features = [ "derive" ] }
tauri = { path = "../../../core/tauri", features = ["api-all"] }
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]

View File

@ -1,14 +0,0 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tauri_build::{try_build, Attributes, WindowsAttributes};
fn main() {
if let Err(error) = try_build(
Attributes::new()
.windows_attributes(WindowsAttributes::new().window_icon_path("../../.icons/icon.ico")),
) {
panic!("error found during tauri-build: {}", error);
}
}

View File

@ -1,111 +0,0 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
#![allow(
// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
clippy::nonstandard_macro_braces,
)]
use serde::Serialize;
use std::fmt;
use std::str::FromStr;
use tauri::{command, Wry};
trait Params:
tauri::Params<Event = Event, Label = Window, MenuId = Menu, SystemTrayMenuId = SystemMenu>
{
}
impl<P> Params for P where
P: tauri::Params<Event = Event, Label = Window, MenuId = Menu, SystemTrayMenuId = SystemMenu>
{
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum Event {
Foo,
Bar,
Unknown(String),
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Foo => "foo",
Self::Bar => "bar",
Self::Unknown(s) => s,
})
}
}
impl FromStr for Event {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"foo" => Self::Foo,
"bar" => Self::Bar,
other => Self::Unknown(other.to_string()),
})
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum Window {
Main,
}
impl fmt::Display for Window {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Main => "main",
})
}
}
impl FromStr for Window {
type Err = Box<dyn std::error::Error>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "main" {
Ok(Self::Main)
} else {
Err(format!("only expect main window label, found: {}", s).into())
}
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize)]
pub enum Menu {
MenuFoo,
MenuBar,
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize)]
pub enum SystemMenu {
SystemFoo,
SystemBar,
}
#[command]
fn log_window_label(window: tauri::Window<impl Params>) {
dbg!(window.label());
}
#[command]
fn send_foo(window: tauri::Window<impl Params>) {
window
.emit(&Event::Foo, ())
.expect("couldn't send Event::Foo");
}
fn main() {
tauri::Builder::<Event, Window, Menu, SystemMenu, _, Wry>::new()
.invoke_handler(tauri::generate_handler![log_window_label, send_foo])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@ -1,56 +0,0 @@
{
"build": {
"distDir": ["../index.html"],
"devPath": ["../index.html"],
"beforeDevCommand": "",
"beforeBuildCommand": ""
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"../../.icons/32x32.png",
"../../.icons/128x128.png",
"../../.icons/128x128@2x.png",
"../../.icons/icon.icns",
"../../.icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool",
"shortDescription": "",
"longDescription": "",
"deb": {
"depends": [],
"useBootstrapper": false
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"useBootstrapper": false,
"exceptionDomain": ""
}
},
"allowlist": {
"all": true
},
"windows": [
{
"title": "Welcome to Tauri!",
"width": 800,
"height": 600,
"resizable": true,
"fullscreen": false
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'"
},
"updater": {
"active": false
}
}
}

View File

@ -48,18 +48,18 @@ mod rust {
#[cfg(feature = "ui")]
mod ui {
use std::sync::{Arc, Mutex};
use tauri::{Manager, Params, State, Window};
use tauri::{Manager, State, Window};
// wrappers around each Window
// we use a dedicated type because Tauri can only manage a single instance of a given type
struct SplashscreenWindow<P: Params>(Arc<Mutex<Window<P>>>);
struct MainWindow<P: Params>(Arc<Mutex<Window<P>>>);
struct SplashscreenWindow(Arc<Mutex<Window>>);
struct MainWindow(Arc<Mutex<Window>>);
#[tauri::command]
fn close_splashscreen<P: Params>(
_: Window<P>, // force inference of P
splashscreen: State<SplashscreenWindow<P>>,
main: State<MainWindow<P>>,
fn close_splashscreen(
_: Window, // force inference of P
splashscreen: State<SplashscreenWindow>,
main: State<MainWindow>,
) {
// Close splashscreen
splashscreen.0.lock().unwrap().close().unwrap();

View File

@ -1,5 +1,5 @@
#[tauri::command(with_window)]
fn exit<M: tauri::Params>(window: tauri::Window<P>) {
fn exit(window: tauri::Window) {
window.close().unwrap();
}