mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-09-11 07:55:34 +03:00
refactor: enhance event system rust apis (#7996)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
198abe3c2c
commit
93c8a77b34
12
.changes/tauri-event-system-apis.md
Normal file
12
.changes/tauri-event-system-apis.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
'tauri': 'major:breaking'
|
||||
---
|
||||
|
||||
The event system APIS on Rust is recieving a few changes for consistency and quality of life improvements:
|
||||
|
||||
- Renamed `Manager::emit_all` to just `Manager::emit` and will now both trigger the events on JS side as well as Rust.
|
||||
- Removed `Manager::trigger_global`, use `Manager::emit`
|
||||
- Added `Manager::emit_filter`.
|
||||
- Removed `Window::emit`, and moved the implementation to `Manager::emit`.
|
||||
- Removed `Window::emit_and_trigger` and `Window::trigger`, use `Window::emit` instead.
|
||||
- Changed `Window::emit_to` to only trigger the target window listeners so it won't be catched by `Manager::listen_global`
|
@ -246,7 +246,7 @@ impl<R: Runtime> GlobalWindowEvent<R> {
|
||||
&self.event
|
||||
}
|
||||
|
||||
/// The window that the menu belongs to.
|
||||
/// The window that the event belongs to.
|
||||
pub fn window(&self) -> &Window<R> {
|
||||
&self.window
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::{Event, EventId};
|
||||
use crate::{Runtime, Window};
|
||||
|
||||
use super::{EmitArgs, Event, EventId};
|
||||
|
||||
use std::{
|
||||
boxed::Box,
|
||||
@ -15,33 +17,33 @@ use std::{
|
||||
};
|
||||
|
||||
/// What to do with the pending handler when resolving it?
|
||||
enum Pending {
|
||||
enum Pending<R: Runtime> {
|
||||
Unlisten(EventId),
|
||||
Listen(EventId, String, Handler),
|
||||
Trigger(String, Option<String>, Option<String>),
|
||||
Listen(EventId, String, Handler<R>),
|
||||
Emit(EmitArgs),
|
||||
}
|
||||
|
||||
/// Stored in [`Listeners`] to be called upon when the event that stored it is triggered.
|
||||
struct Handler {
|
||||
window: Option<String>,
|
||||
struct Handler<R: Runtime> {
|
||||
window: Option<Window<R>>,
|
||||
callback: Box<dyn Fn(Event) + Send>,
|
||||
}
|
||||
|
||||
/// Holds event handlers and pending event handlers, along with the salts associating them.
|
||||
struct InnerListeners {
|
||||
handlers: Mutex<HashMap<String, HashMap<EventId, Handler>>>,
|
||||
pending: Mutex<Vec<Pending>>,
|
||||
struct InnerListeners<R: Runtime> {
|
||||
handlers: Mutex<HashMap<String, HashMap<EventId, Handler<R>>>>,
|
||||
pending: Mutex<Vec<Pending<R>>>,
|
||||
function_name: &'static str,
|
||||
listeners_object_name: &'static str,
|
||||
next_event_id: Arc<AtomicU32>,
|
||||
}
|
||||
|
||||
/// A self-contained event manager.
|
||||
pub struct Listeners {
|
||||
inner: Arc<InnerListeners>,
|
||||
pub struct Listeners<R: Runtime> {
|
||||
inner: Arc<InnerListeners<R>>,
|
||||
}
|
||||
|
||||
impl Default for Listeners {
|
||||
impl<R: Runtime> Default for Listeners<R> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(InnerListeners {
|
||||
@ -55,7 +57,7 @@ impl Default for Listeners {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Listeners {
|
||||
impl<R: Runtime> Clone for Listeners<R> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
@ -63,7 +65,7 @@ impl Clone for Listeners {
|
||||
}
|
||||
}
|
||||
|
||||
impl Listeners {
|
||||
impl<R: Runtime> Listeners<R> {
|
||||
pub(crate) fn next_event_id(&self) -> EventId {
|
||||
self.inner.next_event_id.fetch_add(1, Ordering::Relaxed)
|
||||
}
|
||||
@ -79,7 +81,7 @@ impl Listeners {
|
||||
}
|
||||
|
||||
/// Insert a pending event action to the queue.
|
||||
fn insert_pending(&self, action: Pending) {
|
||||
fn insert_pending(&self, action: Pending<R>) {
|
||||
self
|
||||
.inner
|
||||
.pending
|
||||
@ -89,7 +91,7 @@ impl Listeners {
|
||||
}
|
||||
|
||||
/// Finish all pending event actions.
|
||||
fn flush_pending(&self) {
|
||||
fn flush_pending(&self) -> crate::Result<()> {
|
||||
let pending = {
|
||||
let mut lock = self
|
||||
.inner
|
||||
@ -102,13 +104,17 @@ impl Listeners {
|
||||
for action in pending {
|
||||
match action {
|
||||
Pending::Unlisten(id) => self.unlisten(id),
|
||||
Pending::Listen(id, event, handler) => self.listen_(id, event, handler),
|
||||
Pending::Trigger(ref event, window, payload) => self.trigger(event, window, payload),
|
||||
Pending::Listen(id, event, handler) => self.listen_with_id(id, event, handler),
|
||||
Pending::Emit(args) => {
|
||||
self.emit(&args)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn listen_(&self, id: EventId, event: String, handler: Handler) {
|
||||
fn listen_with_id(&self, id: EventId, event: String, handler: Handler<R>) {
|
||||
match self.inner.handlers.try_lock() {
|
||||
Err(_) => self.insert_pending(Pending::Listen(id, event, handler)),
|
||||
Ok(mut lock) => {
|
||||
@ -117,11 +123,11 @@ impl Listeners {
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an event listener for JS events.
|
||||
/// Adds an event listener.
|
||||
pub(crate) fn listen<F: Fn(Event) + Send + 'static>(
|
||||
&self,
|
||||
event: String,
|
||||
window: Option<String>,
|
||||
window: Option<Window<R>>,
|
||||
handler: F,
|
||||
) -> EventId {
|
||||
let id = self.next_event_id();
|
||||
@ -130,16 +136,16 @@ impl Listeners {
|
||||
callback: Box::new(handler),
|
||||
};
|
||||
|
||||
self.listen_(id, event, handler);
|
||||
self.listen_with_id(id, event, handler);
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
/// Listen to a JS event and immediately unlisten.
|
||||
/// Listen to an event and immediately unlisten.
|
||||
pub(crate) fn once<F: FnOnce(Event) + Send + 'static>(
|
||||
&self,
|
||||
event: String,
|
||||
window: Option<String>,
|
||||
window: Option<Window<R>>,
|
||||
handler: F,
|
||||
) {
|
||||
let self_ = self.clone();
|
||||
@ -164,19 +170,42 @@ impl Listeners {
|
||||
}
|
||||
}
|
||||
|
||||
/// Triggers the given global event with its payload.
|
||||
pub(crate) fn trigger(&self, event: &str, window: Option<String>, payload: Option<String>) {
|
||||
/// Emits the given event with its payload based on a filter.
|
||||
pub(crate) fn emit_filter<F>(&self, emit_args: &EmitArgs, filter: Option<F>) -> crate::Result<()>
|
||||
where
|
||||
F: Fn(&Window<R>) -> bool,
|
||||
{
|
||||
let mut maybe_pending = false;
|
||||
match self.inner.handlers.try_lock() {
|
||||
Err(_) => self.insert_pending(Pending::Trigger(event.to_owned(), window, payload)),
|
||||
Err(_) => self.insert_pending(Pending::Emit(emit_args.clone())),
|
||||
Ok(lock) => {
|
||||
if let Some(handlers) = lock.get(event) {
|
||||
for (&id, handler) in handlers {
|
||||
if handler.window.is_none() || window == handler.window {
|
||||
if let Some(handlers) = lock.get(&emit_args.event_name) {
|
||||
let handlers = if let Some(filter) = filter {
|
||||
handlers
|
||||
.iter()
|
||||
.filter(|h| {
|
||||
h.1
|
||||
.window
|
||||
.as_ref()
|
||||
.map(|w| {
|
||||
// clippy sees this as redundant closure but
|
||||
// fixing it will result in a compiler error
|
||||
#[allow(clippy::redundant_closure)]
|
||||
filter(w)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
handlers.iter().collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
if !handlers.is_empty() {
|
||||
for (&id, handler) in handlers {
|
||||
maybe_pending = true;
|
||||
(handler.callback)(self::Event {
|
||||
id,
|
||||
data: payload.clone(),
|
||||
data: emit_args.payload.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -185,14 +214,22 @@ impl Listeners {
|
||||
}
|
||||
|
||||
if maybe_pending {
|
||||
self.flush_pending();
|
||||
self.flush_pending()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emits the given event with its payload.
|
||||
pub(crate) fn emit(&self, emit_args: &EmitArgs) -> crate::Result<()> {
|
||||
self.emit_filter(emit_args, None::<&dyn Fn(&Window<R>) -> bool>)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::test::MockRuntime;
|
||||
use proptest::prelude::*;
|
||||
|
||||
// dummy event handler function
|
||||
@ -206,7 +243,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 = Default::default();
|
||||
let listeners: Listeners<MockRuntime> = Default::default();
|
||||
// clone e as the key
|
||||
let key = e.clone();
|
||||
// pass e and an dummy func into listen
|
||||
@ -222,7 +259,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 = Default::default();
|
||||
let listeners: Listeners<MockRuntime> = Default::default();
|
||||
// clone e as the key
|
||||
let key = e.clone();
|
||||
// pass e and an dummy func into listen
|
||||
@ -248,11 +285,11 @@ mod test {
|
||||
// check to see if on_event properly grabs the stored function from listen.
|
||||
#[test]
|
||||
fn check_on_event(key in "[a-z]+", d in "[a-z]+") {
|
||||
let listeners: Listeners = Default::default();
|
||||
// call listen with e and the event_fn dummy func
|
||||
let listeners: Listeners<MockRuntime> = Default::default();
|
||||
// call listen with key and the event_fn dummy func
|
||||
listeners.listen(key.clone(), None, event_fn);
|
||||
// call on event with e and d.
|
||||
listeners.trigger(&key, None, Some(d));
|
||||
// call on event with key and d.
|
||||
listeners.emit(&EmitArgs { event_name: key.clone(), event: serde_json::to_string(&key).unwrap(), source_window_label: "null".into(), payload: serde_json::to_string(&d).unwrap() })?;
|
||||
|
||||
// lock the mutex
|
||||
let l = listeners.inner.handlers.lock().unwrap();
|
||||
|
@ -5,6 +5,7 @@
|
||||
mod listener;
|
||||
pub(crate) mod plugin;
|
||||
pub(crate) use listener::Listeners;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Checks if an event name is valid.
|
||||
pub fn is_event_name_valid(event: &str) -> bool {
|
||||
@ -23,11 +24,39 @@ pub fn assert_event_name_is_valid(event: &str) {
|
||||
/// Unique id of an event.
|
||||
pub type EventId = u32;
|
||||
|
||||
/// An event that was triggered.
|
||||
/// Serialized emit arguments.
|
||||
#[derive(Clone)]
|
||||
pub struct EmitArgs {
|
||||
/// Raw event name.
|
||||
pub event_name: String,
|
||||
/// Serialized event name.
|
||||
pub event: String,
|
||||
/// Serialized source window label ("null" for global events)
|
||||
pub source_window_label: String,
|
||||
/// Serialized payload.
|
||||
pub payload: String,
|
||||
}
|
||||
|
||||
impl EmitArgs {
|
||||
pub fn from<S: Serialize>(
|
||||
event: &str,
|
||||
source_window_label: Option<&str>,
|
||||
payload: S,
|
||||
) -> crate::Result<Self> {
|
||||
Ok(EmitArgs {
|
||||
event_name: event.into(),
|
||||
event: serde_json::to_string(event)?,
|
||||
source_window_label: serde_json::to_string(&source_window_label)?,
|
||||
payload: serde_json::to_string(&payload)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An event that was emitted.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Event {
|
||||
id: EventId,
|
||||
data: Option<String>,
|
||||
data: String,
|
||||
}
|
||||
|
||||
impl Event {
|
||||
@ -37,27 +66,11 @@ impl Event {
|
||||
}
|
||||
|
||||
/// The event payload.
|
||||
pub fn payload(&self) -> Option<&str> {
|
||||
self.data.as_deref()
|
||||
pub fn payload(&self) -> &str {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unlisten_js(listeners_object_name: &str, event_name: &str, event_id: EventId) -> String {
|
||||
format!(
|
||||
"
|
||||
(function () {{
|
||||
const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}']
|
||||
if (listeners) {{
|
||||
const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id})
|
||||
if (index > -1) {{
|
||||
window['{listeners_object_name}']['{event_name}'].splice(index, 1)
|
||||
}}
|
||||
}}
|
||||
}})()
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn listen_js(
|
||||
listeners_object_name: &str,
|
||||
event: &str,
|
||||
@ -92,3 +105,52 @@ pub fn listen_js(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn emit_js(event_emit_function_name: &str, emit_args: &EmitArgs) -> crate::Result<String> {
|
||||
Ok(format!(
|
||||
"(function () {{ const fn = window['{}']; fn && fn({{event: {}, windowLabel: {}, payload: {}}}) }})()",
|
||||
event_emit_function_name,
|
||||
emit_args.event,
|
||||
emit_args.source_window_label,
|
||||
emit_args.payload
|
||||
))
|
||||
}
|
||||
|
||||
pub fn unlisten_js(listeners_object_name: &str, event_name: &str, event_id: EventId) -> String {
|
||||
format!(
|
||||
"
|
||||
(function () {{
|
||||
const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}']
|
||||
if (listeners) {{
|
||||
const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id})
|
||||
if (index > -1) {{
|
||||
window['{listeners_object_name}']['{event_name}'].splice(index, 1)
|
||||
}}
|
||||
}}
|
||||
}})()
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn event_initialization_script(function: &str, listeners: &str) -> String {
|
||||
format!(
|
||||
"
|
||||
Object.defineProperty(window, '{function}', {{
|
||||
value: function (eventData) {{
|
||||
const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
|
||||
|
||||
for (let i = listeners.length - 1; i >= 0; i--) {{
|
||||
const listener = listeners[i]
|
||||
if (
|
||||
(listener.windowLabel && listener.windowLabel === eventData.windowLabel) ||
|
||||
(!listener.windowLabel && (listener.windowLabel === null || eventData.windowLabel === null))
|
||||
) {{
|
||||
eventData.id = listener.id
|
||||
listener.handler(eventData)
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}});
|
||||
"
|
||||
)
|
||||
}
|
||||
|
@ -37,6 +37,12 @@ impl<'de> Deserialize<'de> for EventName {
|
||||
|
||||
pub struct WindowLabel(String);
|
||||
|
||||
impl AsRef<str> for WindowLabel {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for WindowLabel {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
@ -75,25 +81,10 @@ pub fn emit<R: Runtime>(
|
||||
window_label: Option<WindowLabel>,
|
||||
payload: Option<JsonValue>,
|
||||
) -> Result<()> {
|
||||
// dispatch the event to Rust listeners
|
||||
window.trigger(
|
||||
&event.0,
|
||||
payload.as_ref().and_then(|p| {
|
||||
serde_json::to_string(&p)
|
||||
.map_err(|e| {
|
||||
#[cfg(debug_assertions)]
|
||||
eprintln!("{e}");
|
||||
e
|
||||
})
|
||||
.ok()
|
||||
}),
|
||||
);
|
||||
|
||||
// emit event to JS
|
||||
if let Some(target) = window_label {
|
||||
window.emit_to(&target.0, &event.0, payload)
|
||||
if let Some(label) = window_label {
|
||||
window.emit_filter(&event.0, payload, |w| label.as_ref() == w.label())
|
||||
} else {
|
||||
window.emit_all(&event.0, payload)
|
||||
window.emit(&event.0, payload)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,67 +546,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
|
||||
self.manager().package_info()
|
||||
}
|
||||
|
||||
/// Emits an event to all windows.
|
||||
///
|
||||
/// Only the webviews receives this event.
|
||||
/// To trigger Rust listeners, use [`Self::trigger_global`], [`Window::trigger`] or [`Window::emit_and_trigger`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[tauri::command]
|
||||
/// fn synchronize(app: tauri::AppHandle) {
|
||||
/// // emits the synchronized event to all windows
|
||||
/// app.emit_all("synchronized", ());
|
||||
/// }
|
||||
/// ```
|
||||
fn emit_all<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
|
||||
self.manager().emit_filter(event, None, payload, |_| true)
|
||||
}
|
||||
|
||||
/// Emits an event to windows matching the filter critera.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[tauri::command]
|
||||
/// fn synchronize(app: tauri::AppHandle) {
|
||||
/// // emits the synchronized event to all windows
|
||||
/// app.emit_filter("synchronized", (), |w| w.label().starts_with("foo-"));
|
||||
/// }
|
||||
/// ```
|
||||
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
|
||||
where
|
||||
S: Serialize + Clone,
|
||||
F: Fn(&Window<R>) -> bool,
|
||||
{
|
||||
self.manager().emit_filter(event, None, payload, filter)
|
||||
}
|
||||
|
||||
/// Emits an event to the window with the specified label.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[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_to("updater", "download-progress", i);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn emit_to<S: Serialize + Clone>(&self, label: &str, event: &str, payload: S) -> Result<()> {
|
||||
self
|
||||
.manager()
|
||||
.emit_filter(event, None, payload, |w| label == w.label())
|
||||
}
|
||||
|
||||
/// Listen to a event triggered on any window ([`Window::trigger`] or [`Window::emit_and_trigger`]) or with [`Self::trigger_global`].
|
||||
/// Listen to an event emitted on any window.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
@ -615,7 +555,7 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
|
||||
/// #[tauri::command]
|
||||
/// fn synchronize(window: tauri::Window) {
|
||||
/// // emits the synchronized event to all windows
|
||||
/// window.emit_and_trigger("synchronized", ());
|
||||
/// window.emit("synchronized", ());
|
||||
/// }
|
||||
///
|
||||
/// tauri::Builder::default()
|
||||
@ -634,40 +574,6 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
|
||||
self.manager().listen(event.into(), None, handler)
|
||||
}
|
||||
|
||||
/// Listen to a global event only once.
|
||||
///
|
||||
/// See [`Self::listen_global`] for more information.
|
||||
fn once_global<F>(&self, event: impl Into<String>, handler: F)
|
||||
where
|
||||
F: FnOnce(Event) + Send + 'static,
|
||||
{
|
||||
self.manager().once(event.into(), None, handler)
|
||||
}
|
||||
|
||||
/// Trigger a global event to Rust listeners.
|
||||
/// To send the events to the webview, see [`Self::emit_all`] and [`Self::emit_to`].
|
||||
/// To trigger listeners registed on an specific window, see [`Window::trigger`].
|
||||
/// To trigger all listeners, see [`Window::emit_and_trigger`].
|
||||
///
|
||||
/// A global event does not have a source or target window attached.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[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 registed in Rust
|
||||
/// app.trigger_global("download-progress", Some(i.to_string()));
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn trigger_global(&self, event: &str, data: Option<String>) {
|
||||
self.manager().trigger(event, None, data)
|
||||
}
|
||||
|
||||
/// Remove an event listener.
|
||||
///
|
||||
/// # Examples
|
||||
@ -696,6 +602,82 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
|
||||
self.manager().unlisten(id)
|
||||
}
|
||||
|
||||
/// Listen to a global event only once.
|
||||
///
|
||||
/// See [`Self::listen_global`] for more information.
|
||||
fn once_global<F>(&self, event: impl Into<String>, handler: F)
|
||||
where
|
||||
F: FnOnce(Event) + Send + 'static,
|
||||
{
|
||||
self.manager().once(event.into(), None, handler)
|
||||
}
|
||||
|
||||
/// Emits an event to all windows.
|
||||
///
|
||||
/// If using [`Window`] to emit the event, it will be used as the source.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[tauri::command]
|
||||
/// fn synchronize(app: tauri::AppHandle) {
|
||||
/// // emits the synchronized event to all windows
|
||||
/// app.emit("synchronized", ());
|
||||
/// }
|
||||
/// ```
|
||||
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
|
||||
self.manager().emit(event, None, payload)
|
||||
}
|
||||
|
||||
/// Emits an event to the window with the specified label.
|
||||
///
|
||||
/// If using [`Window`] to emit the event, it will be used as the source.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[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_to("updater", "download-progress", i);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn emit_to<S: Serialize + Clone>(&self, label: &str, event: &str, payload: S) -> Result<()> {
|
||||
self
|
||||
.manager()
|
||||
.emit_filter(event, None, payload, |w| label == w.label())
|
||||
}
|
||||
|
||||
/// Emits an event to specific windows based on a filter.
|
||||
///
|
||||
/// If using [`Window`] to emit the event, it will be used as the source.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[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, |w| w.label() == "main" );
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
|
||||
where
|
||||
S: Serialize + Clone,
|
||||
F: Fn(&Window<R>) -> bool,
|
||||
{
|
||||
self.manager().emit_filter(event, None, payload, filter)
|
||||
}
|
||||
|
||||
/// Fetch a single window from the manager.
|
||||
fn get_window(&self, label: &str) -> Option<Window<R>> {
|
||||
self.manager().get_window(label)
|
||||
|
@ -27,7 +27,7 @@ use tauri_utils::{
|
||||
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
|
||||
};
|
||||
|
||||
use crate::window::WindowEmitArgs;
|
||||
use crate::event::EmitArgs;
|
||||
use crate::{
|
||||
app::{
|
||||
AppHandle, GlobalWindowEvent, GlobalWindowEventListener, OnPageLoad, PageLoadPayload,
|
||||
@ -225,7 +225,7 @@ fn replace_csp_nonce(
|
||||
pub struct InnerWindowManager<R: Runtime> {
|
||||
pub(crate) windows: Mutex<HashMap<String, Window<R>>>,
|
||||
pub(crate) plugins: Mutex<PluginStore<R>>,
|
||||
listeners: Listeners,
|
||||
listeners: Listeners<R>,
|
||||
pub(crate) state: Arc<StateManager>,
|
||||
|
||||
/// The JS message handler.
|
||||
@ -847,7 +847,10 @@ impl<R: Runtime> WindowManager<R> {
|
||||
}
|
||||
.render_default(&Default::default())?
|
||||
.into_string(),
|
||||
event_initialization_script: &self.event_initialization_script(),
|
||||
event_initialization_script: &crate::event::event_initialization_script(
|
||||
self.listeners().function_name(),
|
||||
self.listeners().listeners_object_name(),
|
||||
),
|
||||
plugin_initialization_script,
|
||||
freeze_prototype,
|
||||
}
|
||||
@ -856,29 +859,7 @@ impl<R: Runtime> WindowManager<R> {
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn event_initialization_script(&self) -> String {
|
||||
format!(
|
||||
"
|
||||
Object.defineProperty(window, '{function}', {{
|
||||
value: function (eventData) {{
|
||||
const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || []
|
||||
|
||||
for (let i = listeners.length - 1; i >= 0; i--) {{
|
||||
const listener = listeners[i]
|
||||
if (listener.windowLabel === null || eventData.windowLabel === null || listener.windowLabel === eventData.windowLabel) {{
|
||||
eventData.id = listener.id
|
||||
listener.handler(eventData)
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
}});
|
||||
",
|
||||
function = self.listeners().function_name(),
|
||||
listeners = self.listeners().listeners_object_name()
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn listeners(&self) -> &Listeners {
|
||||
pub(crate) fn listeners(&self) -> &Listeners<R> {
|
||||
&self.inner.listeners
|
||||
}
|
||||
}
|
||||
@ -1146,26 +1127,6 @@ impl<R: Runtime> WindowManager<R> {
|
||||
self.windows_lock().remove(label);
|
||||
}
|
||||
|
||||
pub fn emit_filter<S, F>(
|
||||
&self,
|
||||
event: &str,
|
||||
source_window_label: Option<&str>,
|
||||
payload: S,
|
||||
filter: F,
|
||||
) -> crate::Result<()>
|
||||
where
|
||||
S: Serialize + Clone,
|
||||
F: Fn(&Window<R>) -> bool,
|
||||
{
|
||||
let emit_args = WindowEmitArgs::from(event, source_window_label, payload)?;
|
||||
assert_event_name_is_valid(event);
|
||||
self
|
||||
.windows()
|
||||
.values()
|
||||
.filter(|&w| filter(w))
|
||||
.try_for_each(|window| window.emit_internal(&emit_args))
|
||||
}
|
||||
|
||||
pub fn eval_script_all<S: Into<String>>(&self, script: S) -> crate::Result<()> {
|
||||
let script = script.into();
|
||||
self
|
||||
@ -1186,21 +1147,20 @@ impl<R: Runtime> WindowManager<R> {
|
||||
&self.inner.package_info
|
||||
}
|
||||
|
||||
pub fn trigger(&self, event: &str, window: Option<String>, data: Option<String>) {
|
||||
assert_event_name_is_valid(event);
|
||||
self.listeners().trigger(event, window, data)
|
||||
}
|
||||
|
||||
pub fn listen<F: Fn(Event) + Send + 'static>(
|
||||
&self,
|
||||
event: String,
|
||||
window: Option<String>,
|
||||
window: Option<Window<R>>,
|
||||
handler: F,
|
||||
) -> EventId {
|
||||
assert_event_name_is_valid(&event);
|
||||
self.listeners().listen(event, window, handler)
|
||||
}
|
||||
|
||||
pub fn unlisten(&self, id: EventId) {
|
||||
self.listeners().unlisten(id)
|
||||
}
|
||||
|
||||
pub fn once<F: FnOnce(Event) + Send + 'static>(
|
||||
&self,
|
||||
event: String,
|
||||
@ -1208,19 +1168,63 @@ impl<R: Runtime> WindowManager<R> {
|
||||
handler: F,
|
||||
) {
|
||||
assert_event_name_is_valid(&event);
|
||||
self.listeners().once(event, window, handler)
|
||||
self
|
||||
.listeners()
|
||||
.once(event, window.and_then(|w| self.get_window(&w)), handler)
|
||||
}
|
||||
|
||||
pub fn event_listeners_object_name(&self) -> &str {
|
||||
self.inner.listeners.listeners_object_name()
|
||||
pub fn emit_filter<S, F>(
|
||||
&self,
|
||||
event: &str,
|
||||
source_window_label: Option<&str>,
|
||||
payload: S,
|
||||
filter: F,
|
||||
) -> crate::Result<()>
|
||||
where
|
||||
S: Serialize + Clone,
|
||||
F: Fn(&Window<R>) -> bool,
|
||||
{
|
||||
assert_event_name_is_valid(event);
|
||||
|
||||
let emit_args = EmitArgs::from(event, source_window_label, payload)?;
|
||||
|
||||
self
|
||||
.windows_lock()
|
||||
.values()
|
||||
.filter(|w| {
|
||||
w.has_js_listener(None, event)
|
||||
|| w.has_js_listener(source_window_label.map(Into::into), event)
|
||||
})
|
||||
.filter(|w| filter(w))
|
||||
.try_for_each(|window| window.emit_js(&emit_args))?;
|
||||
|
||||
self.listeners().emit_filter(&emit_args, Some(filter))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn event_emit_function_name(&self) -> &str {
|
||||
self.inner.listeners.function_name()
|
||||
}
|
||||
pub fn emit<S: Serialize + Clone>(
|
||||
&self,
|
||||
event: &str,
|
||||
source_window_label: Option<&str>,
|
||||
payload: S,
|
||||
) -> crate::Result<()> {
|
||||
assert_event_name_is_valid(event);
|
||||
|
||||
pub fn unlisten(&self, id: EventId) {
|
||||
self.listeners().unlisten(id)
|
||||
let emit_args = EmitArgs::from(event, source_window_label, payload)?;
|
||||
|
||||
self
|
||||
.windows_lock()
|
||||
.values()
|
||||
.filter(|w| {
|
||||
w.has_js_listener(None, event)
|
||||
|| w.has_js_listener(source_window_label.map(Into::into), event)
|
||||
})
|
||||
.try_for_each(|window| window.emit_js(&emit_args))?;
|
||||
|
||||
self.listeners().emit(&emit_args)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_window(&self, label: &str) -> Option<Window<R>> {
|
||||
@ -1346,10 +1350,25 @@ mod tests {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{generate_context, plugin::PluginStore, StateManager, Wry};
|
||||
use std::{
|
||||
sync::mpsc::{channel, Receiver, Sender},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
generate_context,
|
||||
plugin::PluginStore,
|
||||
test::{mock_app, MockRuntime},
|
||||
App, Manager, StateManager, Window, WindowBuilder, Wry,
|
||||
};
|
||||
|
||||
use super::WindowManager;
|
||||
|
||||
const WINDOW_LISTEN_ID: &str = "Window::listen";
|
||||
const WINDOW_LISTEN_GLOBAL_ID: &str = "Window::listen_global";
|
||||
const APP_LISTEN_GLOBAL_ID: &str = "App::listen_global";
|
||||
const TEST_EVENT_NAME: &str = "event";
|
||||
|
||||
#[test]
|
||||
fn check_get_url() {
|
||||
let context = generate_context!("test/fixture/src-tauri/tauri.conf.json", crate);
|
||||
@ -1380,4 +1399,164 @@ mod test {
|
||||
#[cfg(dev)]
|
||||
assert_eq!(manager.get_url().to_string(), "http://localhost:4000/");
|
||||
}
|
||||
|
||||
struct EventSetup {
|
||||
app: App<MockRuntime>,
|
||||
window: Window<MockRuntime>,
|
||||
tx: Sender<(&'static str, String)>,
|
||||
rx: Receiver<(&'static str, String)>,
|
||||
}
|
||||
|
||||
fn setup_events() -> EventSetup {
|
||||
let app = mock_app();
|
||||
let window = WindowBuilder::new(&app, "main", Default::default())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let tx_ = tx.clone();
|
||||
window.listen(TEST_EVENT_NAME, move |evt| {
|
||||
tx_
|
||||
.send((
|
||||
WINDOW_LISTEN_ID,
|
||||
serde_json::from_str::<String>(evt.payload()).unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
let tx_ = tx.clone();
|
||||
window.listen_global(TEST_EVENT_NAME, move |evt| {
|
||||
tx_
|
||||
.send((
|
||||
WINDOW_LISTEN_GLOBAL_ID,
|
||||
serde_json::from_str::<String>(evt.payload()).unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
let tx_ = tx.clone();
|
||||
app.listen_global(TEST_EVENT_NAME, move |evt| {
|
||||
tx_
|
||||
.send((
|
||||
APP_LISTEN_GLOBAL_ID,
|
||||
serde_json::from_str::<String>(evt.payload()).unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
EventSetup {
|
||||
app,
|
||||
window,
|
||||
tx,
|
||||
rx,
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_events(received: &[&str], expected: &[&str]) {
|
||||
for e in expected {
|
||||
assert!(received.contains(e), "{e} did not receive global event");
|
||||
}
|
||||
assert_eq!(
|
||||
received.len(),
|
||||
expected.len(),
|
||||
"received {:?} events but expected {:?}",
|
||||
received,
|
||||
expected
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_global_events() {
|
||||
let EventSetup {
|
||||
app,
|
||||
window: _,
|
||||
tx: _,
|
||||
rx,
|
||||
} = setup_events();
|
||||
|
||||
let mut received = Vec::new();
|
||||
let payload = "global-payload";
|
||||
app.emit(TEST_EVENT_NAME, payload).unwrap();
|
||||
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
||||
assert_eq!(p, payload);
|
||||
received.push(source);
|
||||
}
|
||||
assert_events(
|
||||
&received,
|
||||
&[
|
||||
WINDOW_LISTEN_ID,
|
||||
WINDOW_LISTEN_GLOBAL_ID,
|
||||
APP_LISTEN_GLOBAL_ID,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_global_events() {
|
||||
let EventSetup {
|
||||
app: _,
|
||||
window,
|
||||
tx: _,
|
||||
rx,
|
||||
} = setup_events();
|
||||
|
||||
let mut received = Vec::new();
|
||||
let payload = "global-payload";
|
||||
window.emit(TEST_EVENT_NAME, payload).unwrap();
|
||||
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
||||
assert_eq!(p, payload);
|
||||
received.push(source);
|
||||
}
|
||||
assert_events(
|
||||
&received,
|
||||
&[
|
||||
WINDOW_LISTEN_ID,
|
||||
WINDOW_LISTEN_GLOBAL_ID,
|
||||
APP_LISTEN_GLOBAL_ID,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_local_events() {
|
||||
let EventSetup {
|
||||
app,
|
||||
window,
|
||||
tx,
|
||||
rx,
|
||||
} = setup_events();
|
||||
|
||||
let mut received = Vec::new();
|
||||
let payload = "global-payload";
|
||||
window
|
||||
.emit_to(window.label(), TEST_EVENT_NAME, payload)
|
||||
.unwrap();
|
||||
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
||||
assert_eq!(p, payload);
|
||||
received.push(source);
|
||||
}
|
||||
assert_events(&received, &[WINDOW_LISTEN_ID]);
|
||||
|
||||
received.clear();
|
||||
let other_window_listen_id = "OtherWindow::listen";
|
||||
let other_window = WindowBuilder::new(&app, "other", Default::default())
|
||||
.build()
|
||||
.unwrap();
|
||||
other_window.listen(TEST_EVENT_NAME, move |evt| {
|
||||
tx.send((
|
||||
other_window_listen_id,
|
||||
serde_json::from_str::<String>(evt.payload()).unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
});
|
||||
window
|
||||
.emit_to(other_window.label(), TEST_EVENT_NAME, payload)
|
||||
.unwrap();
|
||||
while let Ok((source, p)) = rx.recv_timeout(Duration::from_secs(1)) {
|
||||
assert_eq!(p, payload);
|
||||
received.push(source);
|
||||
}
|
||||
assert_events(&received, &[other_window_listen_id]);
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ impl Scope {
|
||||
self.event_listeners.lock().unwrap().remove(&id);
|
||||
}
|
||||
|
||||
fn trigger(&self, event: Event) {
|
||||
fn emit(&self, event: Event) {
|
||||
let listeners = self.event_listeners.lock().unwrap();
|
||||
let handlers = listeners.values();
|
||||
for listener in handlers {
|
||||
@ -204,7 +204,7 @@ impl Scope {
|
||||
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
||||
})?;
|
||||
}
|
||||
self.trigger(Event::PathAllowed(path.to_path_buf()));
|
||||
self.emit(Event::PathAllowed(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ impl Scope {
|
||||
path,
|
||||
escaped_pattern,
|
||||
)?;
|
||||
self.trigger(Event::PathAllowed(path.to_path_buf()));
|
||||
self.emit(Event::PathAllowed(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -237,7 +237,7 @@ impl Scope {
|
||||
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
||||
})?;
|
||||
}
|
||||
self.trigger(Event::PathForbidden(path.to_path_buf()));
|
||||
self.emit(Event::PathForbidden(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -251,7 +251,7 @@ impl Scope {
|
||||
path,
|
||||
escaped_pattern,
|
||||
)?;
|
||||
self.trigger(Event::PathForbidden(path.to_path_buf()));
|
||||
self.emit(Event::PathForbidden(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ use crate::TitleBarStyle;
|
||||
use crate::{
|
||||
app::{AppHandle, UriSchemeResponder},
|
||||
command::{CommandArg, CommandItem},
|
||||
event::{Event, EventId},
|
||||
event::{EmitArgs, Event, EventId},
|
||||
ipc::{
|
||||
CallbackFn, Invoke, InvokeBody, InvokeError, InvokeMessage, InvokeResolver,
|
||||
OwnedInvokeResponder,
|
||||
@ -71,26 +71,6 @@ struct WindowCreatedEvent {
|
||||
label: String,
|
||||
}
|
||||
|
||||
pub(crate) struct WindowEmitArgs {
|
||||
pub event: String,
|
||||
pub source_window_label: String,
|
||||
pub payload: String,
|
||||
}
|
||||
|
||||
impl WindowEmitArgs {
|
||||
pub fn from<S: Serialize>(
|
||||
event: &str,
|
||||
source_window_label: Option<&str>,
|
||||
payload: S,
|
||||
) -> crate::Result<Self> {
|
||||
Ok(WindowEmitArgs {
|
||||
event: serde_json::to_string(event)?,
|
||||
source_window_label: serde_json::to_string(&source_window_label)?,
|
||||
payload: serde_json::to_string(&payload)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Monitor descriptor.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@ -976,6 +956,11 @@ impl<R: Runtime> PartialEq for Window<R> {
|
||||
}
|
||||
|
||||
impl<R: Runtime> Manager<R> for Window<R> {
|
||||
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
|
||||
self.manager().emit(event, Some(self.label()), payload)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn emit_to<S: Serialize + Clone>(
|
||||
&self,
|
||||
label: &str,
|
||||
@ -987,10 +972,14 @@ impl<R: Runtime> Manager<R> for Window<R> {
|
||||
.emit_filter(event, Some(self.label()), payload, |w| label == w.label())
|
||||
}
|
||||
|
||||
fn emit_all<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
|
||||
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
|
||||
where
|
||||
S: Serialize + Clone,
|
||||
F: Fn(&Window<R>) -> bool,
|
||||
{
|
||||
self
|
||||
.manager()
|
||||
.emit_filter(event, Some(self.label()), payload, |_| true)
|
||||
.emit_filter(event, Some(self.label()), payload, filter)
|
||||
}
|
||||
}
|
||||
impl<R: Runtime> ManagerBase<R> for Window<R> {
|
||||
@ -2337,6 +2326,14 @@ impl<R: Runtime> Window<R> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn emit_js(&self, emit_args: &EmitArgs) -> crate::Result<()> {
|
||||
self.eval(&crate::event::emit_js(
|
||||
self.manager().listeners().function_name(),
|
||||
emit_args,
|
||||
)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether this window registered a listener to an event from the given window and event name.
|
||||
pub(crate) fn has_js_listener(&self, window_label: Option<String>, event: &str) -> bool {
|
||||
self
|
||||
@ -2445,75 +2442,8 @@ impl<R: Runtime> Window<R> {
|
||||
|
||||
/// Event system APIs.
|
||||
impl<R: Runtime> Window<R> {
|
||||
/// Emits an event to both the JavaScript and the Rust listeners.
|
||||
///
|
||||
/// This API is a combination of [`Self::trigger`] and [`Self::emit`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[tauri::command]
|
||||
/// fn download(window: tauri::Window) {
|
||||
/// window.emit_and_trigger("download-started", ());
|
||||
///
|
||||
/// for i in 1..100 {
|
||||
/// std::thread::sleep(std::time::Duration::from_millis(150));
|
||||
/// // emit a download progress event to all listeners
|
||||
/// window.emit_and_trigger("download-progress", i);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn emit_and_trigger<S: Serialize + Clone>(
|
||||
&self,
|
||||
event: &str,
|
||||
payload: S,
|
||||
) -> crate::Result<()> {
|
||||
self.trigger(event, Some(serde_json::to_string(&payload)?));
|
||||
self.emit(event, payload)
|
||||
}
|
||||
|
||||
pub(crate) fn emit_internal(&self, emit_args: &WindowEmitArgs) -> crate::Result<()> {
|
||||
self.eval(&format!(
|
||||
"(function () {{ const fn = window['{}']; fn && fn({{event: {}, windowLabel: {}, payload: {}}}) }})()",
|
||||
self.manager.event_emit_function_name(),
|
||||
emit_args.event,
|
||||
emit_args.source_window_label,
|
||||
emit_args.payload
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Emits an event to the JavaScript listeners on the current window or globally.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[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 registed in the webview
|
||||
/// window.emit("download-progress", i);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
|
||||
self
|
||||
.manager
|
||||
.emit_filter(event, Some(self.label()), payload, |w| {
|
||||
w.has_js_listener(None, event) || w.has_js_listener(Some(self.label().into()), event)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Listen to an event on this window.
|
||||
///
|
||||
/// This listener only receives events that are triggered using the
|
||||
/// [`trigger`](Window#method.trigger) and [`emit_and_trigger`](Window#method.emit_and_trigger) methods or
|
||||
/// the `emit` function from the window plugin (`@tauri-apps/api/window` package).
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
@ -2532,8 +2462,9 @@ impl<R: Runtime> Window<R> {
|
||||
where
|
||||
F: Fn(Event) + Send + 'static,
|
||||
{
|
||||
let label = self.window.label.clone();
|
||||
self.manager.listen(event.into(), Some(label), handler)
|
||||
self
|
||||
.manager
|
||||
.listen(event.into(), Some(self.clone()), handler)
|
||||
}
|
||||
|
||||
/// Unlisten to an event on this window.
|
||||
@ -2565,7 +2496,7 @@ impl<R: Runtime> Window<R> {
|
||||
self.manager.unlisten(id)
|
||||
}
|
||||
|
||||
/// Listen to an event on this window a single time.
|
||||
/// Listen to an event on this window only once.
|
||||
///
|
||||
/// See [`Self::listen`] for more information.
|
||||
pub fn once<F>(&self, event: impl Into<String>, handler: F)
|
||||
@ -2575,26 +2506,6 @@ impl<R: Runtime> Window<R> {
|
||||
let label = self.window.label.clone();
|
||||
self.manager.once(event.into(), Some(label), handler)
|
||||
}
|
||||
|
||||
/// Triggers an event to the Rust listeners on this window or global listeners.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use tauri::Manager;
|
||||
///
|
||||
/// #[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 registed on `window` in Rust
|
||||
/// window.trigger("download-progress", Some(i.to_string()));
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn trigger(&self, event: &str, data: Option<String>) {
|
||||
let label = self.window.label.clone();
|
||||
self.manager.trigger(event, Some(label), data)
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`WindowEffectsConfig`] object builder
|
||||
|
Loading…
Reference in New Issue
Block a user