Explorar o código

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

amrbashir hai 1 ano
pai
achega
19d32262fb

+ 210 - 11
core/tauri/src/app.rs

@@ -22,8 +22,8 @@ use crate::{
   utils::config::Config,
   utils::Env,
   webview::PageLoadPayload,
-  Context, DeviceEventFilter, EventLoopMessage, Manager, Monitor, Runtime, Scopes, StateManager,
-  Theme, Webview, WebviewWindowBuilder, Window,
+  Context, DeviceEventFilter, Emitter, EventLoopMessage, Listener, Manager, Monitor, Result,
+  Runtime, Scopes, StateManager, Theme, Webview, WebviewWindowBuilder, Window,
 };
 
 #[cfg(desktop)]
@@ -42,6 +42,7 @@ use tauri_runtime::{
 };
 use tauri_utils::PackageInfo;
 
+use serde::Serialize;
 use std::{
   borrow::Cow,
   collections::HashMap,
@@ -66,7 +67,7 @@ pub(crate) type GlobalWebviewEventListener<R> =
   Box<dyn Fn(&Webview<R>, &WebviewEvent) + Send + Sync>;
 /// A closure that is run when the Tauri application is setting up.
 pub type SetupHook<R> =
-  Box<dyn FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send>;
+  Box<dyn FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send>;
 /// A closure that is run every time a page starts or finishes loading.
 pub type OnPageLoad<R> = dyn Fn(&Webview<R>, &PageLoadPayload<'_>) + Send + Sync + 'static;
 
@@ -320,7 +321,7 @@ impl<R: Runtime> Clone for AppHandle<R> {
 
 impl<'de, R: Runtime> CommandArg<'de, R> for AppHandle<R> {
   /// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`AppHandle`]. This will never fail.
-  fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
+  fn from_command(command: CommandItem<'de, R>) -> std::result::Result<Self, InvokeError> {
     Ok(command.message.webview().window().app_handle.clone())
   }
 }
@@ -790,8 +791,6 @@ macro_rules! shared_app_impl {
       /// # Examples
       ///
       /// ```
-      /// use tauri::Manager;
-      ///
       /// tauri::Builder::default()
       ///   .setup(|app| {
       ///     app.listen("component-loaded", move |event| {
@@ -808,13 +807,21 @@ macro_rules! shared_app_impl {
         self.manager.listen(event.into(), EventTarget::App, handler)
       }
 
+      /// Listen to an event on this app only once.
+      ///
+      /// See [`Self::listen`] for more information.
+      pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
+      where
+        F: FnOnce(Event) + Send + 'static,
+      {
+        self.manager.once(event.into(), EventTarget::App, handler)
+      }
+
       /// Unlisten to an event on this app.
       ///
       /// # Examples
       ///
       /// ```
-      /// use tauri::Manager;
-      ///
       /// tauri::Builder::default()
       ///   .setup(|app| {
       ///     let handler = app.listen("component-loaded", move |event| {
@@ -831,14 +838,206 @@ macro_rules! shared_app_impl {
         self.manager.unlisten(id)
       }
 
+      /// Emits an event to all [targets](EventTarget).
+      ///
+      /// # Examples
+      /// ```
+      /// #[tauri::command]
+      /// fn synchronize(app: tauri::AppHandle) {
+      ///   // emits the synchronized event to all webviews
+      ///   app.emit("synchronized", ());
+      /// }
+      /// ```
+      pub 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::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);
+      ///   }
+      /// }
+      /// ```
+      pub 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::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,
+      ///     });
+      ///   }
+      /// }
+      /// ```
+      pub 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> Listener<R> for $app {
+      /// Listen to an event on this app.
+      ///
+      /// # Examples
+      ///
+      /// ```
+      /// use tauri::Listener;
+      ///
+      /// tauri::Builder::default()
+      ///   .setup(|app| {
+      ///     app.listen("component-loaded", move |event| {
+      ///       println!("window just loaded a component");
+      ///     });
+      ///
+      ///     Ok(())
+      ///   });
+      /// ```
+      fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
+      where
+        F: Fn(Event) + Send + 'static,
+      {
+        self.listen(event.into(), handler)
+      }
+
       /// Listen to an event on this app only once.
       ///
       /// See [`Self::listen`] for more information.
-      pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
+      fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
       where
         F: FnOnce(Event) + Send + 'static,
       {
-        self.manager.once(event.into(), EventTarget::App, handler)
+        self.once(event, handler)
+      }
+
+      /// Unlisten to an event on this app.
+      ///
+      /// # Examples
+      ///
+      /// ```
+      /// use tauri::Listener;
+      ///
+      /// tauri::Builder::default()
+      ///   .setup(|app| {
+      ///     let handler = app.listen("component-loaded", move |event| {
+      ///       println!("app just loaded a component");
+      ///     });
+      ///
+      ///     // stop listening to the event when you do not need it anymore
+      ///     app.unlisten(handler);
+      ///
+      ///     Ok(())
+      ///   });
+      /// ```
+      fn unlisten(&self, id: EventId) {
+        self.unlisten(id)
+      }
+    }
+
+    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.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.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.emit_filter(event, payload, filter)
       }
     }
   };
