diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 557d05bc4..8929ee9d1 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -53,8 +53,11 @@ pub(crate) struct SharedContext { } impl App { - pub fn new(cfg: Config, virtual_dom: VirtualDom) -> (EventLoop, Self) { - let event_loop = EventLoopBuilder::::with_user_event().build(); + pub fn new(mut cfg: Config, virtual_dom: VirtualDom) -> (EventLoop, Self) { + let event_loop = cfg + .event_loop + .take() + .unwrap_or_else(|| EventLoopBuilder::::with_user_event().build()); let app = Self { window_behavior: cfg.last_window_close_behavior, diff --git a/packages/desktop/src/config.rs b/packages/desktop/src/config.rs index 68f891d33..61fe76d60 100644 --- a/packages/desktop/src/config.rs +++ b/packages/desktop/src/config.rs @@ -1,12 +1,22 @@ use dioxus_core::LaunchConfig; use std::borrow::Cow; use std::path::PathBuf; +use tao::event_loop::{EventLoop, EventLoopWindowTarget}; use tao::window::{Icon, WindowBuilder}; use wry::http::{Request as HttpRequest, Response as HttpResponse}; use wry::RequestAsyncResponder; +use crate::ipc::UserWindowEvent; use crate::menubar::{default_menu_bar, DioxusMenu}; +type CustomEventHandler = Box< + dyn 'static + + for<'a> FnMut( + &tao::event::Event<'a, UserWindowEvent>, + &EventLoopWindowTarget, + ), +>; + /// The behaviour of the application when the last window is closed. #[derive(Copy, Clone, Eq, PartialEq)] #[non_exhaustive] @@ -37,6 +47,7 @@ impl From for Option { /// The configuration for the desktop application. pub struct Config { + pub(crate) event_loop: Option>, pub(crate) window: WindowBuilder, pub(crate) menu: MenuBuilderState, pub(crate) protocols: Vec, @@ -50,6 +61,7 @@ pub struct Config { pub(crate) root_name: String, pub(crate) background_color: Option<(u8, u8, u8, u8)>, pub(crate) last_window_close_behavior: WindowCloseBehaviour, + pub(crate) custom_event_handler: Option, } impl LaunchConfig for Config {} @@ -80,6 +92,7 @@ impl Config { Self { window, + event_loop: None, menu: MenuBuilderState::Unset, protocols: Vec::new(), asynchronous_protocols: Vec::new(), @@ -92,6 +105,7 @@ impl Config { root_name: "main".to_string(), background_color: None, last_window_close_behavior: WindowCloseBehaviour::LastWindowExitsApp, + custom_event_handler: None, } } @@ -121,6 +135,12 @@ impl Config { self } + /// Set the event loop to be used + pub fn with_event_loop(mut self, event_loop: EventLoop) -> Self { + self.event_loop = Some(event_loop); + self + } + /// Set the configuration for the window. pub fn with_window(mut self, window: WindowBuilder) -> Self { // We need to do a swap because the window builder only takes itself as muy self @@ -138,6 +158,16 @@ impl Config { self } + /// Sets a custom callback to run whenever the event pool receives an event. + pub fn with_custom_event_handler( + mut self, + f: impl FnMut(&tao::event::Event<'_, UserWindowEvent>, &EventLoopWindowTarget) + + 'static, + ) -> Self { + self.custom_event_handler = Some(Box::new(f)); + self + } + /// Set a custom protocol pub fn with_custom_protocol(mut self, name: impl ToString, handler: F) -> Self where diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 6a8d64ea2..ede54c694 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -12,13 +12,18 @@ use tao::event::{Event, StartCause, WindowEvent}; /// /// This will block the main thread, and *must* be spawned on the main thread. This function does not assume any runtime /// and is equivalent to calling launch_with_props with the tokio feature disabled. -pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Config) -> ! { +pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config: Config) -> ! { + let mut custom_event_handler = desktop_config.custom_event_handler.take(); let (event_loop, mut app) = App::new(desktop_config, virtual_dom); - event_loop.run(move |window_event, _, control_flow| { + event_loop.run(move |window_event, event_loop, control_flow| { // Set the control flow and check if any events need to be handled in the app itself app.tick(&window_event); + if let Some(ref mut f) = custom_event_handler { + f(&window_event, event_loop) + } + match window_event { Event::NewEvents(StartCause::Init) => app.handle_start_cause_init(), Event::LoopDestroyed => app.handle_loop_destroyed(),