refactor: document Emitter/Listner traits panics, refactor check into internal struct (#12444)

This commit is contained in:
sftse
2025-01-24 03:17:17 +01:00
committed by GitHub
parent f5a59b93bf
commit 6cbfc4878d
14 changed files with 289 additions and 529 deletions

View File

@@ -327,15 +327,11 @@ mod sys {
}
pub(super) fn error_contended(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32)
err.raw_os_error() == Some(ERROR_LOCK_VIOLATION as i32)
}
pub(super) fn error_unsupported(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32)
}
pub(super) fn unlock(file: &File) -> Result<()> {

View File

@@ -18,7 +18,7 @@ use crate::{
sealed::{ManagerBase, RuntimeOrDispatch},
utils::{config::Config, Env},
webview::PageLoadPayload,
Context, DeviceEventFilter, Emitter, EventLoopMessage, Listener, Manager, Monitor, Result,
Context, DeviceEventFilter, Emitter, EventLoopMessage, EventName, Listener, Manager, Monitor,
Runtime, Scopes, StateManager, Theme, Webview, WebviewWindowBuilder, Window,
};
@@ -38,7 +38,6 @@ use tauri_runtime::{
};
use tauri_utils::{assets::AssetsIter, PackageInfo};
use serde::Serialize;
use std::{
borrow::Cow,
collections::HashMap,
@@ -929,7 +928,8 @@ macro_rules! shared_app_impl {
where
F: Fn(Event) + Send + 'static,
{
self.manager.listen(event.into(), EventTarget::App, handler)
let event = EventName::new(event.into()).unwrap();
self.manager.listen(event, EventTarget::App, handler)
}
/// Listen to an event on this app only once.
@@ -939,7 +939,8 @@ macro_rules! shared_app_impl {
where
F: FnOnce(Event) + Send + 'static,
{
self.manager.once(event.into(), EventTarget::App, handler)
let event = EventName::new(event.into()).unwrap();
self.manager.once(event, EventTarget::App, handler)
}
/// Unlisten to an event on this app.
@@ -966,79 +967,7 @@ macro_rules! shared_app_impl {
}
}
impl<R: Runtime> Emitter<R> for $app {
/// 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<()> {
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
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(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)
}
}
impl<R: Runtime> Emitter<R> for $app {}
};
}

View File

@@ -160,6 +160,9 @@ pub enum Error {
/// Bad `__TAURI_INVOKE_KEY__` value received in ipc message.
#[error("bad __TAURI_INVOKE_KEY__ value received in ipc message")]
InvokeKey,
/// Illegal event name.
#[error("only alphanumeric, '-', '/', ':', '_' permitted for event names: {0:?}")]
IllegalEventName(String),
}
impl From<getrandom::Error> for Error {

View File

@@ -0,0 +1,70 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use serde::{Deserialize, Deserializer};
/// Checks if an event name is valid.
fn is_event_name_valid(event: &str) -> bool {
event
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '/' || c == ':' || c == '_')
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct EventName<S = String>(S);
impl Copy for EventName<&str> {}
impl<S: AsRef<str>> EventName<S> {
pub(crate) fn new(s: S) -> crate::Result<EventName<S>> {
if !is_event_name_valid(s.as_ref()) {
return Err(crate::Error::IllegalEventName(s.as_ref().to_string()));
}
Ok(EventName(s))
}
pub(crate) fn as_str_event(&self) -> EventName<&str> {
EventName(self.0.as_ref())
}
pub(crate) fn as_str(&self) -> &str {
self.0.as_ref()
}
}
impl<S: std::fmt::Display> std::fmt::Display for EventName<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl EventName<&'static str> {
// this convenience method is for using in const contexts to discharge the preconditions
// &'static prevents using this function accidentally with dynamically built string slices
pub(crate) const fn from_str(s: &'static str) -> EventName<&'static str> {
EventName(s)
}
}
impl EventName<&str> {
pub fn into_owned(self) -> EventName {
EventName(self.0.to_string())
}
}
impl<'de> Deserialize<'de> for EventName {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let event_id = String::deserialize(deserializer)?;
if is_event_name_valid(&event_id) {
Ok(EventName(event_id))
} else {
Err(serde::de::Error::custom(
"Event name must include only alphanumeric characters, `-`, `/`, `:` and `_`.",
))
}
}
}

View File