@@ -1199,7 +1398,7 @@ tauri::Builder::default()
   #[must_use]
   pub fn setup<F>(mut self, setup: F) -> Self
   where
-    F: FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send + 'static,
+    F: FnOnce(&mut App<R>) -> std::result::Result<(), Box<dyn std::error::Error>> + Send + 'static,
   {
     self.setup = Box::new(setup);
     self

+ 1 - 1
core/tauri/src/event/plugin.rs

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

+ 168 - 172
core/tauri/src/lib.rs

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

+ 63 - 18
core/tauri/src/manager/mod.rs

@@ -480,6 +480,61 @@ impl<R: Runtime> AppManager<R> {
     self.listeners().once(event, target, handler)
   }
 
+  #[cfg_attr(
+    feature = "tracing",
+    tracing::instrument("app::emit", skip(self, payload))
+  )]
+  pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
+    assert_event_name_is_valid(event);
+
+    #[cfg(feature = "tracing")]
+    let _span = tracing::debug_span!("emit::run").entered();
+    let emit_args = EmitArgs::new(event, payload)?;
+
+    let listeners = self.listeners();
+
+    listeners.emit_js(self.webview.webviews_lock().values(), event, &emit_args)?;
+    listeners.emit(emit_args)?;
+
+    Ok(())
+  }
+
+  #[cfg_attr(
+    feature = "tracing",
+    tracing::instrument("app::emit::to", skip(self, target, payload), fields(target))
+  )]
+  pub fn emit_to<I, S>(&self, target: I, event: &str, payload: S) -> crate::Result<()>
+  where
+    I: Into<EventTarget>,
+    S: Serialize + Clone,
+  {
+    let target = target.into();
+    #[cfg(feature = "tracing")]
+    tracing::Span::current().record("target", format!("{target:?}"));
+
+    match target {
+      // if targeting all, emit to all using emit without filter
+      EventTarget::Any => self.emit(event, payload),
+
+      // if targeting any label, emit using emit_filter and filter labels
+      EventTarget::AnyLabel {
+        label: target_label,
+      } => self.emit_filter(event, payload, |t| match t {
+        EventTarget::Window { label }
+        | EventTarget::Webview { label }
+        | EventTarget::WebviewWindow { label } => label == &target_label,
+        _ => false,
+      }),
+
+      // otherwise match same target
+      _ => self.emit_filter(event, payload, |t| t == &target),
+    }
+  }
+
+  #[cfg_attr(
+    feature = "tracing",
+    tracing::instrument("app::emit::filter", skip(self, payload, filter))
+  )]
   pub fn emit_filter<S, F>(&self, event: &str, payload: S, filter: F) -> crate::Result<()>
   where
     S: Serialize + Clone,
@@ -505,21 +560,6 @@ impl<R: Runtime> AppManager<R> {
     Ok(())
   }
 
-  pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
-    assert_event_name_is_valid(event);
-
-    #[cfg(feature = "tracing")]
-    let _span = tracing::debug_span!("emit::run").entered();
-    let emit_args = EmitArgs::new(event, payload)?;
-
-    let listeners = self.listeners();
-
-    listeners.emit_js(self.webview.webviews_lock().values(), event, &emit_args)?;
-    listeners.emit(emit_args)?;
-
-    Ok(())
-  }
-
   pub fn get_window(&self, label: &str) -> Option<Window<R>> {
     self.window.windows_lock().get(label).cloned()
   }
