#![allow(clippy::tabs_in_doc_comments)]
pub(crate) mod menu;
use std::{
fmt,
hash::{Hash, Hasher},
path::PathBuf,
sync::Arc
};
pub use menu::{MenuEvent, MenuHandle};
use millennium_macros::default_runtime;
use serde::Serialize;
#[cfg(windows)]
use windows::Win32::Foundation::HWND;
use crate::{
app::AppHandle,
command::{CommandArg, CommandItem},
event::{Event, EventHandler},
hooks::{InvokePayload, InvokeResponder},
manager::WindowManager,
runtime::{
http::{Request as HttpRequest, Response as HttpResponse},
menu::Menu,
monitor::Monitor as RuntimeMonitor,
webview::{WebviewAttributes, WindowBuilder as _},
window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, JsEventListenerKey, PendingWindow
},
Dispatch, RuntimeHandle, UserAttentionType
},
sealed::ManagerBase,
sealed::RuntimeOrDispatch,
utils::config::WindowUrl,
CursorIcon, EventLoopMessage, Icon, Invoke, InvokeError, InvokeMessage, InvokeResolver, Manager, PageLoadPayload, Runtime, Theme, WindowEvent
};
#[derive(Clone, Serialize)]
struct WindowCreatedEvent {
label: String
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Monitor {
pub(crate) name: Option<String>,
pub(crate) size: PhysicalSize<u32>,
pub(crate) position: PhysicalPosition<i32>,
pub(crate) scale_factor: f64
}
impl From<RuntimeMonitor> for Monitor {
fn from(monitor: RuntimeMonitor) -> Self {
Self {
name: monitor.name,
size: monitor.size,
position: monitor.position,
scale_factor: monitor.scale_factor
}
}
}
impl Monitor {
pub fn name(&self) -> Option<&String> {
self.name.as_ref()
}
pub fn size(&self) -> &PhysicalSize<u32> {
&self.size
}
pub fn position(&self) -> &PhysicalPosition<i32> {
&self.position
}
pub fn scale_factor(&self) -> f64 {
self.scale_factor
}
}
#[default_runtime(crate::MillenniumWebview, millennium_webview)]
pub struct WindowBuilder<'a, R: Runtime> {
manager: WindowManager<R>,
runtime: RuntimeOrDispatch<'a, R>,
app_handle: AppHandle<R>,
label: String,
pub(crate) window_builder: <R::Dispatcher as Dispatch<EventLoopMessage>>::WindowBuilder,
pub(crate) webview_attributes: WebviewAttributes,
web_resource_request_handler: Option<Box<dyn Fn(&HttpRequest, &mut HttpResponse) + Send + Sync>>
}
impl<'a, R: Runtime> fmt::Debug for WindowBuilder<'a, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WindowBuilder")
.field("manager", &self.manager)
.field("app_handle", &self.app_handle)
.field("label", &self.label)
.field("window_builder", &self.window_builder)
.field("webview_attributes", &self.webview_attributes)
.finish()
}
}
impl<'a, R: Runtime> WindowBuilder<'a, R> {
pub fn new<M: Manager<R>, L: Into<String>>(manager: &'a M, label: L, url: WindowUrl) -> Self {
let runtime = manager.runtime();
let app_handle = manager.app_handle();
Self {
manager: manager.manager().clone(),
runtime,
app_handle,
label: label.into(),
window_builder: <R::Dispatcher as Dispatch<EventLoopMessage>>::WindowBuilder::new(),
webview_attributes: WebviewAttributes::new(url),
web_resource_request_handler: None
}
}
pub fn on_web_resource_request<F: Fn(&HttpRequest, &mut HttpResponse) + Send + Sync + 'static>(mut self, f: F) -> Self {
self.web_resource_request_handler.replace(Box::new(f));
self
}
pub fn build(mut self) -> crate::Result<Window<R>> {
let web_resource_request_handler = self.web_resource_request_handler.take();
let pending = PendingWindow::new(self.window_builder.clone(), self.webview_attributes.clone(), self.label.clone())?;
let labels = self.manager.labels().into_iter().collect::<Vec<_>>();
let pending = self
.manager
.prepare_window(self.app_handle.clone(), pending, &labels, web_resource_request_handler)?;
let window = match &mut self.runtime {
RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending),
RuntimeOrDispatch::RuntimeHandle(handle) => handle.create_window(pending),
RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_window(pending)
}
.map(|window| self.manager.attach_window(self.app_handle.clone(), window))?;
self.manager
.emit_filter("millennium://window-created", None, Some(WindowCreatedEvent { label: window.label().into() }), |w| w != &window)?;
Ok(window)
}
#[must_use]
pub fn menu(mut self, menu: Menu) -> Self {
self.window_builder = self.window_builder.menu(menu);
self
}
#[must_use]
pub fn center(mut self) -> Self {
self.window_builder = self.window_builder.center();
self
}
#[must_use]
pub fn position(mut self, x: f64, y: f64) -> Self {
self.window_builder = self.window_builder.position(x, y);
self
}
#[must_use]
pub fn set_inner_size(mut self, width: f64, height: f64) -> Self {
self.window_builder = self.window_builder.set_inner_size(width, height);
self
}
#[must_use]
pub fn min_inner_size(mut self, min_width: f64, min_height: f64) -> Self {
self.window_builder = self.window_builder.min_inner_size(min_width, min_height);
self
}
#[must_use]
pub fn max_inner_size(mut self, max_width: f64, max_height: f64) -> Self {
self.window_builder = self.window_builder.max_inner_size(max_width, max_height);
self
}
#[must_use]
pub fn resizable(mut self, resizable: bool) -> Self {
self.window_builder = self.window_builder.resizable(resizable);
self
}
#[must_use]
pub fn title<S: Into<String>>(mut self, title: S) -> Self {
self.window_builder = self.window_builder.title(title);
self
}
#[must_use]
pub fn fullscreen(mut self, fullscreen: bool) -> Self {
self.window_builder = self.window_builder.fullscreen(fullscreen);
self
}
#[must_use]
pub fn focus(mut self) -> Self {
self.window_builder = self.window_builder.focus();
self
}
#[must_use]
pub fn maximized(mut self, maximized: bool) -> Self {
self.window_builder = self.window_builder.maximized(maximized);
self
}
#[must_use]
pub fn visible(mut self, visible: bool) -> Self {
self.window_builder = self.window_builder.visible(visible);
self
}
#[must_use]
pub fn theme(mut self, theme: Option<Theme>) -> Self {
self.window_builder = self.window_builder.theme(theme);
self
}
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
#[cfg_attr(doc_cfg, doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api"))))]
#[must_use]
pub fn transparent(mut self, transparent: bool) -> Self {
self.window_builder = self.window_builder.transparent(transparent);
self
}
#[must_use]
pub fn decorations(mut self, decorations: bool) -> Self {
self.window_builder = self.window_builder.decorations(decorations);
self
}
#[cfg(target_os = "windows")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "windows")))]
pub fn titlebar_hidden(mut self, titlebar_hidden: bool) -> Self {
self.window_builder = self.window_builder.titlebar_hidden(titlebar_hidden);
self
}
#[must_use]
pub fn always_on_top(mut self, always_on_top: bool) -> Self {
self.window_builder = self.window_builder.always_on_top(always_on_top);
self
}
pub fn icon(mut self, icon: Icon) -> crate::Result<Self> {
self.window_builder = self.window_builder.icon(icon.try_into()?)?;
Ok(self)
}
#[must_use]
pub fn skip_taskbar(mut self, skip: bool) -> Self {
self.window_builder = self.window_builder.skip_taskbar(skip);
self
}
#[cfg(windows)]
#[must_use]
pub fn parent_window(mut self, parent: HWND) -> Self {
self.window_builder = self.window_builder.parent_window(parent);
self
}
#[cfg(target_os = "macos")]
#[must_use]
pub fn parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
self.window_builder = self.window_builder.parent_window(parent);
self
}
#[cfg(windows)]
#[must_use]
pub fn owner_window(mut self, owner: HWND) -> Self {
self.window_builder = self.window_builder.owner_window(owner);
self
}
#[must_use]
pub fn initialization_script(mut self, script: &str) -> Self {
self.webview_attributes.initialization_scripts.push(script.to_string());
self
}
#[must_use]
pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
self.webview_attributes.data_directory.replace(data_directory);
self
}
#[must_use]
pub fn disable_file_drop_handler(mut self) -> Self {
self.webview_attributes.file_drop_handler_enabled = false;
self
}
#[must_use]
pub fn enable_clipboard_access(mut self) -> Self {
self.webview_attributes.clipboard = true;
self
}
}
#[default_runtime(crate::MillenniumWebview, millennium_webview)]
#[derive(Debug)]
pub struct Window<R: Runtime> {
window: DetachedWindow<EventLoopMessage, R>,
manager: WindowManager<R>,
pub(crate) app_handle: AppHandle<R>
}
#[cfg(any(windows, target_os = "macos"))]
#[cfg_attr(doc_cfg, doc(cfg(any(windows, target_os = "macos"))))]
unsafe impl<R: Runtime> raw_window_handle::HasRawWindowHandle for Window<R> {
#[cfg(windows)]
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
let mut handle = raw_window_handle::Win32Handle::empty();
handle.hwnd = self.hwnd().expect("failed to get window `hwnd`").0 as *mut _;
raw_window_handle::RawWindowHandle::Win32(handle)
}
#[cfg(target_os = "macos")]
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
let mut handle = raw_window_handle::AppKitHandle::empty();
handle.ns_window = self.ns_window().expect("failed to get window's `ns_window`");
raw_window_handle::RawWindowHandle::AppKit(handle)
}
}
impl<R: Runtime> Clone for Window<R> {
fn clone(&self) -> Self {
Self {
window: self.window.clone(),
manager: self.manager.clone(),
app_handle: self.app_handle.clone()
}
}
}
impl<R: Runtime> Hash for Window<R> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.window.label.hash(state)
}
}
impl<R: Runtime> Eq for Window<R> {}
impl<R: Runtime> PartialEq for Window<R> {
fn eq(&self, other: &Self) -> bool {
self.window.label.eq(&other.window.label)
}
}
impl<R: Runtime> Manager<R> for Window<R> {}
impl<R: Runtime> ManagerBase<R> for Window<R> {
fn manager(&self) -> &WindowManager<R> {
&self.manager
}
fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
RuntimeOrDispatch::Dispatch(self.dispatcher())
}
fn managed_app_handle(&self) -> AppHandle<R> {
self.app_handle.clone()
}
}
impl<'de, R: Runtime> CommandArg<'de, R> for Window<R> {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
Ok(command.message.window())
}
}
#[cfg(feature = "millennium_webview")]
#[cfg_attr(doc_Cfg, doc(cfg(feature = "millennium_webview")))]
pub struct PlatformWebview(millennium_runtime_webview::Webview);
#[cfg(feature = "millennium_webview")]
impl PlatformWebview {
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
#[cfg_attr(
doc_cfg,
doc(cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))
)]
pub fn inner(&self) -> std::rc::Rc<webkit2gtk::WebView> {
self.0.clone()
}
#[cfg(windows)]
#[cfg_attr(doc_cfg, doc(cfg(windows)))]
pub fn controller(&self) -> webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller {
self.0.controller.clone()
}
#[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
pub fn inner(&self) -> cocoa::base::id {
self.0.webview
}
#[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
pub fn controller(&self) -> cocoa::base::id {
self.0.manager
}
#[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
pub fn ns_window(&self) -> cocoa::base::id {
self.0.ns_window
}
}
#[cfg(feature = "millennium_webview")]
impl Window<crate::MillenniumWebview> {
#[cfg_attr(doc_cfg, doc(cfg(feature = "millennium_webview")))]
pub fn with_webview<F: FnOnce(PlatformWebview) + Send + 'static>(&self, f: F) -> crate::Result<()> {
self.window.dispatcher.with_webview(|w| f(PlatformWebview(w))).map_err(Into::into)
}
}
impl<R: Runtime> Window<R> {
pub(crate) fn new(manager: WindowManager<R>, window: DetachedWindow<EventLoopMessage, R>, app_handle: AppHandle<R>) -> Self {
Self { window, manager, app_handle }
}
pub fn builder<'a, M: Manager<R>, L: Into<String>>(manager: &'a M, label: L, url: WindowUrl) -> WindowBuilder<'a, R> {
WindowBuilder::<'a, R>::new(manager, label.into(), url)
}
pub(crate) fn invoke_responder(&self) -> Arc<InvokeResponder<R>> {
self.manager.invoke_responder()
}
pub(crate) fn dispatcher(&self) -> R::Dispatcher {
self.window.dispatcher.clone()
}
pub fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
self.window.dispatcher.run_on_main_thread(f).map_err(Into::into)
}
pub fn label(&self) -> &str {
&self.window.label
}
pub fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) {
self.window.dispatcher.on_window_event(move |event| f(&event.clone().into()));
}
pub fn on_menu_event<F: Fn(MenuEvent) + Send + 'static>(&self, f: F) -> uuid::Uuid {
let menu_ids = self.window.menu_ids.clone();
self.window.dispatcher.on_menu_event(move |event| {
f(MenuEvent {
menu_item_id: menu_ids.lock().unwrap().get(&event.menu_item_id).unwrap().clone()
})
})
}
}
impl<R: Runtime> Window<R> {
pub fn menu_handle(&self) -> MenuHandle<R> {
MenuHandle {
ids: self.window.menu_ids.clone(),
dispatcher: self.dispatcher()
}
}
pub fn scale_factor(&self) -> crate::Result<f64> {
self.window.dispatcher.scale_factor().map_err(Into::into)
}
pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.dispatcher.inner_position().map_err(Into::into)
}
pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.dispatcher.outer_position().map_err(Into::into)
}
pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.dispatcher.inner_size().map_err(Into::into)
}
pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.dispatcher.outer_size().map_err(Into::into)
}
pub fn is_fullscreen(&self) -> crate::Result<bool> {
self.window.dispatcher.is_fullscreen().map_err(Into::into)
}
pub fn is_maximized(&self) -> crate::Result<bool> {
self.window.dispatcher.is_maximized().map_err(Into::into)
}
pub fn is_decorated(&self) -> crate::Result<bool> {
self.window.dispatcher.is_decorated().map_err(Into::into)
}
pub fn is_resizable(&self) -> crate::Result<bool> {
self.window.dispatcher.is_resizable().map_err(Into::into)
}
pub fn is_visible(&self) -> crate::Result<bool> {
self.window.dispatcher.is_visible().map_err(Into::into)
}
pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.dispatcher.current_monitor().map(|m| m.map(Into::into)).map_err(Into::into)
}
pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.dispatcher.primary_monitor().map(|m| m.map(Into::into)).map_err(Into::into)
}
pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
self.window
.dispatcher
.available_monitors()
.map(|m| m.into_iter().map(Into::into).collect())
.map_err(Into::into)
}
#[cfg(target_os = "macos")]
pub fn ns_window(&self) -> crate::Result<*mut std::ffi::c_void> {
self.window.dispatcher.ns_window().map_err(Into::into)
}
#[cfg(windows)]
pub fn hwnd(&self) -> crate::Result<HWND> {
self.window.dispatcher.hwnd().map_err(Into::into)
}
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
pub fn gtk_window(&self) -> crate::Result<gtk::ApplicationWindow> {
self.window.dispatcher.gtk_window().map_err(Into::into)
}
pub fn theme(&self) -> crate::Result<Theme> {
self.window.dispatcher.theme().map_err(Into::into)
}
}
impl<R: Runtime> Window<R> {
pub fn center(&self) -> crate::Result<()> {
self.window.dispatcher.center().map_err(Into::into)
}
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> crate::Result<()> {
self.window.dispatcher.request_user_attention(request_type).map_err(Into::into)
}
pub fn print(&self) -> crate::Result<()> {
self.window.dispatcher.print().map_err(Into::into)
}
pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
self.window.dispatcher.set_resizable(resizable).map_err(Into::into)
}
pub fn set_title(&self, title: &str) -> crate::Result<()> {
self.window.dispatcher.set_title(title.to_string()).map_err(Into::into)
}
pub fn maximize(&self) -> crate::Result<()> {
self.window.dispatcher.maximize().map_err(Into::into)
}
pub fn unmaximize(&self) -> crate::Result<()> {
self.window.dispatcher.unmaximize().map_err(Into::into)
}
pub fn minimize(&self) -> crate::Result<()> {
self.window.dispatcher.minimize().map_err(Into::into)
}
pub fn unminimize(&self) -> crate::Result<()> {
self.window.dispatcher.unminimize().map_err(Into::into)
}
pub fn show(&self) -> crate::Result<()> {
self.window.dispatcher.show().map_err(Into::into)
}
pub fn hide(&self) -> crate::Result<()> {
self.window.dispatcher.hide().map_err(Into::into)
}
pub fn close(&self) -> crate::Result<()> {
self.window.dispatcher.close().map_err(Into::into)
}
pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
self.window.dispatcher.set_decorations(decorations).map_err(Into::into)
}
pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
self.window.dispatcher.set_always_on_top(always_on_top).map_err(Into::into)
}
pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
self.window.dispatcher.set_size(size.into()).map_err(Into::into)
}
pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self.window.dispatcher.set_min_size(size.map(|s| s.into())).map_err(Into::into)
}
pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self.window.dispatcher.set_max_size(size.map(|s| s.into())).map_err(Into::into)
}
pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self.window.dispatcher.set_position(position.into()).map_err(Into::into)
}
pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
self.window.dispatcher.set_fullscreen(fullscreen).map_err(Into::into)
}
pub fn set_focus(&self) -> crate::Result<()> {
self.window.dispatcher.set_focus().map_err(Into::into)
}
pub fn set_icon(&self, icon: Icon) -> crate::Result<()> {
self.window.dispatcher.set_icon(icon.try_into()?).map_err(Into::into)
}
pub fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
self.window.dispatcher.set_skip_taskbar(skip).map_err(Into::into)
}
pub fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
self.window.dispatcher.set_cursor_grab(grab).map_err(Into::into)
}
pub fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
self.window.dispatcher.set_cursor_visible(visible).map_err(Into::into)
}
pub fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
self.window.dispatcher.set_cursor_icon(icon).map_err(Into::into)
}
pub fn set_cursor_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self.window.dispatcher.set_cursor_position(position).map_err(Into::into)
}
pub fn start_dragging(&self) -> crate::Result<()> {
self.window.dispatcher.start_dragging().map_err(Into::into)
}
}
impl<R: Runtime> Window<R> {
pub fn on_message(self, payload: InvokePayload) -> crate::Result<()> {
let manager = self.manager.clone();
match payload.cmd.as_str() {
"__initialized" => {
let payload: PageLoadPayload = serde_json::from_value(payload.inner)?;
manager.run_on_page_load(self, payload);
}
_ => {
let message = InvokeMessage::new(self.clone(), manager.state(), payload.cmd.to_string(), payload.inner);
let resolver = InvokeResolver::new(self, payload.callback, payload.error);
let invoke = Invoke { message, resolver };
if let Some(module) = &payload.millennium_module {
crate::endpoints::handle(module.to_string(), invoke, manager.config(), manager.package_info());
} else if payload.cmd.starts_with("plugin:") {
manager.extend_api(invoke);
} else {
manager.run_invoke_handler(invoke);
}
}
}
Ok(())
}
pub fn eval(&self, js: &str) -> crate::Result<()> {
self.window.dispatcher.eval_script(js).map_err(Into::into)
}
pub(crate) fn register_js_listener(&self, window_label: Option<String>, event: String, id: u64) {
self.window
.js_event_listeners
.lock()
.unwrap()
.entry(JsEventListenerKey { window_label, event })
.or_insert_with(Default::default)
.insert(id);
}
pub(crate) fn unregister_js_listener(&self, id: u64) {
let mut empty = None;
let mut js_listeners = self.window.js_event_listeners.lock().unwrap();
let iter = js_listeners.iter_mut();
for (key, ids) in iter {
if ids.contains(&id) {
ids.remove(&id);
if ids.is_empty() {
empty.replace(key.clone());
}
break;
}
}
if let Some(key) = empty {
js_listeners.remove(&key);
}
}
pub(crate) fn has_js_listener(&self, window_label: Option<String>, event: &str) -> bool {
self.window
.js_event_listeners
.lock()
.unwrap()
.contains_key(&JsEventListenerKey { window_label, event: event.into() })
}
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(doc_cfg, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn open_devtools(&self) {
self.window.dispatcher.open_devtools();
}
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(doc_cfg, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn close_devtools(&self) {
self.window.dispatcher.close_devtools();
}
#[cfg(any(debug_assertions, feature = "devtools"))]
#[cfg_attr(doc_cfg, doc(cfg(any(debug_assertions, feature = "devtools"))))]
pub fn is_devtools_open(&self) -> bool {
self.window.dispatcher.is_devtools_open().unwrap_or_default()
}
}
impl<R: Runtime> Window<R> {
pub fn emit_and_trigger<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.trigger(event, Some(serde_json::to_string(&payload)?));
self.emit(event, payload)
}
pub(crate) fn emit_internal<S: Serialize>(&self, event: &str, source_window_label: Option<&str>, payload: S) -> crate::Result<()> {
self.eval(&format!(
"window['{}']({{event: {}, windowLabel: {}, payload: {}}})",
self.manager.event_emit_function_name(),
serde_json::to_string(event)?,
serde_json::to_string(&source_window_label)?,
serde_json::to_value(payload)?,
))?;
Ok(())
}
pub fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> crate::Result<()> {
self.manager
.emit_filter(event, Some(self.label()), payload, |w| w.has_js_listener(None, event) || w.has_js_listener(Some(self.label().into()), event))?;
Ok(())
}
pub fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static
{
let label = self.window.label.clone();
self.manager.listen(event.into(), Some(label), handler)
}
pub fn unlisten(&self, handler_id: EventHandler) {
self.manager.unlisten(handler_id)
}
pub fn once<F>(&self, event: impl Into<String>, handler: F) -> EventHandler
where
F: FnOnce(Event) + Send + 'static
{
let label = self.window.label.clone();
self.manager.once(event.into(), Some(label), handler)
}
pub fn trigger(&self, event: &str, data: Option<String>) {
let label = self.window.label.clone();
self.manager.trigger(event, Some(label), data)
}
}
#[cfg(test)]
mod tests {
#[test]
fn window_is_send_sync() {
crate::test_utils::assert_send::<super::Window>();
crate::test_utils::assert_sync::<super::Window>();
}
}