refactor(core): menu and system-tray feature flags (#1765)

This commit is contained in:
Lucas Fernandes Nogueira
2021-05-10 13:27:42 -03:00
committed by GitHub
parent 08c1c5ca5c
commit 22676df57a
20 changed files with 332 additions and 155 deletions

View File

@@ -80,6 +80,14 @@ The code for the bundler is located in `[Tauri repo root]/tooling/bundler`, and
The code for Tauri Core is located in `[Tauri repo root]/core/tauri`, and the Rust API, Macros, and Utils are in `[Tauri repo root]/core/tauri-(api/macros/utils)`. The easiest way to test your changes is to use the `[Tauri repo root]/examples/helloworld` app. It automatically rebuilds and uses your local copy of the Tauri core packages. Just run `yarn tauri build` or `yarn tauri dev` in the helloworld app directory after making changes to test them out. To use your local changes in another project, edit its `src-tauri/Cargo.toml` file so that the `tauri` key looks like `tauri = { path = "PATH", features = [ "api-all", "cli" ] }`, where `PATH` is the relative path to `[Tauri repo root]/core/tauri`. Then, your local copy of the Tauri core packages will be rebuilt and used whenever you build that project.
#### Building the documentation locally
You can build the Rust documentation locally running the following script:
```bash
$ RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --open
```
### Developing the JS API
The JS API provides bindings between the developer's JS in the Webview and the builtin Tauri APIs, written in Rust. Its code is located in `[Tauri repo root]/tooling/api`. After making changes to the code, run `yarn build` to build it. To test your changes, we recommend using the API example app, located in `[Tauri repo root]/examples/api`. It will automatically use your local copy of the JS API and provides a helpful UI to test the various commands.

View File

@@ -13,10 +13,6 @@ use tauri_codegen::{context_codegen, ContextData};
// TODO docs
/// A builder for generating a Tauri application context during compile time.
///
/// Meant to be used with [`tauri::include_codegen_context!`] inside your application code.
///
/// [`tauri::include_codegen_context!`]: https://docs.rs/tauri/0.12/tauri/macro.include_codegen_context.html
#[cfg_attr(doc_cfg, doc(cfg(feature = "codegen")))]
#[derive(Debug)]
pub struct CodegenContext {

View File

@@ -10,8 +10,13 @@ description = "Wry bindings to the Tauri runtime"
edition = "2018"
[dependencies]
wry = "0.9.1"
wry = { version = "0.9.2", default-features = false, features = ["file-drop", "protocol", "win32"] }
tauri-runtime = { version = "0.0.0", path = "../tauri-runtime" }
tauri-utils = { version = "1.0.0-beta-rc.1", path = "../tauri-utils" }
image = "0.23"
uuid = { version = "0.8.2", features = [ "v4" ] }
[features]
dox = [ "wry/dox" ]
menu = [ "wry/menu" ]
system-tray = [ "wry/tray" ]

View File

@@ -5,18 +5,24 @@
//! The [`wry`] Tauri [`Runtime`].
use tauri_runtime::{
menu::{CustomMenuItem, Menu, MenuId, MenuItem, SystemTrayMenuItem},
monitor::Monitor,
webview::{
FileDropEvent, FileDropHandler, RpcRequest, WebviewRpcHandler, WindowBuilder, WindowBuilderBase,
},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, MenuEvent, PendingWindow, WindowEvent,
DetachedWindow, PendingWindow, WindowEvent,
},
Dispatch, Error, Icon, Params, Result, Runtime, SystemTrayEvent,
Dispatch, Error, Icon, Params, Result, Runtime,
};
#[cfg(feature = "menu")]
use tauri_runtime::window::MenuEvent;
#[cfg(feature = "system-tray")]
use tauri_runtime::SystemTrayEvent;
#[cfg(feature = "system-tray")]
use wry::application::platform::system_tray::SystemTrayBuilder;
use image::{GenericImageView, Pixel};
use tauri_utils::config::WindowConfig;
use uuid::Uuid;
@@ -29,12 +35,7 @@ use wry::{
},
event::{Event, WindowEvent as WryWindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget},
menu::{
CustomMenu as WryCustomMenu, Menu as WryMenu, MenuId as WryMenuId, MenuItem as WryMenuItem,
MenuType,
},
monitor::MonitorHandle,
platform::system_tray::SystemTrayBuilder,
window::{Fullscreen, Icon as WindowIcon, Window, WindowBuilder as WryWindowBuilder, WindowId},
},
webview::{
@@ -52,15 +53,16 @@ use std::{
},
};
#[cfg(any(feature = "menu", feature = "system-tray"))]
mod menu;
#[cfg(any(feature = "menu", feature = "system-tray"))]
use menu::*;
type CreateWebviewHandler =
Box<dyn FnOnce(&EventLoopWindowTarget<Message>) -> Result<WebView> + Send>;
type MainThreadTask = Box<dyn FnOnce() + Send>;
type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
type WindowEventListeners = Arc<Mutex<HashMap<Uuid, WindowEventHandler>>>;
type MenuEventHandler = Box<dyn Fn(&MenuEvent) + Send>;
type MenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;
type SystemTrayEventHandler = Box<dyn Fn(&SystemTrayEvent) + Send>;
type SystemTrayEventListeners = HashMap<Uuid, SystemTrayEventHandler>;
#[repr(C)]
#[derive(Debug)]
@@ -219,73 +221,6 @@ impl From<Position> for PositionWrapper {
}
}
pub struct CustomMenuWrapper(WryCustomMenu);
impl<I: MenuId> From<CustomMenuItem<I>> for CustomMenuWrapper {
fn from(item: CustomMenuItem<I>) -> Self {
Self(WryCustomMenu {
id: WryMenuId(item.id_value()),
name: item.name,
keyboard_accelerators: None,
})
}
}
struct MenuItemWrapper(WryMenuItem);
impl<I: MenuId> From<MenuItem<I>> for MenuItemWrapper {
fn from(item: MenuItem<I>) -> Self {
match item {
MenuItem::Custom(custom) => Self(WryMenuItem::Custom(CustomMenuWrapper::from(custom).0)),
MenuItem::About(v) => Self(WryMenuItem::About(v)),
MenuItem::Hide => Self(WryMenuItem::Hide),
MenuItem::Services => Self(WryMenuItem::Services),
MenuItem::HideOthers => Self(WryMenuItem::HideOthers),
MenuItem::ShowAll => Self(WryMenuItem::ShowAll),
MenuItem::CloseWindow => Self(WryMenuItem::CloseWindow),
MenuItem::Quit => Self(WryMenuItem::Quit),
MenuItem::Copy => Self(WryMenuItem::Copy),
MenuItem::Cut => Self(WryMenuItem::Cut),
MenuItem::Undo => Self(WryMenuItem::Undo),
MenuItem::Redo => Self(WryMenuItem::Redo),
MenuItem::SelectAll => Self(WryMenuItem::SelectAll),
MenuItem::Paste => Self(WryMenuItem::Paste),
MenuItem::EnterFullScreen => Self(WryMenuItem::EnterFullScreen),
MenuItem::Minimize => Self(WryMenuItem::Minimize),
MenuItem::Zoom => Self(WryMenuItem::Zoom),
MenuItem::Separator => Self(WryMenuItem::Separator),
_ => unimplemented!(),
}
}
}
pub struct MenuWrapper(WryMenu);
impl<I: MenuId> From<Menu<I>> for MenuWrapper {
fn from(menu: Menu<I>) -> Self {
Self(WryMenu {
title: menu.title,
items: menu
.items
.into_iter()
.map(|m| MenuItemWrapper::from(m).0)
.collect(),
})
}
}
impl<I: MenuId> From<SystemTrayMenuItem<I>> for MenuItemWrapper {
fn from(item: SystemTrayMenuItem<I>) -> Self {
match item {
SystemTrayMenuItem::Custom(custom) => {
Self(WryMenuItem::Custom(CustomMenuWrapper::from(custom).0))
}
SystemTrayMenuItem::Separator => Self(WryMenuItem::Separator),
_ => unimplemented!(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct WindowBuilderWrapper(WryWindowBuilder);
@@ -320,6 +255,7 @@ impl WindowBuilder for WindowBuilderWrapper {
window
}
#[cfg(feature = "menu")]
fn menu<I: MenuId>(self, menu: Vec<Menu<I>>) -> Self {
Self(
self.0.with_menu(
@@ -401,6 +337,7 @@ impl WindowBuilder for WindowBuilderWrapper {
self.0.window.window_icon.is_some()
}
#[cfg(feature = "menu")]
fn has_menu(&self) -> bool {
self.0.window.window_menu.is_some()
}
@@ -481,6 +418,7 @@ struct DispatcherContext {
proxy: EventLoopProxy<Message>,
task_tx: Sender<MainThreadTask>,
window_event_listeners: WindowEventListeners,
#[cfg(feature = "menu")]
menu_event_listeners: MenuEventListeners,
}
@@ -526,6 +464,7 @@ impl Dispatch for WryDispatcher {
id
}
#[cfg(feature = "menu")]
fn on_menu_event<F: Fn(&MenuEvent) + Send + 'static>(&self, f: F) -> Uuid {
let id = Uuid::new_v4();
self
@@ -817,7 +756,9 @@ pub struct Wry {
webviews: Mutex<HashMap<WindowId, WebView>>,
task_tx: Sender<MainThreadTask>,
window_event_listeners: WindowEventListeners,
#[cfg(feature = "menu")]
menu_event_listeners: MenuEventListeners,
#[cfg(feature = "system-tray")]
system_tray_event_listeners: SystemTrayEventListeners,
task_rx: Receiver<MainThreadTask>,
}
@@ -834,7 +775,9 @@ impl Runtime for Wry {
task_tx,
task_rx,
window_event_listeners: Default::default(),
#[cfg(feature = "menu")]
menu_event_listeners: Default::default(),
#[cfg(feature = "system-tray")]
system_tray_event_listeners: HashMap::default(),
})
}
@@ -851,6 +794,7 @@ impl Runtime for Wry {
proxy: proxy.clone(),
task_tx: self.task_tx.clone(),
window_event_listeners: self.window_event_listeners.clone(),
#[cfg(feature = "menu")]
menu_event_listeners: self.menu_event_listeners.clone(),
},
pending,
@@ -862,6 +806,7 @@ impl Runtime for Wry {
proxy,
task_tx: self.task_tx.clone(),
window_event_listeners: self.window_event_listeners.clone(),
#[cfg(feature = "menu")]
menu_event_listeners: self.menu_event_listeners.clone(),
},
};
@@ -875,7 +820,7 @@ impl Runtime for Wry {
Ok(DetachedWindow { label, dispatcher })
}
#[cfg(target_os = "linux")]
#[cfg(all(feature = "system-tray", target_os = "linux"))]
fn system_tray<I: MenuId>(
&self,
icon: std::path::PathBuf,
@@ -893,7 +838,7 @@ impl Runtime for Wry {
Ok(())
}
#[cfg(not(target_os = "linux"))]
#[cfg(all(feature = "system-tray", not(target_os = "linux")))]
fn system_tray<I: MenuId>(
&self,
icon: Vec<u8>,
@@ -911,6 +856,7 @@ impl Runtime for Wry {
Ok(())
}
#[cfg(feature = "system-tray")]
fn on_system_tray_event<F: Fn(&SystemTrayEvent) + Send + 'static>(&mut self, f: F) -> Uuid {
let id = Uuid::new_v4();
self.system_tray_event_listeners.insert(id, Box::new(f));
@@ -924,8 +870,11 @@ impl Runtime for Wry {
};
let task_rx = self.task_rx;
let window_event_listeners = self.window_event_listeners.clone();
#[cfg(feature = "menu")]
let menu_event_listeners = self.menu_event_listeners.clone();
#[cfg(feature = "system-tray")]
let system_tray_event_listeners = self.system_tray_event_listeners;
self.event_loop.run(move |event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
@@ -940,6 +889,7 @@ impl Runtime for Wry {
}
match event {
#[cfg(feature = "menu")]
Event::MenuEvent {
menu_id,
origin: MenuType::Menubar,
@@ -951,6 +901,7 @@ impl Runtime for Wry {
handler(&event);
}
}
#[cfg(feature = "system-tray")]
Event::MenuEvent {
menu_id,
origin: MenuType::SystemTray,

View File

@@ -0,0 +1,92 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
pub use tauri_runtime::{
menu::{CustomMenuItem, Menu, MenuItem, SystemTrayMenuItem},
window::MenuEvent,
MenuId, SystemTrayEvent,
};
pub use wry::application::menu::{
CustomMenu as WryCustomMenu, Menu as WryMenu, MenuId as WryMenuId, MenuItem as WryMenuItem,
MenuType,
};
use uuid::Uuid;
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
pub type MenuEventHandler = Box<dyn Fn(&MenuEvent) + Send>;
pub type MenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;
pub type SystemTrayEventHandler = Box<dyn Fn(&SystemTrayEvent) + Send>;
pub type SystemTrayEventListeners = HashMap<Uuid, SystemTrayEventHandler>;
pub struct CustomMenuWrapper(pub WryCustomMenu);
impl<I: MenuId> From<CustomMenuItem<I>> for CustomMenuWrapper {
fn from(item: CustomMenuItem<I>) -> Self {
Self(WryCustomMenu {
id: WryMenuId(item.id_value()),
name: item.name,
keyboard_accelerators: None,
})
}
}
pub struct MenuItemWrapper(pub WryMenuItem);
impl<I: MenuId> From<MenuItem<I>> for MenuItemWrapper {
fn from(item: MenuItem<I>) -> Self {
match item {
MenuItem::Custom(custom) => Self(WryMenuItem::Custom(CustomMenuWrapper::from(custom).0)),
MenuItem::About(v) => Self(WryMenuItem::About(v)),
MenuItem::Hide => Self(WryMenuItem::Hide),
MenuItem::Services => Self(WryMenuItem::Services),
MenuItem::HideOthers => Self(WryMenuItem::HideOthers),
MenuItem::ShowAll => Self(WryMenuItem::ShowAll),
MenuItem::CloseWindow => Self(WryMenuItem::CloseWindow),
MenuItem::Quit => Self(WryMenuItem::Quit),
MenuItem::Copy => Self(WryMenuItem::Copy),
MenuItem::Cut => Self(WryMenuItem::Cut),
MenuItem::Undo => Self(WryMenuItem::Undo),
MenuItem::Redo => Self(WryMenuItem::Redo),
MenuItem::SelectAll => Self(WryMenuItem::SelectAll),
MenuItem::Paste => Self(WryMenuItem::Paste),
MenuItem::EnterFullScreen => Self(WryMenuItem::EnterFullScreen),
MenuItem::Minimize => Self(WryMenuItem::Minimize),
MenuItem::Zoom => Self(WryMenuItem::Zoom),
MenuItem::Separator => Self(WryMenuItem::Separator),
_ => unimplemented!(),
}
}
}
pub struct MenuWrapper(pub WryMenu);
impl<I: MenuId> From<Menu<I>> for MenuWrapper {
fn from(menu: Menu<I>) -> Self {
Self(WryMenu {
title: menu.title,
items: menu
.items
.into_iter()
.map(|m| MenuItemWrapper::from(m).0)
.collect(),
})
}
}
impl<I: MenuId> From<SystemTrayMenuItem<I>> for MenuItemWrapper {
fn from(item: SystemTrayMenuItem<I>) -> Self {
match item {
SystemTrayMenuItem::Custom(custom) => {
Self(WryMenuItem::Custom(CustomMenuWrapper::from(custom).0))
}
SystemTrayMenuItem::Separator => Self(WryMenuItem::Separator),
_ => unimplemented!(),
}
}
}

View File

@@ -9,9 +9,23 @@ repository = "https://github.com/tauri-apps/tauri"
description = "Runtime for Tauri applications"
edition = "2018"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [ "--cfg", "doc_cfg" ]
default-target = "x86_64-unknown-linux-gnu"
targets = [
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
"x86_64-apple-darwin"
]
[dependencies]
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
thiserror = "1.0"
tauri-utils = { version = "1.0.0-beta-rc.1", path = "../tauri-utils" }
uuid = { version = "0.8.2", features = [ "v4" ] }
[features]
menu = []
system-tray = []

View File

@@ -4,12 +4,17 @@
//! Internal runtime between Tauri and the underlying webview runtime.
use std::path::PathBuf;
#![cfg_attr(doc_cfg, feature(doc_cfg))]
use std::{fmt::Debug, hash::Hash, path::PathBuf};
use serde::Serialize;
use tauri_utils::assets::Assets;
use uuid::Uuid;
/// Create window and system tray menus.
#[cfg(any(feature = "menu", feature = "system-tray"))]
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "menu", feature = "system-tray"))))]
pub mod menu;
/// Types useful for interacting with a user's monitors.
pub mod monitor;
@@ -17,15 +22,19 @@ pub mod tag;
pub mod webview;
pub mod window;
use menu::{MenuId, SystemTrayMenuItem};
use monitor::Monitor;
use tag::Tag;
use webview::WindowBuilder;
use window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, MenuEvent, PendingWindow, WindowEvent,
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 {}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
@@ -41,7 +50,9 @@ pub enum Error {
/// Failed to serialize/deserialize.
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
/// Encountered an error creating the app system tray,
/// Encountered an error creating the app system tray.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
#[error("error encountered during tray setup: {0}")]
SystemTray(Box<dyn std::error::Error + Send>),
/// Failed to load window icon.
@@ -108,22 +119,29 @@ pub trait Runtime: Sized + 'static {
) -> crate::Result<DetachedWindow<P>>;
/// Adds the icon to the system tray with the specified menu items.
#[cfg(target_os = "linux")]
#[cfg(all(feature = "system-tray", target_os = "linux"))]
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "system-tray", target_os = "linux"))))]
fn system_tray<I: MenuId>(
&self,
icon: std::path::PathBuf,
menu: Vec<SystemTrayMenuItem<I>>,
menu: Vec<menu::SystemTrayMenuItem<I>>,
) -> crate::Result<()>;
/// Adds the icon to the system tray with the specified menu items.
#[cfg(not(target_os = "linux"))]
#[cfg(all(feature = "system-tray", not(target_os = "linux")))]
#[cfg_attr(
doc_cfg,
doc(cfg(all(feature = "system-tray", not(target_os = "linux"))))
)]
fn system_tray<I: MenuId>(
&self,
icon: Vec<u8>,
menu: Vec<SystemTrayMenuItem<I>>,
menu: Vec<menu::SystemTrayMenuItem<I>>,
) -> crate::Result<()>;
/// Registers a system tray event handler.
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
fn on_system_tray_event<F: Fn(&SystemTrayEvent) + Send + 'static>(&mut self, f: F) -> Uuid;
/// Run the webview runtime.
@@ -145,7 +163,9 @@ pub trait Dispatch: Clone + Send + Sized + 'static {
fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> Uuid;
/// Registers a window event handler.
fn on_menu_event<F: Fn(&MenuEvent) + Send + 'static>(&self, f: F) -> Uuid;
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
fn on_menu_event<F: Fn(&window::MenuEvent) + Send + 'static>(&self, f: F) -> Uuid;
// GETTERS