@@ -624,7 +664,8 @@ mod test {
     test::{mock_app, MockRuntime},
     webview::WebviewBuilder,
     window::WindowBuilder,
-    App, Manager, StateManager, Webview, WebviewWindow, WebviewWindowBuilder, Window, Wry,
+    App, Emitter, Listener, Manager, StateManager, Webview, WebviewWindow, WebviewWindowBuilder,
+    Window, Wry,
   };
 
   use super::AppManager;
@@ -771,7 +812,11 @@ mod test {
     run_emit_test("emit (webview_window)", webview_window, &rx);
   }
 
-  fn run_emit_test<M: Manager<MockRuntime>>(kind: &str, m: M, rx: &Receiver<(&str, String)>) {
+  fn run_emit_test<M: Manager<MockRuntime> + Emitter<MockRuntime>>(
+    kind: &str,
+    m: M,
+    rx: &Receiver<(&str, String)>,
+  ) {
     let mut received = Vec::new();
     let payload = "global-payload";
     m.emit(TEST_EVENT_NAME, payload).unwrap();
@@ -844,7 +889,7 @@ mod test {
     );
   }
 
-  fn run_emit_to_test<M: Manager<MockRuntime>>(
+  fn run_emit_to_test<M: Manager<MockRuntime> + Emitter<MockRuntime>>(
     kind: &str,
     m: &M,
     window: &Window<MockRuntime>,

+ 251 - 3
core/tauri/src/webview/mod.rs

@@ -34,7 +34,8 @@ use crate::{
   },
   manager::{webview::WebviewLabelDef, AppManager},
   sealed::{ManagerBase, RuntimeOrDispatch},
-  AppHandle, Event, EventId, EventLoopMessage, Manager, ResourceTable, Runtime, Window,
+  AppHandle, Emitter, Event, EventId, EventLoopMessage, Listener, Manager, ResourceTable, Runtime,
+  Window,
 };
 
 use std::{
@@ -1476,7 +1477,7 @@ tauri::Builder::default()
   .setup(|app| {
     let webview = app.get_webview("main").unwrap();
     webview.listen("component-loaded", move |event| {
-      println!("window just loaded a component");
+      println!("webview just loaded a component");
     });
 
     Ok(())
@@ -1539,12 +1540,259 @@ tauri::Builder::default()
   {
     self.manager.once(
       event.into(),
-      EventTarget::Webview {
+      EventTarget::Window {
         label: self.label().to_string(),
       },
       handler,
     )
   }
+
+  /// Emits an event to all [targets](EventTarget).
+  ///
+  /// # Examples
+  #[cfg_attr(
+    feature = "unstable",
+    doc = r####"
+```
+#[tauri::command]
+fn synchronize(webview: tauri::Webview) {
+  // emits the synchronized event to all webviews
+  webview.emit("synchronized", ());
+}
+  ```
+  "####
+  )]
+  pub 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::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);
+  }
+}
+```
+"####
+  )]
+  pub 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::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,
+    });
+  }
+}
+  ```
+  "####
+  )]
+  pub 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> Listener<R> for Webview<R> {
+  /// Listen to an event on this webview.
+  ///
+  /// # Examples
+  #[cfg_attr(
+    feature = "unstable",
+    doc = r####"
+```
+use tauri::Listener;
+
+tauri::Builder::default()
+  .setup(|app| {
+    let webview = app.get_webview("main").unwrap();
+    webview.listen("component-loaded", move |event| {
+      println!("webview just loaded a component");
+    });
+
+    Ok(())
+  });
+```
+  "####
+  )]
+  fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
+  where
+    F: Fn(Event) + Send + 'static,
+  {
+    self.listen(event, handler)
+  }
+
+  /// Listen to an event on this webview only once.
+  ///
+  /// See [`Self::listen`] for more information.
+  fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
+  where
+    F: FnOnce(Event) + Send + 'static,
+  {
+    self.once(event, handler)
+  }
+
+  /// Unlisten to an event on this webview.
+  ///
+  /// # Examples
+  #[cfg_attr(
+    feature = "unstable",
+    doc = r####"
+```
+use tauri::{Manager, Listener};
+
+tauri::Builder::default()
+  .setup(|app| {
+    let webview = app.get_webview("main").unwrap();
+    let webview_ = webview.clone();
+    let handler = webview.listen("component-loaded", move |event| {
+      println!("webview just loaded a component");
+
+      // we no longer need to listen to the event
+      // we also could have used `webview.once` instead
+      webview_.unlisten(event.id());
+    });
+
+    // stop listening to the event when you do not need it anymore
+    webview.unlisten(handler);
+
+    Ok(())
+  });
+```
+  "####
+  )]
+  fn unlisten(&self, id: EventId) {
+    self.unlisten(id)
+  }
+}
+
+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.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.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.emit_filter(event, payload, filter)
+  }
 }
 
 impl<R: Runtime> Manager<R> for Webview<R> {

+ 412 - 244
core/tauri/src/webview/webview_window.rs

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

+ 249 - 1
core/tauri/src/window/mod.rs

@@ -28,7 +28,8 @@ use crate::{
   sealed::{ManagerBase, RuntimeOrDispatch},
   utils::config::{WindowConfig, WindowEffectsConfig},
   webview::WebviewBuilder,
-  EventLoopMessage, Manager, ResourceTable, Runtime, Theme, Webview, WindowEvent,
+  Emitter, EventLoopMessage, Listener, Manager, ResourceTable, Runtime, Theme, Webview,
+  WindowEvent,
 };
 #[cfg(desktop)]
 use crate::{
@@ -2089,6 +2090,253 @@ tauri::Builder::default()
       handler,
     )
   }
