feat!: add Listener and Emitter traits (#9640)

* feat!: add `Listener` and `Emitter` traits

* keep only trait implementation

* change file

* fix doctests

* fix build
This commit is contained in:
Amr Bashir 2024-07-12 15:01:40 +03:00 committed by GitHub
parent 1a88fc1a9b
commit ba9590aa92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 856 additions and 503 deletions

View File

@ -0,0 +1,15 @@
---
"tauri": "patch:breaking"
---
Added `Emitter` and `Listener` traits that defines what an emitter or a listener can do, this however comes with a few breaking changes:
- Removed `Manager::listen_any`, use `Listener::listen_any` instead.
- Removed `Manager::once_any`, use `Listener::once_any` instead.
- Removed `Manager::unlisten`, use `Listener::unlisten` instead.
- Removed `Manager::emit`, use `Emitter::emit` instead.
- Removed `Manager::emit_to`, use `Emitter::emit_to` instead.
- Removed `Manager::emit_filter`, use `Emitter::emit_filter` instead.
- Removed `App/AppHandle::listen`, `WebviewWindow::listen`, `Window::listen` and `Webview::listen`, use `Listener::listen` instead.
- Removed `App/AppHandle::once`, `WebviewWindow::once`, `Window::once` and `Webview::once`, use `Listener::once` instead.
- Removed `App/AppHandle::unlisten`, `WebviewWindow::unlisten`, `Window::unlisten` and `Webview::unlisten`, use `Listener::unlisten` instead.

View File

@ -22,8 +22,8 @@ use crate::{
utils::config::Config,
utils::Env,
webview::PageLoadPayload,
Context, DeviceEventFilter, EventLoopMessage, Manager, Monitor, Runtime, Scopes, StateManager,
Theme, Webview, WebviewWindowBuilder, Window,
Context, DeviceEventFilter, Emitter, EventLoopMessage, Listener, Manager, Monitor, Result,
Runtime, Scopes, StateManager, Theme, Webview, WebviewWindowBuilder, Window,
};
#[cfg(desktop)]
@ -42,6 +42,7 @@ use tauri_runtime::{
};
use tauri_utils::PackageInfo;
use serde::Serialize;
use std::{
borrow::Cow,
collections::HashMap,
@ -66,7 +67,7 @@ pub(crate) type GlobalWebviewEventListener<R> =
Box<dyn Fn(&Webview<R>, &WebviewEvent) + Send + Sync>;
/// A closure that is run when the Tauri application is setting up.
pub type SetupHook<R> =
Box<dyn FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send>;
Box<dyn FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send>;
/// A closure that is run every time a page starts or finishes loading.
pub type OnPageLoad<R> = dyn Fn(&Webview<R>, &PageLoadPayload<'_>) + Send + Sync + 'static;
@ -328,7 +329,7 @@ impl<R: Runtime> Clone for AppHandle<R> {
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, R>) -> Result<Self, InvokeError> {
fn from_command(command: CommandItem<'de, R>) -> std::result::Result<Self, InvokeError> {
Ok(command.message.webview().window().app_handle)
}
}
@ -820,14 +821,13 @@ macro_rules! shared_app_impl {
}
}
/// Event system APIs.
impl<R: Runtime> $app {
impl<R: Runtime> Listener<R> for $app {
/// Listen to an event on this app.
///
/// # Examples
///
/// ```
/// use tauri::Manager;
/// use tauri::Listener;
///
/// tauri::Builder::default()
/// .setup(|app| {
@ -838,19 +838,29 @@ macro_rules! shared_app_impl {
/// Ok(())
/// });
/// ```
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self.manager.listen(event.into(), EventTarget::App, handler)
}
/// Listen to an event on this app only once.
///
/// See [`Self::listen`] for more information.
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager.once(event.into(), EventTarget::App, handler)
}
/// Unlisten to an event on this app.
///
/// # Examples
///
/// ```
/// use tauri::Manager;
/// use tauri::Listener;
///
/// tauri::Builder::default()
/// .setup(|app| {
@ -864,18 +874,82 @@ macro_rules! shared_app_impl {
/// Ok(())
/// });
/// ```
pub fn unlisten(&self, id: EventId) {
fn unlisten(&self, id: EventId) {
self.manager.unlisten(id)
}
}
/// Listen to an event on this app only once.
impl<R: Runtime> Emitter<R> for $app {
/// Emits an event to all [targets](EventTarget).
///
/// See [`Self::listen`] for more information.
pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
/// # Examples
/// ```
/// use tauri::Emitter;
///
/// #[tauri::command]
/// fn synchronize(app: tauri::AppHandle) {
/// // emits the synchronized event to all webviews
/// app.emit("synchronized", ());
/// }
/// ```
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager.emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
F: FnOnce(Event) + Send + 'static,
I: Into<EventTarget>,
S: Serialize + Clone,
{
self.manager.once(event.into(), EventTarget::App, handler)
self.manager.emit_to(target, event, payload)
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager.emit_filter(event, payload, filter)
}
}
};
@ -1239,7 +1313,7 @@ tauri::Builder::default()
#[must_use]
pub fn setup<F>(mut self, setup: F) -> Self
where
F: FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send + 'static,
F: FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send + 'static,
{
self.setup = Box::new(setup);
self

View File

@ -9,8 +9,8 @@ use serde_json::Value as JsonValue;
use tauri_runtime::window::is_label_valid;
use crate::plugin::{Builder, TauriPlugin};
use crate::{command, ipc::CallbackFn, EventId, Manager, Result, Runtime};
use crate::{AppHandle, Webview};
use crate::{command, ipc::CallbackFn, EventId, Result, Runtime};
use crate::{AppHandle, Emitter, Webview};
use super::{is_event_name_valid, EventTarget};

View File

@ -539,178 +539,6 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
self.manager().package_info()
}
/// Listen to an emitted event to any [target](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Manager;
///
/// #[tauri::command]
/// fn synchronize(window: tauri::Window) {
/// // emits the synchronized event to all windows
/// window.emit("synchronized", ());
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen_any("synchronized", |event| {
/// println!("app is in sync");
/// });
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self
.manager()
.listen(event.into(), EventTarget::Any, handler)
}
/// Remove an event listener.
///
/// # Examples
/// ```
/// use tauri::Manager;
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle().clone();
/// let handler = app.listen_any("ready", move |event| {
/// println!("app is ready");
///
/// // we no longer need to listen to the event
/// // we also could have used `app.once_global` instead
/// handle.unlisten(event.id());
/// });
///
/// // stop listening to the event when you do not need it anymore
/// app.unlisten(handler);
///
///
/// Ok(())
/// });
/// ```
fn unlisten(&self, id: EventId) {
self.manager().unlisten(id)
}
/// Listens once to an emitted event to any [target](EventTarget) .
///
/// See [`Self::listen_any`] for more information.
fn once_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager().once(event.into(), EventTarget::Any, handler)
}
/// Emits an event to all [targets](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Manager;
///
/// #[tauri::command]
/// fn synchronize(app: tauri::AppHandle) {
/// // emits the synchronized event to all webviews
/// app.emit("synchronized", ());
/// }
/// ```
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit", skip(self, payload))
)]
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
self.manager().emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Manager, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::to", skip(self, target, payload), fields(target))
)]
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
{
let target = target.into();
#[cfg(feature = "tracing")]
tracing::Span::current().record("target", format!("{target:?}"));
match target {
// if targeting all, emit to all using emit without filter
EventTarget::Any => self.manager().emit(event, payload),
// if targeting any label, emit using emit_filter and filter labels
EventTarget::AnyLabel {
label: target_label,
} => self.manager().emit_filter(event, payload, |t| match t {
EventTarget::Window { label }
| EventTarget::Webview { label }
| EventTarget::WebviewWindow { label } => label == &target_label,
_ => false,
}),
// otherwise match same target
_ => self.manager().emit_filter(event, payload, |t| t == &target),
}
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Manager, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::filter", skip(self, payload, filter))
)]
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager().emit_filter(event, payload, filter)
}
/// Fetch a single window from the manager.
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
@ -933,6 +761,174 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
}
}
/// Listen to events.
pub trait Listener<R: Runtime>: sealed::ManagerBase<R> {
/// Listen to an emitted event on this manager.
///
/// # Examples
/// ```
/// use tauri::{Manager, Listener, Emitter};
///
/// #[tauri::command]
/// fn synchronize(window: tauri::Window) {
/// // emits the synchronized event to all windows
/// window.emit("synchronized", ());
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen("synchronized", |event| {
/// println!("app is in sync");
/// });
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static;
/// Listen to an event on this manager only once.
///
/// See [`Self::listen`] for more information.
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static;
/// Remove an event listener.
///
/// # Examples
/// ```
/// use tauri::{Manager, Listener};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle().clone();
/// let handler = app.listen_any("ready", move |event| {
/// println!("app is ready");
///
/// // we no longer need to listen to the event
/// // we also could have used `app.once_global` instead
/// handle.unlisten(event.id());
/// });
///
/// // stop listening to the event when you do not need it anymore
/// app.unlisten(handler);
///
///
/// Ok(())
/// });
/// ```
fn unlisten(&self, id: EventId);
/// Listen to an emitted event to any [target](EventTarget).
///
/// # Examples
/// ```
/// use tauri::{Manager, Emitter, Listener};
///
/// #[tauri::command]
/// fn synchronize(window: tauri::Window) {
/// // emits the synchronized event to all windows
/// window.emit("synchronized", ());
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.listen_any("synchronized", |event| {
/// println!("app is in sync");
/// });
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
fn listen_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
self
.manager()
.listen(event.into(), EventTarget::Any, handler)
}
/// Listens once to an emitted event to any [target](EventTarget) .
///
/// See [`Self::listen_any`] for more information.
fn once_any<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager().once(event.into(), EventTarget::Any, handler)
}
}
/// Emit events.
pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
/// Emits an event to all [targets](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Emitter;
///
/// #[tauri::command]
/// fn synchronize(app: tauri::AppHandle) {
/// // emits the synchronized event to all webviews
/// app.emit("synchronized", ());
/// }
/// ```
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()>;
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// app.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// app.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// app.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// app.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// app.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone;
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(app: tauri::AppHandle) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// app.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool;
}
/// Prevent implementation details from leaking out of the [`Manager`] trait.
pub(crate) mod sealed {
use super::Runtime;

View File

@ -472,10 +472,6 @@ impl<R: Runtime> AppManager<R> {
self.listeners().listen(event, target, handler)
}
pub fn unlisten(&self, id: EventId) {
self.listeners().unlisten(id)
}
pub fn once<F: FnOnce(Event) + Send + 'static>(
&self,
event: String,
@ -486,6 +482,33 @@ impl<R: Runtime> AppManager<R> {
self.listeners().once(event, target, handler)
}
pub fn unlisten(&self, id: EventId) {
self.listeners().unlisten(id)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit", skip(self, payload))
)]
pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
assert_event_name_is_valid(event);
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = EmitArgs::new(event, payload)?;
let listeners = self.listeners();
listeners.emit_js(self.webview.webviews_lock().values(), event, &emit_args)?;
listeners.emit(emit_args)?;
Ok(())
}
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::filter", skip(self, payload, filter))
)]
pub fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
@ -511,19 +534,36 @@ impl<R: Runtime> AppManager<R> {
Ok(())
}
pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
assert_event_name_is_valid(event);
#[cfg_attr(
feature = "tracing",
tracing::instrument("app::emit::to", skip(self, target, payload), fields(target))
)]
pub fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> crate::Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
{
let target = target.into();
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = EmitArgs::new(event, payload)?;
tracing::Span::current().record("target", format!("{target:?}"));
let listeners = self.listeners();
match target {
// if targeting all, emit to all using emit without filter
EventTarget::Any => self.emit(event, payload),
listeners.emit_js(self.webview.webviews_lock().values(), event, &emit_args)?;
listeners.emit(emit_args)?;
// if targeting any label, emit using emit_filter and filter labels
EventTarget::AnyLabel {
label: target_label,
} => self.emit_filter(event, payload, |t| match t {
EventTarget::Window { label }
| EventTarget::Webview { label }
| EventTarget::WebviewWindow { label } => label == &target_label,
_ => false,
}),
Ok(())
// otherwise match same target
_ => self.emit_filter(event, payload, |t| t == &target),
}
}
pub fn get_window(&self, label: &str) -> Option<Window<R>> {
@ -634,7 +674,8 @@ mod test {
test::{mock_app, MockRuntime},
webview::WebviewBuilder,
window::WindowBuilder,
App, Manager, StateManager, Webview, WebviewWindow, WebviewWindowBuilder, Window, Wry,
App, Emitter, Listener, Manager, StateManager, Webview, WebviewWindow, WebviewWindowBuilder,
Window, Wry,
};
use super::AppManager;
@ -780,7 +821,11 @@ mod test {
run_emit_test("emit (webview_window)", webview_window, &rx);
}
fn run_emit_test<M: Manager<MockRuntime>>(kind: &str, m: M, rx: &Receiver<(&str, String)>) {
fn run_emit_test<M: Manager<MockRuntime> + Emitter<MockRuntime>>(
kind: &str,
m: M,
rx: &Receiver<(&str, String)>,
) {
let mut received = Vec::new();
let payload = "global-payload";
m.emit(TEST_EVENT_NAME, payload).unwrap();
@ -853,7 +898,7 @@ mod test {
);
}
fn run_emit_to_test<M: Manager<MockRuntime>>(
fn run_emit_to_test<M: Manager<MockRuntime> + Emitter<MockRuntime>>(
kind: &str,
m: &M,
window: &Window<MockRuntime>,

View File

@ -25,7 +25,7 @@ use crate::{
pattern::PatternJavascript,
sealed::ManagerBase,
webview::PageLoadPayload,
AppHandle, EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Webview, Window,
AppHandle, Emitter, EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Webview, Window,
};
use super::{

View File

@ -17,8 +17,8 @@ use tauri_runtime::{
};
use crate::{
app::GlobalWindowEventListener, image::Image, sealed::ManagerBase, AppHandle, EventLoopMessage,
EventTarget, Manager, Runtime, Scopes, Window, WindowEvent,
app::GlobalWindowEventListener, image::Image, sealed::ManagerBase, AppHandle, Emitter,
EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Window, WindowEvent,
};
const WINDOW_RESIZED_EVENT: &str = "tauri://resize";

View File

@ -34,7 +34,8 @@ use crate::{
},
manager::{webview::WebviewLabelDef, AppManager},
sealed::{ManagerBase, RuntimeOrDispatch},
AppHandle, Event, EventId, EventLoopMessage, Manager, ResourceTable, Runtime, Window,
AppHandle, Emitter, Event, EventId, EventLoopMessage, Listener, Manager, ResourceTable, Runtime,
Window,
};
use std::{
@ -1484,8 +1485,7 @@ tauri::Builder::default()
}
}
/// Event system APIs.
impl<R: Runtime> Webview<R> {
impl<R: Runtime> Listener<R> for Webview<R> {
/// Listen to an event on this webview.
///
/// # Examples
@ -1493,13 +1493,13 @@ impl<R: Runtime> Webview<R> {
feature = "unstable",
doc = r####"
```
use tauri::Manager;
use tauri::{Manager, Listener};
tauri::Builder::default()
.setup(|app| {
let webview = app.get_webview("main").unwrap();
webview.listen("component-loaded", move |event| {
println!("window just loaded a component");
println!("webview just loaded a component");
});
Ok(())
@ -1507,7 +1507,7 @@ tauri::Builder::default()
```
"####
)]
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
@ -1520,6 +1520,22 @@ tauri::Builder::default()
)
}
/// Listen to an event on this webview only once.
///
/// See [`Self::listen`] for more information.
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager.once(
event.into(),
EventTarget::Webview {
label: self.label().to_string(),
},
handler,
)
}
/// Unlisten to an event on this webview.
///
/// # Examples
@ -1527,7 +1543,7 @@ tauri::Builder::default()
feature = "unstable",
doc = r####"
```
use tauri::Manager;
use tauri::{Manager, Listener};
tauri::Builder::default()
.setup(|app| {
@ -1549,24 +1565,97 @@ tauri::Builder::default()
```
"####
)]
pub fn unlisten(&self, id: EventId) {
fn unlisten(&self, id: EventId) {
self.manager.unlisten(id)
}
}
/// Listen to an event on this webview only once.
impl<R: Runtime> Emitter<R> for Webview<R> {
/// Emits an event to all [targets](EventTarget).
///
/// See [`Self::listen`] for more information.
pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::Emitter;
#[tauri::command]
fn synchronize(webview: tauri::Webview) {
// emits the synchronized event to all webviews
webview.emit("synchronized", ());
}
```
"####
)]
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.manager.emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::{Emitter, EventTarget};
#[tauri::command]
fn download(webview: tauri::Webview) {
for i in 1..100 {
std::thread::sleep(std::time::Duration::from_millis(150));
// emit a download progress event to all listeners
webview.emit_to(EventTarget::any(), "download-progress", i);
// emit an event to listeners that used App::listen or AppHandle::listen
webview.emit_to(EventTarget::app(), "download-progress", i);
// emit an event to any webview/window/webviewWindow matching the given label
webview.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
webview.emit_to(EventTarget::labeled("updater"), "download-progress", i);
// emit an event to listeners that used WebviewWindow::listen
webview.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
}
}
```
"####
)]
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> crate::Result<()>
where
F: FnOnce(Event) + Send + 'static,
I: Into<EventTarget>,
S: Serialize + Clone,
{
self.manager.once(
event.into(),
EventTarget::Webview {
label: self.label().to_string(),
},
handler,
)
self.manager.emit_to(target, event, payload)
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::{Emitter, EventTarget};
#[tauri::command]
fn download(webview: tauri::Webview) {
for i in 1..100 {
std::thread::sleep(std::time::Duration::from_millis(150));
// emit a download progress event to the updater window
webview.emit_filter("download-progress", i, |t| match t {
EventTarget::WebviewWindow { label } => label == "main",
_ => false,
});
}
}
```
"####
)]
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager.emit_filter(event, payload, filter)
}
}

View File

@ -14,7 +14,7 @@ use crate::{
event::EventTarget,
runtime::dpi::{PhysicalPosition, PhysicalSize},
window::Monitor,
ResourceTable,
Emitter, Listener, ResourceTable,
};
#[cfg(desktop)]
use crate::{
@ -26,6 +26,7 @@ use crate::{
UserAttentionType,
},
};
use serde::Serialize;
use tauri_utils::config::{WebviewUrl, WindowConfig};
use url::Url;
@ -189,40 +190,34 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
/// but it might be implemented in the future. **Always** check the request URL.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust,no_run
use tauri::{
utils::config::{Csp, CspDirectiveSources, WebviewUrl},
window::WindowBuilder,
webview::WebviewWindowBuilder,
};
use http::header::HeaderValue;
use std::collections::HashMap;
tauri::Builder::default()
.setup(|app| {
let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
.on_web_resource_request(|request, response| {
if request.uri().scheme_str() == Some("tauri") {
// if we have a CSP header, Tauri is loading an HTML file
// for this example, let's dynamically change the CSP
if let Some(csp) = response.headers_mut().get_mut("Content-Security-Policy") {
// use the tauri helper to parse the CSP policy to a map
let mut csp_map: HashMap<String, CspDirectiveSources> = Csp::Policy(csp.to_str().unwrap().to_string()).into();
csp_map.entry("script-src".to_string()).or_insert_with(Default::default).push("'unsafe-inline'");
// use the tauri helper to get a CSP string from the map
let csp_string = Csp::from(csp_map).to_string();
*csp = HeaderValue::from_str(&csp_string).unwrap();
}
}
})
.build()?;
Ok(())
});
```
"####
)]
/// ```rust,no_run
/// use tauri::{
/// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
/// webview::WebviewWindowBuilder,
/// };
/// use http::header::HeaderValue;
/// use std::collections::HashMap;
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
/// .on_web_resource_request(|request, response| {
/// if request.uri().scheme_str() == Some("tauri") {
/// // if we have a CSP header, Tauri is loading an HTML file
/// // for this example, let's dynamically change the CSP
/// if let Some(csp) = response.headers_mut().get_mut("Content-Security-Policy") {
/// // use the tauri helper to parse the CSP policy to a map
/// let mut csp_map: HashMap<String, CspDirectiveSources> = Csp::Policy(csp.to_str().unwrap().to_string()).into();
/// csp_map.entry("script-src".to_string()).or_insert_with(Default::default).push("'unsafe-inline'");
/// // use the tauri helper to get a CSP string from the map
/// let csp_string = Csp::from(csp_map).to_string();
/// *csp = HeaderValue::from_str(&csp_string).unwrap();
/// }
/// }
/// })
/// .build()?;
/// Ok(())
/// });
/// ```
pub fn on_web_resource_request<
F: Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync + 'static,
>(
@ -236,30 +231,24 @@ tauri::Builder::default()
/// Defines a closure to be executed when the webview navigates to a URL. Returning `false` cancels the navigation.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust,no_run
use tauri::{
utils::config::{Csp, CspDirectiveSources, WebviewUrl},
window::WindowBuilder,
webview::WebviewWindowBuilder,
};
use http::header::HeaderValue;
use std::collections::HashMap;
tauri::Builder::default()
.setup(|app| {
let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
.on_navigation(|url| {
// allow the production URL or localhost on dev
url.scheme() == "tauri" || (cfg!(dev) && url.host_str() == Some("localhost"))
})
.build()?;
Ok(())
});
```
"####
)]
/// ```rust,no_run
/// use tauri::{
/// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
/// webview::WebviewWindowBuilder,
/// };
/// use http::header::HeaderValue;
/// use std::collections::HashMap;
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
/// .on_navigation(|url| {
/// // allow the production URL or localhost on dev
/// url.scheme() == "tauri" || (cfg!(dev) && url.host_str() == Some("localhost"))
/// })
/// .build()?;
/// Ok(())
/// });
/// ```
pub fn on_navigation<F: Fn(&Url) -> bool + Send + 'static>(mut self, f: F) -> Self {
self.webview_builder = self.webview_builder.on_navigation(f);
self
@ -318,36 +307,30 @@ tauri::Builder::default()
/// or [`tauri_runtime::webview::PageLoadEvent::Finished`] when the page finishes loading.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust,no_run
use tauri::{
utils::config::{Csp, CspDirectiveSources, WebviewUrl},
window::WindowBuilder,
webview::{PageLoadEvent, WebviewWindowBuilder},
};
use http::header::HeaderValue;
use std::collections::HashMap;
tauri::Builder::default()
.setup(|app| {
let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
.on_page_load(|window, payload| {
match payload.event() {
PageLoadEvent::Started => {
println!("{} finished loading", payload.url());
}
PageLoadEvent::Finished => {
println!("{} finished loading", payload.url());
}
}
})
.build()?;
Ok(())
});
```
"####
)]
/// ```rust,no_run
/// use tauri::{
/// utils::config::{Csp, CspDirectiveSources, WebviewUrl},
/// webview::{PageLoadEvent, WebviewWindowBuilder},
/// };
/// use http::header::HeaderValue;
/// use std::collections::HashMap;
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into()))
/// .on_page_load(|window, payload| {
/// match payload.event() {
/// PageLoadEvent::Started => {
/// println!("{} finished loading", payload.url());
/// }
/// PageLoadEvent::Finished => {
/// println!("{} finished loading", payload.url());
/// }
/// }
/// })
/// .build()?;
/// Ok(())
/// });
/// ```
pub fn on_page_load<F: Fn(WebviewWindow<R>, PageLoadPayload<'_>) + Send + Sync + 'static>(
mut self,
f: F,
@ -776,32 +759,27 @@ impl<'a, R: Runtime, M: Manager<R>> WebviewWindowBuilder<'a, R, M> {
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust
use tauri::{WindowBuilder, Runtime};
const INIT_SCRIPT: &str = r#"
if (window.location.origin === 'https://tauri.app') {
console.log("hello world from js init script");
window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
}
"#;
fn main() {
tauri::Builder::default()
.setup(|app| {
let webview = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
.initialization_script(INIT_SCRIPT)
.build()?;
Ok(())
});
}
```
"####
)]
/// ```rust
/// use tauri::{WebviewWindowBuilder, Runtime};
///
/// const INIT_SCRIPT: &str = r#"
/// if (window.location.origin === 'https://tauri.app') {
/// console.log("hello world from js init script");
///
/// window.__MY_CUSTOM_PROPERTY__ = { foo: 'bar' };
/// }
/// "#;
///
/// fn main() {
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview = tauri::WebviewWindowBuilder::new(app, "label", tauri::WebviewUrl::App("index.html".into()))
/// .initialization_script(INIT_SCRIPT)
/// .build()?;
/// Ok(())
/// });
/// }
/// ```
#[must_use]
pub fn initialization_script(mut self, script: &str) -> Self {
self.webview_builder = self.webview_builder.initialization_script(script);
@ -1003,36 +981,33 @@ impl<R: Runtime> WebviewWindow<R> {
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::menu::{Menu, Submenu, MenuItem};
tauri::Builder::default()
.setup(|app| {
let handle = app.handle();
let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
let menu = Menu::with_items(handle, &[
&Submenu::with_items(handle, "File", true, &[
&save_menu_item,
])?,
])?;
let webview_window = tauri::window::WindowBuilder::new(app, "editor")
.menu(menu)
.build()
.unwrap();
webview_window.on_menu_event(move |window, event| {
if event.id == save_menu_item.id() {
// save menu item
}
});
Ok(())
});
```
"####
)]
/// ```
/// use tauri::menu::{Menu, Submenu, MenuItem};
/// use tauri::{WebviewWindowBuilder, WebviewUrl};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle();
/// let save_menu_item = MenuItem::new(handle, "Save", true, None::<&str>)?;
/// let menu = Menu::with_items(handle, &[
/// &Submenu::with_items(handle, "File", true, &[
/// &save_menu_item,
/// ])?,
/// ])?;
/// let webview_window = WebviewWindowBuilder::new(app, "editor", WebviewUrl::default())
/// .menu(menu)
/// .build()
/// .unwrap();
///
/// webview_window.on_menu_event(move |window, event| {
/// if event.id == save_menu_item.id() {
/// // save menu item
/// }
/// });
///
/// Ok(())
/// });
/// ```
pub fn on_menu_event<F: Fn(&crate::Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
&self,
f: F,
@ -1698,20 +1673,15 @@ impl<R: Runtime> WebviewWindow<R> {
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust,no_run
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
#[cfg(debug_assertions)]
app.get_webview("main").unwrap().open_devtools();
Ok(())
});
```
"####
)]
/// ```rust,no_run
/// use tauri::Manager;
/// tauri::Builder::default()
/// .setup(|app| {
/// #[cfg(debug_assertions)]
/// app.get_webview_window("main").unwrap().open_devtools();
/// Ok(())
/// });
/// ```
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn open_devtools(&self) {
@ -1729,27 +1699,22 @@ tauri::Builder::default()
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust,no_run
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
#[cfg(debug_assertions)]
{
let webview = app.get_webview("main").unwrap();
webview.open_devtools();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(10));
webview.close_devtools();
});
}
Ok(())
});
```
"####
)]
/// ```rust,no_run
/// use tauri::Manager;
/// tauri::Builder::default()
/// .setup(|app| {
/// #[cfg(debug_assertions)]
/// {
/// let webview = app.get_webview_window("main").unwrap();
/// webview.open_devtools();
/// std::thread::spawn(move || {
/// std::thread::sleep(std::time::Duration::from_secs(10));
/// webview.close_devtools();
/// });
/// }
/// Ok(())
/// });
/// ```
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn close_devtools(&self) {
@ -1767,25 +1732,20 @@ tauri::Builder::default()
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```rust,no_run
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
#[cfg(debug_assertions)]
{
let webview = app.get_webview("main").unwrap();
if !webview.is_devtools_open() {
webview.open_devtools();
}
}
Ok(())
});
```
"####
)]
/// ```rust,no_run
/// use tauri::Manager;
/// tauri::Builder::default()
/// .setup(|app| {
/// #[cfg(debug_assertions)]
/// {
/// let webview = app.get_webview_window("main").unwrap();
/// if !webview.is_devtools_open() {
/// webview.open_devtools();
/// }
/// }
/// Ok(())
/// });
/// ```
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(docsrs, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn is_devtools_open(&self) -> bool {
@ -1804,31 +1764,25 @@ tauri::Builder::default()
}
}
/// Event system APIs.
impl<R: Runtime> WebviewWindow<R> {
impl<R: Runtime> Listener<R> for WebviewWindow<R> {
/// Listen to an event on this webview window.
///
/// # Examples
///
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
let webview = app.get_webview("main").unwrap();
webview.listen("component-loaded", move |event| {
println!("window just loaded a component");
});
Ok(())
});
```
"####
)]
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
/// ```
/// use tauri::{Manager, Listener};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = app.get_webview_window("main").unwrap();
/// webview_window.listen("component-loaded", move |event| {
/// println!("window just loaded a component");
/// });
///
/// Ok(())
/// });
/// ```
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
@ -1841,43 +1795,10 @@ tauri::Builder::default()
)
}
/// Unlisten to an event on this webview window.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
let webview = app.get_webview("main").unwrap();
let webview_ = webview.clone();
let handler = webview.listen("component-loaded", move |event| {
println!("webview just loaded a component");
// we no longer need to listen to the event
// we also could have used `webview.once` instead
webview_.unlisten(event.id());
});
// stop listening to the event when you do not need it anymore
webview.unlisten(handler);
Ok(())
});
```
"####
)]
pub fn unlisten(&self, id: EventId) {
self.manager().unlisten(id)
}
/// Listen to an event on this window webview only once.
///
/// See [`Self::listen`] for more information.
pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
@ -1889,6 +1810,108 @@ tauri::Builder::default()
handler,
)
}
/// Unlisten to an event on this webview window.
///
/// # Examples
/// ```
/// use tauri::{Manager, Listener};
///
/// tauri::Builder::default()
/// .setup(|app| {
/// let webview_window = app.get_webview_window("main").unwrap();
/// let webview_window_ = webview_window.clone();
/// let handler = webview_window.listen("component-loaded", move |event| {
/// println!("webview_window just loaded a component");
///
/// // we no longer need to listen to the event
/// // we also could have used `webview_window.once` instead
/// webview_window_.unlisten(event.id());
/// });
///
/// // stop listening to the event when you do not need it anymore
/// webview_window.unlisten(handler);
///
/// Ok(())
/// });
/// ```
fn unlisten(&self, id: EventId) {
self.manager().unlisten(id)
}
}
impl<R: Runtime> Emitter<R> for WebviewWindow<R> {
/// Emits an event to all [targets](EventTarget).
///
/// # Examples
/// ```
/// use tauri::Emitter;
///
/// #[tauri::command]
/// fn synchronize(window: tauri::WebviewWindow) {
/// // emits the synchronized event to all webviews
/// window.emit("synchronized", ());
/// }
/// ```
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.manager().emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(window: tauri::WebviewWindow) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to all listeners
/// window.emit_to(EventTarget::any(), "download-progress", i);
/// // emit an event to listeners that used App::listen or AppHandle::listen
/// window.emit_to(EventTarget::app(), "download-progress", i);
/// // emit an event to any webview/window/webviewWindow matching the given label
/// window.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
/// window.emit_to(EventTarget::labeled("updater"), "download-progress", i);
/// // emit an event to listeners that used WebviewWindow::listen
/// window.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
/// }
/// }
/// ```
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> crate::Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
{
self.manager().emit_to(target, event, payload)
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
/// ```
/// use tauri::{Emitter, EventTarget};
///
/// #[tauri::command]
/// fn download(window: tauri::WebviewWindow) {
/// for i in 1..100 {
/// std::thread::sleep(std::time::Duration::from_millis(150));
/// // emit a download progress event to the updater window
/// window.emit_filter("download-progress", i, |t| match t {
/// EventTarget::WebviewWindow { label } => label == "main",
/// _ => false,
/// });
/// }
/// }
/// ```
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager().emit_filter(event, payload, filter)
}
}
impl<R: Runtime> Manager<R> for WebviewWindow<R> {

View File

@ -28,7 +28,8 @@ use crate::{
sealed::{ManagerBase, RuntimeOrDispatch},
utils::config::{WindowConfig, WindowEffectsConfig},
webview::WebviewBuilder,
EventLoopMessage, Manager, ResourceTable, Runtime, Theme, Webview, WindowEvent,
Emitter, EventLoopMessage, Listener, Manager, ResourceTable, Runtime, Theme, Webview,
WindowEvent,
};
#[cfg(desktop)]
use crate::{
@ -2019,8 +2020,7 @@ pub struct ProgressBarState {
pub progress: Option<u64>,
}
/// Event system APIs.
impl<R: Runtime> Window<R> {
impl<R: Runtime> Listener<R> for Window<R> {
/// Listen to an event on this window.
///
/// # Examples
@ -2028,7 +2028,7 @@ impl<R: Runtime> Window<R> {
feature = "unstable",
doc = r####"
```
use tauri::Manager;
use tauri::{Manager, Listener};
tauri::Builder::default()
.setup(|app| {
@ -2042,7 +2042,7 @@ tauri::Builder::default()
```
"####
)]
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static,
{
@ -2055,6 +2055,22 @@ tauri::Builder::default()
)
}
/// Listen to an event on this window only once.
///
/// See [`Self::listen`] for more information.
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static,
{
self.manager.once(
event.into(),
EventTarget::Window {
label: self.label().to_string(),
},
handler,
)
}
/// Unlisten to an event on this window.
///
/// # Examples
@ -2062,7 +2078,7 @@ tauri::Builder::default()
feature = "unstable",
doc = r####"
```
use tauri::Manager;
use tauri::{Manager, Listener};
tauri::Builder::default()
.setup(|app| {
@ -2084,24 +2100,97 @@ tauri::Builder::default()
```
"####
)]
pub fn unlisten(&self, id: EventId) {
fn unlisten(&self, id: EventId) {
self.manager.unlisten(id)
}
}
/// Listen to an event on this window only once.
impl<R: Runtime> Emitter<R> for Window<R> {
/// Emits an event to all [targets](EventTarget).
///
/// See [`Self::listen`] for more information.
pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::Emitter;
#[tauri::command]
fn synchronize(window: tauri::Window) {
// emits the synchronized event to all webviews
window.emit("synchronized", ());
}
```
"####
)]
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.manager.emit(event, payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::{Emitter, EventTarget};
#[tauri::command]
fn download(window: tauri::Window) {
for i in 1..100 {
std::thread::sleep(std::time::Duration::from_millis(150));
// emit a download progress event to all listeners
window.emit_to(EventTarget::any(), "download-progress", i);
// emit an event to listeners that used App::listen or AppHandle::listen
window.emit_to(EventTarget::app(), "download-progress", i);
// emit an event to any webview/window/webviewWindow matching the given label
window.emit_to("updater", "download-progress", i); // similar to using EventTarget::labeled
window.emit_to(EventTarget::labeled("updater"), "download-progress", i);
// emit an event to listeners that used WebviewWindow::listen
window.emit_to(EventTarget::webview_window("updater"), "download-progress", i);
}
}
```
"####
)]
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> crate::Result<()>
where
F: FnOnce(Event) + Send + 'static,
I: Into<EventTarget>,
S: Serialize + Clone,
{
self.manager.once(
event.into(),
EventTarget::Window {
label: self.label().to_string(),
},
handler,
)
self.manager.emit_to(target, event, payload)
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
/// # Examples
#[cfg_attr(
feature = "unstable",
doc = r####"
```
use tauri::{Emitter, EventTarget};
#[tauri::command]
fn download(window: tauri::Window) {
for i in 1..100 {
std::thread::sleep(std::time::Duration::from_millis(150));
// emit a download progress event to the updater window
window.emit_filter("download-progress", i, |t| match t {
EventTarget::WebviewWindow { label } => label == "main",
_ => false,
});
}
}
```
"####
)]
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool,
{
self.manager.emit_filter(event, payload, filter)
}
}