View File

@@ -2,18 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{
collections::hash_map::DefaultHasher,
fmt::Debug,
hash::{Hash, Hasher},
};
use std::{collections::hash_map::DefaultHasher, hash::Hasher};
use serde::Serialize;
/// 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 {}
use super::MenuId;
/// A window menu.
#[derive(Debug, Clone)]

View File

@@ -2,13 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! Items specific to the [`Runtime`](crate::runtime::Runtime)'s webview.
//! Items specific to the [`Runtime`](crate::Runtime)'s webview.
use crate::{
menu::{Menu, MenuId},
window::DetachedWindow,
Icon,
};
use crate::{window::DetachedWindow, Icon};
#[cfg(feature = "menu")]
use crate::{menu::Menu, MenuId};
use serde::Deserialize;
use serde_json::Value as JsonValue;
@@ -80,14 +79,14 @@ impl WebviewAttributes {
}
}
/// Do **NOT** implement this trait except for use in a custom [`Runtime`](crate::runtime::Runtime).
/// Do **NOT** implement this trait except for use in a custom [`Runtime`](crate::Runtime).
///
/// This trait is separate from [`WindowBuilder`] to prevent "accidental" implementation.
pub trait WindowBuilderBase: Sized {}
/// A builder for all attributes related to a single webview.
///
/// This trait is only meant to be implemented by a custom [`Runtime`](crate::runtime::Runtime)
/// This trait is only meant to be implemented by a custom [`Runtime`](crate::Runtime)
/// and not by applications.
pub trait WindowBuilder: WindowBuilderBase {
/// Initializes a new window attributes builder.
@@ -97,6 +96,8 @@ pub trait WindowBuilder: WindowBuilderBase {
fn with_config(config: WindowConfig) -> Self;
/// Sets the menu for the window.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
fn menu<I: MenuId>(self, menu: Vec<Menu<I>>) -> Self;
/// The initial position of the window's.
@@ -143,6 +144,8 @@ pub trait WindowBuilder: WindowBuilderBase {
fn has_icon(&self) -> bool;
/// Whether the menu was set or not.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
fn has_menu(&self) -> bool;
}

View File

@@ -117,7 +117,7 @@ pub struct DetachedWindow<M: Params> {
/// Name of the window
pub label: M::Label,
/// The [`Dispatch`](crate::runtime::Dispatch) associated with the window.
/// The [`Dispatch`](crate::Dispatch) associated with the window.
pub dispatcher: <M::Runtime as Runtime>::Dispatcher,
}

View File

@@ -11,7 +11,14 @@ edition = "2018"
exclude = [ "/test" ]
[package.metadata.docs.rs]
features = [ "api-all" ]
all-features = true
rustdoc-args = [ "--cfg", "doc_cfg" ]
default-target = "x86_64-unknown-linux-gnu"
targets = [
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
"x86_64-apple-darwin"
]
[dependencies]
serde_json = { version = "1.0", features = [ "raw_value" ] }
@@ -66,12 +73,16 @@ tokio-test = "0.4.1"
mockito = "0.30"
[features]
default = ["wry"]
wry = ["tauri-runtime-wry"]
default = [ "wry" ]
dox = [ "tauri-runtime-wry/dox" ]
wry = [ "tauri-runtime-wry" ]
cli = [ "clap" ]
custom-protocol = [ "tauri-macros/custom-protocol" ]
api-all = [ "notification-all", "global-shortcut-all", "updater" ]
updater = [ "reqwest/default-tls" ]
menu = [ "tauri-runtime/menu", "tauri-runtime-wry/menu" ]
system-tray = [ "tauri-runtime/system-tray", "tauri-runtime-wry/system-tray" ]
# allowlist
fs-all = [ ]
fs-read-text-file = [ ]
fs-read-binary-file = [ ]

View File

@@ -59,7 +59,7 @@ fn escape_json_parse(json: &RawValue) -> String {
/// This will serialize primitive JSON types (e.g. booleans, strings, numbers, etc.) as JavaScript literals,
/// but will serialize arrays and objects whose serialized JSON string is smaller than 1 GB and larger
/// than 10 KiB with `JSON.parse('...')`.
/// https://github.com/GoogleChromeLabs/json-parse-benchmark
/// See [json-parse-benchmark](https://github.com/GoogleChromeLabs/json-parse-benchmark).
///
/// # Examples
/// ```

View File

@@ -9,11 +9,10 @@ use crate::{
manager::{Args, WindowManager},
plugin::{Plugin, PluginStore},
runtime::{
menu::{Menu, MenuId, SystemTrayMenuItem},
tag::Tag,
webview::{CustomProtocol, WebviewAttributes, WindowBuilder},
window::{PendingWindow, WindowEvent},
Dispatch, Params, Runtime,
Dispatch, MenuId, Params, Runtime,
},
sealed::{ManagerBase, RuntimeOrDispatch},
Context, Invoke, Manager, StateManager, Window,
@@ -21,19 +20,29 @@ use crate::{
use std::{collections::HashMap, sync::Arc};
#[cfg(feature = "menu")]
use crate::runtime::menu::Menu;
#[cfg(feature = "system-tray")]
use crate::runtime::menu::SystemTrayMenuItem;
#[cfg(feature = "updater")]
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>;
#[cfg(feature = "system-tray")]
type SystemTrayEventListener<P> =
Box<dyn Fn(&AppHandle<P>, SystemTrayEvent<<P as Params>::SystemTrayMenuId>) + Send + Sync>;
/// System tray event.
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
pub struct SystemTrayEvent<I: MenuId> {
menu_item_id: I,
}
#[cfg(feature = "system-tray")]
impl<I: MenuId> SystemTrayEvent<I> {
/// The menu item id.
pub fn menu_item_id(&self) -> &I {
@@ -42,11 +51,14 @@ impl<I: MenuId> SystemTrayEvent<I> {
}
/// 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>,
}
#[cfg(feature = "menu")]
impl<P: Params> WindowMenuEvent<P> {
/// The menu item id.
pub fn menu_item_id(&self) -> &P::MenuId {
@@ -220,18 +232,22 @@ where
state: StateManager,
/// The menu set to all windows.
#[cfg(feature = "menu")]
menu: Vec<Menu<MID>>,
/// Menu event handlers that listens to all windows.
#[cfg(feature = "menu")]
menu_event_listeners: Vec<GlobalMenuEventListener<Args<E, L, MID, TID, A, R>>>,
/// Window event handlers that listens to all windows.
window_event_listeners: Vec<GlobalWindowEventListener<Args<E, L, MID, TID, A, R>>>,
/// The app system tray menu items.
#[cfg(feature = "system-tray")]
system_tray: Vec<SystemTrayMenuItem<TID>>,
/// System tray event handlers.
#[cfg(feature = "system-tray")]
system_tray_event_listeners: Vec<SystemTrayEventListener<Args<E, L, MID, TID, A, R>>>,
}
@@ -254,10 +270,14 @@ where
plugins: PluginStore::default(),
uri_scheme_protocols: Default::default(),
state: StateManager::new(),
#[cfg(feature = "menu")]
menu: Vec::new(),
#[cfg(feature = "menu")]
menu_event_listeners: Vec::new(),
window_event_listeners: Vec::new(),
#[cfg(feature = "system-tray")]
system_tray: Vec::new(),
#[cfg(feature = "system-tray")]
system_tray_event_listeners: Vec::new(),
}
}
@@ -373,18 +393,24 @@ 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, items: Vec<SystemTrayMenuItem<TID>>) -> Self {
self.system_tray = items;
self
}
/// 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: Vec<Menu<MID>>) -> Self {
self.menu = menu;
self
}
/// 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,
>(
@@ -407,6 +433,8 @@ where
}
/// Registers a system tray event handler.
#[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>>, SystemTrayEvent<TID>) + Send + Sync + 'static,
>(
@@ -445,6 +473,7 @@ where
/// Runs the configured Tauri application.
pub fn run(mut self, context: Context<A>) -> crate::Result<()> {
#[cfg(feature = "system-tray")]
let system_tray_icon = context.system_tray_icon.clone();
let manager = WindowManager::with_handlers(
context,
@@ -453,9 +482,9 @@ where
self.on_page_load,
self.uri_scheme_protocols,
self.state,
self.menu,
self.menu_event_listeners,
self.window_event_listeners,
#[cfg(feature = "menu")]
(self.menu, self.menu_event_listeners),
);
// set up all the windows defined in the config
@@ -504,6 +533,7 @@ where
(self.setup)(&mut app).map_err(|e| crate::Error::Setup(e))?;
#[cfg(feature = "system-tray")]
if !self.system_tray.is_empty() {
let ids = get_menu_ids(&self.system_tray);
app
@@ -534,6 +564,7 @@ where
}
}
#[cfg(feature = "system-tray")]
fn get_menu_ids<I: MenuId>(items: &[SystemTrayMenuItem<I>]) -> HashMap<u32, I> {
let mut map = HashMap::new();
for item in items {

View File

@@ -32,8 +32,8 @@ pub struct CommandItem<'a, P: Params> {
/// # Provided Implementations
///
/// Tauri implements [`CommandArg`] automatically for a number of types.
/// * [`tauri::Window`]
/// * [`tauri::State`]
/// * [`crate::Window`]
/// * [`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 {

View File

@@ -9,12 +9,12 @@
//! The user interface in Tauri apps currently leverages Cocoa/WebKit on macOS, gtk-webkit2 on Linux and MSHTML (IE10/11) or Webkit via Edge on Windows.
//! Tauri uses (and contributes to) the MIT licensed project that you can find at [webview](https://github.com/webview/webview).
#![warn(missing_docs, rust_2018_idioms)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
/// The Tauri error enum.
pub use error::Error;
pub use tauri_macros::{command, generate_handler};
/// Core API.
pub mod api;
pub(crate) mod app;
/// Async runtime.
@@ -53,29 +53,41 @@ use serde::Serialize;
use std::{borrow::Borrow, collections::HashMap, path::PathBuf, sync::Arc};
// Export types likely to be used by the application.
#[cfg(any(feature = "menu", feature = "system-tray"))]
#[cfg_attr(doc_cfg, doc(cfg(any(feature = "menu", feature = "system-tray"))))]
pub use runtime::menu::CustomMenuItem;
pub use {
self::api::assets::Assets,
self::api::{
config::{Config, WindowUrl},
PackageInfo,
},
self::app::{App, Builder, GlobalWindowEvent, SystemTrayEvent, WindowMenuEvent},
self::app::{App, Builder, GlobalWindowEvent},
self::hooks::{
Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokeResolver, InvokeResponse, OnPageLoad,
PageLoadPayload, SetupHook,
},
self::runtime::{
menu::{CustomMenuItem, Menu, MenuId, MenuItem, SystemTrayMenuItem},
tag::{Tag, TagRef},
webview::{WebviewAttributes, WindowBuilder},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
WindowEvent,
},
Params,
MenuId, Params,
},
self::state::{State, StateManager},
self::window::{MenuEvent, Monitor, Window},
self::window::{Monitor, Window},
};
#[cfg(feature = "system-tray")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "system-tray")))]
pub use {self::app::SystemTrayEvent, self::runtime::menu::SystemTrayMenuItem};
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
pub use {
self::app::WindowMenuEvent,
self::runtime::menu::{Menu, MenuItem},
self::window::MenuEvent,
};
/// Reads the config file at compile time and generates a [`Context`] based on its content.
@@ -217,7 +229,7 @@ pub trait Manager<P: Params>: sealed::ManagerBase<P> {
}
/// Add `state` to the state managed by the application.
/// See [`tauri::Builder#manage`] for instructions.
/// See [`crate::Builder#manage`] for instructions.
fn manage<T>(&self, state: T)
where
T: Send + Sync + 'static,

View File

@@ -9,12 +9,11 @@ use crate::{
path::{resolve_path, BaseDirectory},
PackageInfo,
},
app::{GlobalMenuEventListener, GlobalWindowEvent, GlobalWindowEventListener, WindowMenuEvent},
app::{GlobalWindowEvent, GlobalWindowEventListener},
event::{Event, EventHandler, Listeners},
hooks::{InvokeHandler, OnPageLoad, PageLoadPayload},
plugin::PluginStore,
runtime::{
menu::{Menu, MenuId, MenuItem},
private::ParamsBase,
tag::{tags_to_javascript_array, Tag, TagRef, ToJsString},
webview::{
@@ -22,10 +21,20 @@ use crate::{
WindowBuilder,
},
window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent},
Icon, Params, Runtime,
Icon, MenuId, Params, Runtime,
},
App, Context, Invoke, MenuEvent, StateManager, Window,
App, Context, Invoke, StateManager, Window,
};
#[cfg(feature = "menu")]
use crate::app::{GlobalMenuEventListener, WindowMenuEvent};
#[cfg(feature = "menu")]
use crate::{
runtime::menu::{Menu, MenuItem},
MenuEvent,
};
use serde::Serialize;
use serde_json::Value as JsonValue;
use std::borrow::Borrow;
@@ -45,6 +54,7 @@ const WINDOW_DESTROYED_EVENT: &str = "tauri://destroyed";
const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
const WINDOW_BLUR_EVENT: &str = "tauri://blur";
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`]
@@ -83,12 +93,16 @@ pub struct InnerWindowManager<P: Params> {
/// 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: Vec<Menu<P::MenuId>>,
/// Maps runtime id to a strongly typed menu id.
#[cfg(feature = "menu")]
menu_ids: HashMap<u32, 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>>>,
menu_ids: HashMap<u32, P::MenuId>,
}
/// A [Zero Sized Type] marker representing a full [`Params`].
@@ -148,6 +162,7 @@ impl<P: Params> Clone for WindowManager<P> {
}
}
#[cfg(feature = "menu")]
fn get_menu_ids<I: MenuId>(menu: &[Menu<I>]) -> HashMap<u32, I> {
let mut map = HashMap::new();
for m in menu {
@@ -169,11 +184,12 @@ impl<P: Params> WindowManager<P> {
on_page_load: Box<OnPageLoad<P>>,
uri_scheme_protocols: HashMap<String, Arc<CustomProtocol>>,
state: StateManager,
menu: Vec<Menu<P::MenuId>>,
menu_event_listeners: Vec<GlobalMenuEventListener<P>>,
window_event_listeners: Vec<GlobalWindowEventListener<P>>,
#[cfg(feature = "menu")] (menu, menu_event_listeners): (
Vec<Menu<P::MenuId>>,
Vec<GlobalMenuEventListener<P>>,
),
) -> Self {
let menu_ids = get_menu_ids(&menu);
Self {
inner: Arc::new(InnerWindowManager {
windows: Mutex::default(),
@@ -188,10 +204,13 @@ impl<P: Params> WindowManager<P> {
salts: Mutex::default(),
package_info: context.package_info,
uri_scheme_protocols,
#[cfg(feature = "menu")]
menu_ids: get_menu_ids(&menu),
#[cfg(feature = "menu")]
menu,
#[cfg(feature = "menu")]
menu_event_listeners: Arc::new(menu_event_listeners),
window_event_listeners: Arc::new(window_event_listeners),
menu_ids,
}),
_marker: Args::default(),
}
@@ -208,6 +227,7 @@ impl<P: Params> WindowManager<P> {
}
/// Get the menu ids mapper.
#[cfg(feature = "menu")]
pub(crate) fn menu_ids(&self) -> HashMap<u32, P::MenuId> {
self.inner.menu_ids.clone()
}
@@ -259,6 +279,7 @@ impl<P: Params> WindowManager<P> {
}
}
#[cfg(feature = "menu")]
if !pending.window_builder.has_menu() {
pending.window_builder = pending.window_builder.menu(self.inner.menu.clone());
}
@@ -471,8 +492,8 @@ mod test {
Box::new(|_, _| ()),
Default::default(),
StateManager::new(),
Vec::new(),
Default::default(),
#[cfg(feature = "menu")]
Default::default(),
);
@@ -565,17 +586,20 @@ impl<P: Params> WindowManager<P> {
});
}
});
let window_ = window.clone();
let menu_event_listeners = self.inner.menu_event_listeners.clone();
window.on_menu_event(move |event| {
let _ = on_menu_event(&window_, &event);
for handler in menu_event_listeners.iter() {
handler(WindowMenuEvent {
window: window_.clone(),
menu_item_id: event.menu_item_id.clone(),
});
}
});
#[cfg(feature = "menu")]
{
let window_ = window.clone();
let menu_event_listeners = self.inner.menu_event_listeners.clone();
window.on_menu_event(move |event| {
let _ = on_menu_event(&window_, &event);
for handler in menu_event_listeners.iter() {
handler(WindowMenuEvent {
window: window_.clone(),
menu_item_id: event.menu_item_id.clone(),
});
}
});
}
// insert the window into our manager
{
@@ -762,6 +786,7 @@ struct ScaleFactorChanged {
size: PhysicalSize<u32>,
}
#[cfg(feature = "menu")]
fn on_menu_event<P: Params>(window: &Window<P>, event: &MenuEvent<P::MenuId>) -> crate::Result<()> {
window.emit(
&MENU_EVENT

View File

@@ -12,7 +12,7 @@ pub struct State<'r, T: Send + Sync + 'static>(&'r T);
impl<'r, T: Send + Sync + 'static> State<'r, T> {
/// Retrieve a borrow to the underlying value with a lifetime of `'r`.
/// Using this method is typically unnecessary as `State` implements
/// [`Deref`] with a [`Deref::Target`] of `T`.
/// [`std::ops::Deref`] with a [`std::ops::Deref::Target`] of `T`.
#[inline(always)]
pub fn inner(&self) -> &'r T {
self.0

View File

@@ -2,13 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#[cfg(feature = "menu")]
use crate::runtime::MenuId;
use crate::{
api::config::WindowUrl,
command::{CommandArg, CommandItem},
event::{Event, EventHandler},
manager::WindowManager,
runtime::{
menu::MenuId,
monitor::Monitor as RuntimeMonitor,
tag::{TagRef, ToJsString},
webview::{InvokePayload, WebviewAttributes, WindowBuilder},
@@ -31,11 +32,14 @@ use std::{
};
/// The window menu event.
#[cfg(feature = "menu")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "menu")))]
#[derive(Debug, Clone)]
pub struct MenuEvent<I: MenuId> {
pub(crate) menu_item_id: I,
}
#[cfg(feature = "menu")]
impl<I: MenuId> MenuEvent<I> {
/// The menu item id.
pub fn menu_item_id(&self) -> &I {
@@ -287,6 +291,8 @@ 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) {
let menu_ids = self.manager.menu_ids();
self.window.dispatcher.on_menu_event(move |event| {

View File

@@ -11,7 +11,7 @@ tauri-build = { path = "../../../core/tauri-build" }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
tauri = { path = "../../../core/tauri", features = ["api-all", "cli", "updater"] }
tauri = { path = "../../../core/tauri", features = ["api-all", "cli", "updater", "system-tray", "menu"] }
[features]
default = [ "custom-protocol" ]

View File

@@ -43,6 +43,9 @@ pub fn rewrite_manifest(config: ConfigHandle) -> crate::Result<()> {
if config.tauri.updater.active {
features.push("updater").unwrap();
}
if config.tauri.system_tray.is_some() {
features.push("system-tray").unwrap();
}
if let Some(tauri) = tauri_entry.as_table_mut() {
let manifest_features = tauri.entry("features");
@@ -51,6 +54,15 @@ pub fn rewrite_manifest(config: ConfigHandle) -> crate::Result<()> {
match tauri {
Value::InlineTable(table) => {
let manifest_features = table.get_or_insert("features", Value::Array(Default::default()));
if let Value::Array(f) = &manifest_features {
for feat in f.iter() {
if let Value::String(feature) = feat {
if feature.value() == "menu" {
features.push("menu").unwrap();
}
}
}
}
*manifest_features = Value::Array(features);
}
Value::String(version) => {