mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
refactor: document Emitter/Listner traits panics, refactor check into internal struct (#12444)
This commit is contained in:
@@ -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<()> {
|
||||
|
||||
@@ -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 {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
70
crates/tauri/src/event/event_name.rs
Normal file
70
crates/tauri/src/event/event_name.rs
Normal 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 `_`.",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user