View File

@ -4035,6 +4035,17 @@ dependencies = [
"syn 2.0.63",
]
[[package]]
name = "windows-implement"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "windows-interface"
version = "0.57.0"
@ -4046,6 +4057,17 @@ dependencies = [
"syn 2.0.63",
]
[[package]]
name = "windows-interface"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "windows-result"
version = "0.1.1"

View File

@ -12,7 +12,7 @@ use serde::Serialize;
use tauri::{
ipc::Channel,
webview::{PageLoadEvent, WebviewWindowBuilder},
App, AppHandle, Manager, RunEvent, Runtime, WebviewUrl,
App, AppHandle, Emitter, Listener, Manager, RunEvent, Runtime, WebviewUrl,
};
use tauri_plugin_sample::{PingRequest, SampleExt};

View File

@ -4,7 +4,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{webview::PageLoadEvent, WebviewWindowBuilder};
use tauri::{webview::PageLoadEvent, Listener, WebviewWindowBuilder};
use tauri_utils::acl::ExecutionContext;
fn main() {

View File

@ -4,7 +4,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{webview::PageLoadEvent, WebviewUrl, WebviewWindowBuilder};
use tauri::{webview::PageLoadEvent, Listener, WebviewUrl, WebviewWindowBuilder};
use tauri_utils::acl::ExecutionContext;
fn main() {