@@ -21,7 +21,7 @@ enum Pending {
Unlisten(EventId),
Listen {
id: EventId,
event: String,
event: crate::EventName,
handler: Handler,
},
Emit(EmitArgs),
@@ -55,13 +55,12 @@ impl JsHandler {
}
type WebviewLabel = String;
type EventName = String;
/// Holds event handlers and pending event handlers, along with the salts associating them.
struct InnerListeners {
pending: Mutex<Vec<Pending>>,
handlers: Mutex<HashMap<EventName, HashMap<EventId, Handler>>>,
js_event_listeners: Mutex<HashMap<WebviewLabel, HashMap<EventName, HashSet<JsHandler>>>>,
handlers: Mutex<HashMap<crate::EventName, HashMap<EventId, Handler>>>,
js_event_listeners: Mutex<HashMap<WebviewLabel, HashMap<crate::EventName, HashSet<JsHandler>>>>,
function_name: &'static str,
listeners_object_name: &'static str,
next_event_id: Arc<AtomicU32>,
@@ -128,16 +127,14 @@ impl Listeners {
match action {
Pending::Unlisten(id) => self.unlisten(id),
Pending::Listen { id, event, handler } => self.listen_with_id(id, event, handler),
Pending::Emit(args) => {
self.emit(args)?;
}
Pending::Emit(args) => self.emit(args)?,
}
}
Ok(())
}
fn listen_with_id(&self, id: EventId, event: String, handler: Handler) {
fn listen_with_id(&self, id: EventId, event: crate::EventName, handler: Handler) {
match self.inner.handlers.try_lock() {
Err(_) => self.insert_pending(Pending::Listen { id, event, handler }),
Ok(mut lock) => {
@@ -149,7 +146,7 @@ impl Listeners {
/// Adds an event listener.
pub(crate) fn listen<F: Fn(Event) + Send + 'static>(
&self,
event: String,
event: crate::EventName,
target: EventTarget,
handler: F,
) -> EventId {
@@ -162,7 +159,7 @@ impl Listeners {
/// Listen to an event and immediately unlisten.
pub(crate) fn once<F: FnOnce(Event) + Send + 'static>(
&self,
event: String,
event: crate::EventName,
target: EventTarget,
handler: F,
) -> EventId {
@@ -199,7 +196,7 @@ impl Listeners {
match self.inner.handlers.try_lock() {
Err(_) => self.insert_pending(Pending::Emit(emit_args)),
Ok(lock) => {
if let Some(handlers) = lock.get(&emit_args.event_name) {
if let Some(handlers) = lock.get(&emit_args.event) {
let handlers = handlers.iter();
let handlers = handlers.filter(|(_, h)| match_any_or_filter(&h.target, &filter));
for (&id, Handler { callback, .. }) in handlers {
@@ -224,29 +221,31 @@ impl Listeners {
pub(crate) fn listen_js(
&self,
event: &str,
event: crate::EventName<&str>,
source_webview_label: &str,
target: EventTarget,
id: EventId,
) {
let event = event.into_owned();
let mut listeners = self.inner.js_event_listeners.lock().unwrap();
listeners
.entry(source_webview_label.to_string())
.or_default()
.entry(event.to_string())
.entry(event)
.or_default()
.insert(JsHandler::new(target, id));
}
pub(crate) fn unlisten_js(&self, event: &str, id: EventId) {
pub(crate) fn unlisten_js(&self, event: crate::EventName<&str>, id: EventId) {
let event = event.into_owned();
let mut js_listeners = self.inner.js_event_listeners.lock().unwrap();
let js_listeners = js_listeners.values_mut();
for js_listeners in js_listeners {
if let Some(handlers) = js_listeners.get_mut(event) {
if let Some(handlers) = js_listeners.get_mut(&event) {
handlers.retain(|h| h.id != id);
if handlers.is_empty() {
js_listeners.remove(event);
js_listeners.remove(&event);
}
}
}
@@ -254,13 +253,14 @@ impl Listeners {
pub(crate) fn has_js_listener<F: Fn(&EventTarget) -> bool>(
&self,
event: &str,
event: crate::EventName<&str>,
filter: F,
) -> bool {
let event = event.into_owned();
let js_listeners = self.inner.js_event_listeners.lock().unwrap();
js_listeners.values().any(|events| {
events
.get(event)
.get(&event)
.map(|handlers| handlers.iter().any(|handler| filter(&handler.target)))
.unwrap_or(false)
})
@@ -269,7 +269,6 @@ impl Listeners {
pub(crate) fn emit_js_filter<'a, R, I, F>(
&self,
mut webviews: I,
event: &str,
emit_args: &EmitArgs,
filter: Option<F>,
) -> crate::Result<()>
@@ -278,6 +277,7 @@ impl Listeners {
I: Iterator<Item = &'a Webview<R>>,
F: Fn(&EventTarget) -> bool,
{
let event = &emit_args.event;
let js_listeners = self.inner.js_event_listeners.lock().unwrap();
webviews.try_for_each(|webview| {
if let Some(handlers) = js_listeners.get(webview.label()).and_then(|s| s.get(event)) {
@@ -293,22 +293,12 @@ impl Listeners {
})
}
pub(crate) fn emit_js<'a, R, I>(
&self,
webviews: I,
event: &str,
emit_args: &EmitArgs,
) -> crate::Result<()>
pub(crate) fn emit_js<'a, R, I>(&self, webviews: I, emit_args: &EmitArgs) -> crate::Result<()>
where
R: Runtime,
I: Iterator<Item = &'a Webview<R>>,
{
self.emit_js_filter(
webviews,
event,
emit_args,
None::<&dyn Fn(&EventTarget) -> bool>,
)
self.emit_js_filter(webviews, emit_args, None::<&dyn Fn(&EventTarget) -> bool>)
}
}
@@ -339,9 +329,9 @@ mod test {
fn listeners_check_key(e in "[a-z]+") {
let listeners: Listeners = Default::default();
// clone e as the key
let key = e.clone();
let key = crate::EventName::new(e.clone()).unwrap();
// pass e and an dummy func into listen
listeners.listen(e, EventTarget::Any, event_fn);
listeners.listen(key.clone(), EventTarget::Any, event_fn);
// lock mutex
let l = listeners.inner.handlers.lock().unwrap();
@@ -353,11 +343,10 @@ 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();
// clone e as the key
let key = e.clone();
let listeners: Listeners = Default::default();
let key = crate::EventName::new(e.clone()).unwrap();
// pass e and an dummy func into listen
listeners.listen(e, EventTarget::Any, event_fn);
listeners.listen(key.clone(), EventTarget::Any, event_fn);
// lock mutex
let mut l = listeners.inner.handlers.lock().unwrap();
@@ -378,16 +367,13 @@ 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]+") {
fn check_on_event(e in "[a-z]+", d in "[a-z]+") {
let listeners: Listeners = Default::default();
let key = crate::EventName::new(e.clone()).unwrap();
// call listen with key and the event_fn dummy func
listeners.listen(key.clone(), EventTarget::Any, event_fn);
// call on event with key and d.
listeners.emit(EmitArgs {
event_name: key.clone(),
event: serde_json::to_string(&key).unwrap(),
payload: serde_json::to_string(&d).unwrap()
})?;
listeners.emit(EmitArgs::new(key.as_str_event(), &d)?)?;
// lock the mutex
let l = listeners.inner.handlers.lock().unwrap();

View File

@@ -9,19 +9,9 @@ use std::{convert::Infallible, str::FromStr};
pub(crate) use listener::Listeners;
use serde::{Deserialize, Serialize};
/// Checks if an event name is valid.
pub fn is_event_name_valid(event: &str) -> bool {
event
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '/' || c == ':' || c == '_')
}
mod event_name;
pub fn assert_event_name_is_valid(event: &str) {
assert!(
is_event_name_valid(event),
"Event name must include only alphanumeric characters, `-`, `/`, `:` and `_`."
);
}
pub(crate) use event_name::EventName;
/// Unique id of an event.
pub type EventId = u32;
@@ -123,22 +113,19 @@ impl FromStr for EventTarget {
/// Serialized emit arguments.
#[derive(Clone)]
pub struct EmitArgs {
/// Raw event name.
pub event_name: String,
/// Serialized event name.
pub event: String,
/// event name.
event: EventName,
/// Serialized payload.
pub payload: String,
payload: String,
}
impl EmitArgs {
pub fn new<S: Serialize>(event: &str, payload: S) -> crate::Result<Self> {
pub fn new<S: Serialize>(event: EventName<&str>, payload: &S) -> crate::Result<Self> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("window::emit::serialize").entered();
Ok(EmitArgs {
event_name: event.into(),
event: serde_json::to_string(event)?,
payload: serde_json::to_string(&payload)?,
event: event.into_owned(),
payload: serde_json::to_string(payload)?,
})
}
}
@@ -169,7 +156,7 @@ impl Event {
pub fn listen_js_script(
listeners_object_name: &str,
serialized_target: &str,
event: &str,
event: EventName<&str>,
event_id: EventId,
handler: &str,
) -> String {
@@ -198,7 +185,7 @@ pub fn emit_js_script(
serialized_ids: &str,
) -> crate::Result<String> {
Ok(format!(
"(function () {{ const fn = window['{}']; fn && fn({{event: {}, payload: {}}}, {ids}) }})()",
"(function () {{ const fn = window['{}']; fn && fn({{event: '{}', payload: {}}}, {ids}) }})()",
event_emit_function_name,
emit_args.event,
emit_args.payload,
@@ -208,7 +195,7 @@ pub fn emit_js_script(
pub fn unlisten_js_script(
listeners_object_name: &str,
event_name: &str,
event_name: EventName<&str>,
event_id: EventId,
) -> String {
format!(
@@ -239,3 +226,15 @@ pub fn event_initialization_script(function: &str, listeners: &str) -> String {
"
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_illegal_event_name() {
let s = EventName::new("some\r illegal event name")
.unwrap_err()
.to_string();
assert_eq!("only alphanumeric, '-', '/', ':', '_' permitted for event names: \"some\\r illegal event name\"", s);
}
}

View File

@@ -1,9 +1,6 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::ops::Deref;
use serde::{Deserialize, Deserializer};
use serde_json::Value as JsonValue;
use tauri_runtime::window::is_label_valid;
@@ -12,33 +9,8 @@ use crate::plugin::{Builder, TauriPlugin};
use crate::{command, ipc::CallbackFn, EventId, Result, Runtime};
use crate::{AppHandle, Emitter, Webview};
use super::{is_event_name_valid, EventTarget};
pub struct EventName(String);
impl Deref for EventName {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'de> Deserialize<'de> for EventName {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let event_id = String::deserialize(deserializer)?;
if is_event_name_valid(&event_id) {
Ok(EventName(event_id))
} else {
Err(serde::de::Error::custom(
"Event name must include only alphanumeric characters, `-`, `/`, `:` and `_`.",
))
}
}
}
use super::EventName;
use super::EventTarget;
pub struct WebviewLabel(String);
@@ -65,41 +37,41 @@ impl<'de> Deserialize<'de> for WebviewLabel {
}
#[command(root = "crate")]
pub async fn listen<R: Runtime>(
async fn listen<R: Runtime>(
webview: Webview<R>,
event: EventName,
target: EventTarget,
handler: CallbackFn,
) -> Result<EventId> {
webview.listen_js(&event, target, handler)
webview.listen_js(event.as_str_event(), target, handler)
}
#[command(root = "crate")]
pub async fn unlisten<R: Runtime>(
async fn unlisten<R: Runtime>(
webview: Webview<R>,
event: EventName,
event_id: EventId,
) -> Result<()> {
webview.unlisten_js(&event, event_id)
webview.unlisten_js(event.as_str_event(), event_id)
}
#[command(root = "crate")]
pub async fn emit<R: Runtime>(
async fn emit<R: Runtime>(
app: AppHandle<R>,
event: EventName,
payload: Option<JsonValue>,
) -> Result<()> {
app.emit(&event, payload)
app.emit(event.as_str(), payload)
}
#[command(root = "crate")]
pub async fn emit_to<R: Runtime>(
async fn emit_to<R: Runtime>(
app: AppHandle<R>,
target: EventTarget,
event: EventName,
payload: Option<JsonValue>,
) -> Result<()> {
app.emit_to(target, &event, payload)
app.emit_to(target, event.as_str(), payload)
}
/// Initializes the event plugin.

View File

@@ -208,6 +208,7 @@ pub use runtime::ActivationPolicy;
#[cfg(target_os = "macos")]
pub use self::utils::TitleBarStyle;
use self::event::EventName;
pub use self::event::{Event, EventId, EventTarget};
pub use {
self::app::{
@@ -837,6 +838,8 @@ pub trait Listener<R: Runtime>: sealed::ManagerBase<R> {
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
/// # Panics
/// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: Fn(Event) + Send + 'static;
@@ -844,6 +847,8 @@ pub trait Listener<R: Runtime>: sealed::ManagerBase<R> {
/// Listen to an event on this manager only once.
///
/// See [`Self::listen`] for more information.
/// # Panics
/// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
where
F: FnOnce(Event) + Send + 'static;
@@ -895,23 +900,27 @@ pub trait Listener<R: Runtime>: sealed::ManagerBase<R> {
/// })
/// .invoke_handler(tauri::generate_handler![synchronize]);
/// ```
/// # Panics
/// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
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)
let event = EventName::new(event.into()).unwrap();
self.manager().listen(event, EventTarget::Any, handler)
}
/// Listens once to an emitted event to any [target](EventTarget) .
///
/// See [`Self::listen_any`] for more information.
/// # Panics
/// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
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)
let event = EventName::new(event.into()).unwrap();
self.manager().once(event, EventTarget::Any, handler)
}
}
@@ -929,7 +938,10 @@ pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
/// app.emit("synchronized", ());
/// }
/// ```
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()>;
fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
let event = EventName::new(event)?;
self.manager().emit(event, &payload)
}
/// Emits an event to all [targets](EventTarget) matching the given target.
///
@@ -956,7 +968,11 @@ pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone;
S: Serialize + Clone,
{
let event = EventName::new(event)?;
self.manager().emit_to(target, event, &payload)
}
/// Emits an event to all [targets](EventTarget) based on the given filter.
///
@@ -979,7 +995,11 @@ pub trait Emitter<R: Runtime>: sealed::ManagerBase<R> {
fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> Result<()>
where
S: Serialize + Clone,
F: Fn(&EventTarget) -> bool;
F: Fn(&EventTarget) -> bool,
{
let event = EventName::new(event)?;
self.manager().emit_filter(event, &payload, filter)
}
}
/// Prevent implementation details from leaking out of the [`Manager`] trait.

View File

@@ -19,18 +19,18 @@ use tauri_utils::{
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
};
use crate::resources::ResourceTable;
use crate::{
app::{
AppHandle, ChannelInterceptor, GlobalWebviewEventListener, GlobalWindowEventListener,
OnPageLoad,
},
event::{assert_event_name_is_valid, Event, EventId, EventTarget, Listeners},
event::{EmitArgs, Event, EventId, EventTarget, Listeners},
ipc::{Invoke, InvokeHandler, RuntimeAuthority},
plugin::PluginStore,
utils::{config::Config, PackageInfo},
Assets, Context, Pattern, Runtime, StateManager, Window,
Assets, Context, EventName, Pattern, Runtime, StateManager, Webview, Window,
};
use crate::{event::EmitArgs, resources::ResourceTable, Webview};
#[cfg(desktop)]
mod menu;
@@ -507,23 +507,25 @@ impl<R: Runtime> AppManager<R> {
&self.package_info
}
pub fn listen<F: Fn(Event) + Send + 'static>(
/// # Panics
/// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
pub(crate) fn listen<F: Fn(Event) + Send + 'static>(
&self,
event: String,
event: EventName,
target: EventTarget,
handler: F,
) -> EventId {
assert_event_name_is_valid(&event);
self.listeners().listen(event, target, handler)
}
pub fn once<F: FnOnce(Event) + Send + 'static>(
/// # Panics
/// Will panic if `event` contains characters other than alphanumeric, `-`, `/`, `:` and `_`
pub(crate) fn once<F: FnOnce(Event) + Send + 'static>(
&self,
event: String,
event: EventName,
target: EventTarget,
handler: F,
) -> EventId {
assert_event_name_is_valid(&event);
self.listeners().once(event, target, handler)
}
@@ -535,12 +537,14 @@ impl<R: Runtime> AppManager<R> {
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);
pub(crate) fn emit<S: Serialize>(
&self,
event: EventName<&str>,
payload: &S,
) -> crate::Result<()> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("emit::run").entered();
let emit_args = EmitArgs::new(event, payload)?;
let emit_args = EmitArgs::new(event, &payload)?;
let listeners = self.listeners();
let webviews = self
@@ -550,7 +554,7 @@ impl<R: Runtime> AppManager<R> {
.cloned()
.collect::<Vec<_>>();
listeners.emit_js(webviews.iter(), event, &emit_args)?;
listeners.emit_js(webviews.iter(), &emit_args)?;
listeners.emit(emit_args)?;
Ok(())
@@ -560,22 +564,24 @@ impl<R: Runtime> AppManager<R> {
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<()>
pub(crate) fn emit_filter<S, F>(
&self,
event: EventName<&str>,
payload: S,
filter: F,
) -> crate::Result<()>
where
S: Serialize + Clone,
S: Serialize,
F: Fn(&EventTarget) -> bool,
{
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 emit_args = EmitArgs::new(event, &payload)?;
let listeners = self.listeners();
listeners.emit_js_filter(
self.webview.webviews_lock().values(),
event,
&emit_args,
Some(&filter),
)?;
@@ -589,10 +595,15 @@ impl<R: Runtime> AppManager<R> {
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<()>
pub(crate) fn emit_to<I, S>(
&self,
target: I,
event: EventName<&str>,
payload: &S,
) -> crate::Result<()>
where
I: Into<EventTarget>,
S: Serialize + Clone,
S: Serialize,
{
let target = target.into();
#[cfg(feature = "tracing")]

View File

@@ -25,8 +25,7 @@ use crate::{
pattern::PatternJavascript,
sealed::ManagerBase,
webview::PageLoadPayload,
Emitter, EventLoopMessage, EventTarget, Manager, Runtime, Scopes, UriSchemeContext, Webview,
Window,
EventLoopMessage, EventTarget, Manager, Runtime, Scopes, UriSchemeContext, Webview, Window,
};
use super::{
@@ -633,8 +632,8 @@ impl<R: Runtime> WebviewManager<R> {
}
let _ = webview.manager.emit(
"tauri://webview-created",
Some(crate::webview::CreatedEvent {
crate::EventName::from_str("tauri://webview-created"),
&Some(crate::webview::CreatedEvent {
label: webview.label().into(),
}),
);
@@ -661,14 +660,20 @@ impl<R: Runtime> WebviewManager<R> {
impl<R: Runtime> Webview<R> {
/// Emits event to [`EventTarget::Window`] and [`EventTarget::WebviewWindow`]
fn emit_to_webview<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
fn emit_to_webview<S: Serialize + Clone>(
&self,
event: crate::EventName<&str>,
payload: S,
) -> crate::Result<()> {
let window_label = self.label();
self.emit_filter(event, payload, |target| match target {
EventTarget::Webview { label } | EventTarget::WebviewWindow { label } => {
label == window_label
}
_ => false,
})
self
.manager()
.emit_filter(event, payload, |target| match target {
EventTarget::Webview { label } | EventTarget::WebviewWindow { label } => {
label == window_label
}
_ => false,
})
}
}

View File

@@ -17,22 +17,24 @@ use tauri_runtime::{
};
use crate::{
app::GlobalWindowEventListener, image::Image, sealed::ManagerBase, AppHandle, Emitter,
app::GlobalWindowEventListener, event::EventName, image::Image, sealed::ManagerBase, AppHandle,
EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Window, WindowEvent,
};
const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
const WINDOW_MOVED_EVENT: &str = "tauri://move";
const WINDOW_CLOSE_REQUESTED_EVENT: &str = "tauri://close-requested";
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";
const WINDOW_THEME_CHANGED: &str = "tauri://theme-changed";
pub(crate) const DRAG_ENTER_EVENT: &str = "tauri://drag-enter";
pub(crate) const DRAG_OVER_EVENT: &str = "tauri://drag-over";
pub(crate) const DRAG_DROP_EVENT: &str = "tauri://drag-drop";
pub(crate) const DRAG_LEAVE_EVENT: &str = "tauri://drag-leave";
const WINDOW_RESIZED_EVENT: EventName<&str> = EventName::from_str("tauri://resize");
const WINDOW_MOVED_EVENT: EventName<&str> = EventName::from_str("tauri://move");
const WINDOW_CLOSE_REQUESTED_EVENT: EventName<&str> =
EventName::from_str("tauri://close-requested");
const WINDOW_DESTROYED_EVENT: EventName<&str> = EventName::from_str("tauri://destroyed");
const WINDOW_FOCUS_EVENT: EventName<&str> = EventName::from_str("tauri://focus");
const WINDOW_BLUR_EVENT: EventName<&str> = EventName::from_str("tauri://blur");
const WINDOW_SCALE_FACTOR_CHANGED_EVENT: EventName<&str> =
EventName::from_str("tauri://scale-change");
const WINDOW_THEME_CHANGED: EventName<&str> = EventName::from_str("tauri://theme-changed");
pub(crate) const DRAG_ENTER_EVENT: EventName<&str> = EventName::from_str("tauri://drag-enter");
pub(crate) const DRAG_OVER_EVENT: EventName<&str> = EventName::from_str("tauri://drag-over");
pub(crate) const DRAG_DROP_EVENT: EventName<&str> = EventName::from_str("tauri://drag-drop");
pub(crate) const DRAG_LEAVE_EVENT: EventName<&str> = EventName::from_str("tauri://drag-leave");
pub struct WindowManager<R: Runtime> {
pub windows: Mutex<HashMap<String, Window<R>>>,
@@ -124,16 +126,20 @@ impl<R: Runtime> WindowManager<R> {
impl<R: Runtime> Window<R> {
/// Emits event to [`EventTarget::Window`] and [`EventTarget::WebviewWindow`]
fn emit_to_window<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
fn emit_to_window<S: Serialize>(&self, event: EventName<&str>, payload: &S) -> crate::Result<()> {
let window_label = self.label();
self.emit_filter(event, payload, |target| match target {
EventTarget::Window { label } | EventTarget::WebviewWindow { label } => label == window_label,
_ => false,
})
self
.manager()
.emit_filter(event, payload, |target| match target {
EventTarget::Window { label } | EventTarget::WebviewWindow { label } => {
label == window_label
}
_ => false,
})
}
/// Checks whether has js listener for [`EventTarget::Window`] or [`EventTarget::WebviewWindow`]
fn has_js_listener(&self, event: &str) -> bool {
fn has_js_listener(&self, event: EventName<&str>) -> bool {
let window_label = self.label();
let listeners = self.manager().listeners();
listeners.has_js_listener(event, |target| match target {
@@ -158,10 +164,10 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
if window.has_js_listener(WINDOW_CLOSE_REQUESTED_EVENT) {
api.prevent_close();
}
window.emit_to_window(WINDOW_CLOSE_REQUESTED_EVENT, ())?;
window.emit_to_window(WINDOW_CLOSE_REQUESTED_EVENT, &())?;
}
WindowEvent::Destroyed => {
window.emit_to_window(WINDOW_DESTROYED_EVENT, ())?;
window.emit_to_window(WINDOW_DESTROYED_EVENT, &())?;
}
WindowEvent::Focused(focused) => window.emit_to_window(
if *focused {
@@ -169,7 +175,7 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
} else {
WINDOW_BLUR_EVENT
},
(),
&(),
)?,
WindowEvent::ScaleFactorChanged {
scale_factor,
@@ -177,7 +183,7 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
..
} => window.emit_to_window(
WINDOW_SCALE_FACTOR_CHANGED_EVENT,
ScaleFactorChanged {
&ScaleFactorChanged {
scale_factor: *scale_factor,
size: *new_inner_size,
},
@@ -190,13 +196,14 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
};
if window.is_webview_window() {
window.emit_to(
// use underlying manager, otherwise have to recheck EventName
window.manager().emit_to(
EventTarget::labeled(window.label()),
DRAG_ENTER_EVENT,
payload,
&payload,
)?
} else {
window.emit_to_window(DRAG_ENTER_EVENT, payload)?
window.emit_to_window(DRAG_ENTER_EVENT, &payload)?
}
}
DragDropEvent::Over { position } => {
@@ -205,13 +212,14 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
paths: None,
};
if window.is_webview_window() {
window.emit_to(
// use underlying manager, otherwise have to recheck EventName
window.manager().emit_to(
EventTarget::labeled(window.label()),
DRAG_OVER_EVENT,
payload,
&payload,
)?
} else {
window.emit_to_window(DRAG_OVER_EVENT, payload)?
window.emit_to_window(DRAG_OVER_EVENT, &payload)?
}
}
DragDropEvent::Drop { paths, position } => {
@@ -229,27 +237,29 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
};
if window.is_webview_window() {
window.emit_to(
// use underlying manager, otherwise have to recheck EventName
window.manager().emit_to(
EventTarget::labeled(window.label()),
DRAG_DROP_EVENT,
payload,
&payload,
)?
} else {
window.emit_to_window(DRAG_DROP_EVENT, payload)?
window.emit_to_window(DRAG_DROP_EVENT, &payload)?
}
}
DragDropEvent::Leave => {
if window.is_webview_window() {
window.emit_to(EventTarget::labeled(window.label()), DRAG_LEAVE_EVENT, ())?
// use underlying manager, otherwise have to recheck EventName
window
.manager()
.emit_to(EventTarget::labeled(window.label()), DRAG_LEAVE_EVENT, &())?
} else {
window.emit_to_window(DRAG_LEAVE_EVENT, ())?
window.emit_to_window(DRAG_LEAVE_EVENT, &())?
}
}
_ => unimplemented!(),
},
WindowEvent::ThemeChanged(theme) => {
window.emit_to_window(WINDOW_THEME_CHANGED, theme.to_string())?
}
WindowEvent::ThemeChanged(theme) => window.emit_to_window(WINDOW_THEME_CHANGED, &theme)?,
}
Ok(())
}

View File

@@ -35,8 +35,8 @@ use crate::{
},
manager::AppManager,
sealed::{ManagerBase, RuntimeOrDispatch},
AppHandle, Emitter, Event, EventId, EventLoopMessage, Listener, Manager, ResourceTable, Runtime,
Window,
AppHandle, Emitter, Event, EventId, EventLoopMessage, EventName, Listener, Manager,
ResourceTable, Runtime, Window,
};
use std::{
@@ -1491,7 +1491,7 @@ fn main() {
/// Register a JS event listener and return its identifier.
pub(crate) fn listen_js(
&self,
event: &str,
event: EventName<&str>,
target: EventTarget,
handler: CallbackFn,
) -> crate::Result<EventId> {
@@ -1513,7 +1513,7 @@ fn main() {
}
/// Unregister a JS event listener.
pub(crate) fn unlisten_js(&self, event: &str, id: EventId) -> crate::Result<()> {
pub(crate) fn unlisten_js(&self, event: EventName<&str>, id: EventId) -> crate::Result<()> {
let listeners = self.manager().listeners();
self.eval(&crate::event::unlisten_js_script(
@@ -1711,8 +1711,9 @@ tauri::Builder::default()
where
F: Fn(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager.listen(
event.into(),
event,
EventTarget::Webview {
label: self.label().to_string(),
},
@@ -1727,8 +1728,9 @@ tauri::Builder::default()
where
F: FnOnce(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager.once(
event.into(),
event,
EventTarget::Webview {
label: self.label().to_string(),
},
@@ -1770,94 +1772,7 @@ tauri::Builder::default()
}
}
impl<R: Runtime> Emitter<R> for Webview<R> {
/// Emits an event to all [targets](EventTarget).
///
/// # 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
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
#[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)
}
}
impl<R: Runtime> Emitter<R> for Webview<R> {}
impl<R: Runtime> Manager<R> for Webview<R> {
fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {

View File

@@ -15,7 +15,7 @@ use crate::{
ipc::ScopeObject,
runtime::dpi::{PhysicalPosition, PhysicalSize},
window::Monitor,
Emitter, Listener, ResourceTable, Window,
Emitter, EventName, Listener, ResourceTable, Window,
};
#[cfg(desktop)]
use crate::{
@@ -27,7 +27,6 @@ use crate::{
UserAttentionType,
},
};
use serde::Serialize;
use tauri_utils::{
config::{Color, WebviewUrl, WindowConfig},
Theme,
@@ -2010,8 +2009,9 @@ impl<R: Runtime> Listener<R> for WebviewWindow<R> {
where
F: Fn(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager().listen(
event.into(),
event,
EventTarget::WebviewWindow {
label: self.label().to_string(),
},
@@ -2026,8 +2026,9 @@ impl<R: Runtime> Listener<R> for WebviewWindow<R> {
where
F: FnOnce(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager().once(
event.into(),
event,
EventTarget::WebviewWindow {
label: self.label().to_string(),
},
@@ -2064,79 +2065,7 @@ impl<R: Runtime> Listener<R> for WebviewWindow<R> {
}
}
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> Emitter<R> for WebviewWindow<R> {}
impl<R: Runtime> Manager<R> for WebviewWindow<R> {
fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {

View File

@@ -28,7 +28,7 @@ use crate::{
sealed::{ManagerBase, RuntimeOrDispatch},
utils::config::{WindowConfig, WindowEffectsConfig},
webview::WebviewBuilder,
Emitter, EventLoopMessage, Listener, Manager, ResourceTable, Runtime, Theme, Webview,
Emitter, EventLoopMessage, EventName, Listener, Manager, ResourceTable, Runtime, Theme, Webview,
WindowEvent,
};
#[cfg(desktop)]
@@ -405,8 +405,8 @@ tauri::Builder::default()
// run on the main thread to fix a deadlock on webview.eval if the tracing feature is enabled
let _ = window.run_on_main_thread(move || {
let _ = app_manager.emit(
"tauri://window-created",
Some(crate::webview::CreatedEvent {
crate::EventName::from_str("tauri://window-created"),
&Some(crate::webview::CreatedEvent {
label: window_label,
}),
);
@@ -2150,8 +2150,9 @@ tauri::Builder::default()
where
F: Fn(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager.listen(
event.into(),
event,
EventTarget::Window {
label: self.label().to_string(),
},
@@ -2166,8 +2167,9 @@ tauri::Builder::default()
where
F: FnOnce(Event) + Send + 'static,
{
let event = EventName::new(event.into()).unwrap();
self.manager.once(
event.into(),
event,
EventTarget::Window {
label: self.label().to_string(),
},
@@ -2209,94 +2211,7 @@ tauri::Builder::default()
}
}
impl<R: Runtime> Emitter<R> for Window<R> {
/// Emits an event to all [targets](EventTarget).
///
/// # 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
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
#[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)
}
}
impl<R: Runtime> Emitter<R> for Window<R> {}
/// The [`WindowEffectsConfig`] object builder
#[derive(Default)]