+
+  /// Emits an event to all [targets](EventTarget).
+  ///
+  /// # Examples
+  #[cfg_attr(
+    feature = "unstable",
+    doc = r####"
+```
+#[tauri::command]
+fn synchronize(window: tauri::Window) {
+  // emits the synchronized event to all webviews
+  window.emit("synchronized", ());
+}
+  ```
+  "####
+  )]
+  pub 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::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);
+  }
+}
+```
+"####
+  )]
+  pub 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::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,
+    });
+  }
+}
+  ```
+  "####
+  )]
+  pub 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> Listener<R> for Window<R> {
+  /// Listen to an event on this window.
+  ///
+  /// # Examples
+  #[cfg_attr(
+    feature = "unstable",
+    doc = r####"
+```
+use tauri::Listener;
+
+tauri::Builder::default()
+  .setup(|app| {
+    let window = app.get_window("main").unwrap();
+    window.listen("component-loaded", move |event| {
+      println!("window just loaded a component");
+    });
+
+    Ok(())
+  });
+```
+  "####
+  )]
+  fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
+  where
+    F: Fn(Event) + Send + 'static,
+  {
+    self.listen(event, handler)
+  }
+
+  /// Listen to an event on this window only once.
+  ///
+  /// See [`Self::listen`] for more information.
+  fn once<F>(&self, event: impl Into<String>, handler: F) -> EventId
+  where
+    F: FnOnce(Event) + Send + 'static,
+  {
+    self.once(event, handler)
+  }
+
+  /// Unlisten to an event on this window.
+  ///
+  /// # Examples
+  #[cfg_attr(
+    feature = "unstable",
+    doc = r####"
+```
+use tauri::{Manager, Listener};
+
+tauri::Builder::default()
+  .setup(|app| {
+    let window = app.get_window("main").unwrap();
+    let window_ = window.clone();
+    let handler = window.listen("component-loaded", move |event| {
+      println!("window just loaded a component");
+
+      // we no longer need to listen to the event
+      // we also could have used `window.once` instead
+      window_.unlisten(event.id());
+    });
+
+    // stop listening to the event when you do not need it anymore
+    window.unlisten(handler);
+
+    Ok(())
+  });
+```
+  "####
+  )]
+  fn unlisten(&self, id: EventId) {
+    self.unlisten(id)
+  }
+}
+
+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.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.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.emit_filter(event, payload, filter)
+  }
 }
 
 /// The [`WindowEffectsConfig`] object builder