Browse Source

Add support for user-provided event loops & event handlers (#3180)

* added support for custom event loop
* added support for custom event callbacks
Alex Andreba 7 months ago
parent
commit
1370bce182
3 changed files with 42 additions and 4 deletions
  1. 5 2
      packages/desktop/src/app.rs
  2. 30 0
      packages/desktop/src/config.rs
  3. 7 2
      packages/desktop/src/launch.rs

+ 5 - 2
packages/desktop/src/app.rs

@@ -53,8 +53,11 @@ pub(crate) struct SharedContext {
 }
 
 impl App {
-    pub fn new(cfg: Config, virtual_dom: VirtualDom) -> (EventLoop<UserWindowEvent>, Self) {
-        let event_loop = EventLoopBuilder::<UserWindowEvent>::with_user_event().build();
+    pub fn new(mut cfg: Config, virtual_dom: VirtualDom) -> (EventLoop<UserWindowEvent>, Self) {
+        let event_loop = cfg
+            .event_loop
+            .take()
+            .unwrap_or_else(|| EventLoopBuilder::<UserWindowEvent>::with_user_event().build());
 
         let app = Self {
             window_behavior: cfg.last_window_close_behavior,

+ 30 - 0
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<UserWindowEvent>,
+        ),
+>;
+
 /// The behaviour of the application when the last window is closed.
 #[derive(Copy, Clone, Eq, PartialEq)]
 #[non_exhaustive]
@@ -37,6 +47,7 @@ impl From<MenuBuilderState> for Option<DioxusMenu> {
 
 /// The configuration for the desktop application.
 pub struct Config {
+    pub(crate) event_loop: Option<EventLoop<UserWindowEvent>>,
     pub(crate) window: WindowBuilder,
     pub(crate) menu: MenuBuilderState,
     pub(crate) protocols: Vec<WryProtocol>,
@@ -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<CustomEventHandler>,
 }
 
 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<UserWindowEvent>) -> 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<UserWindowEvent>)
+            + 'static,
+    ) -> Self {
+        self.custom_event_handler = Some(Box::new(f));
+        self
+    }
+
     /// Set a custom protocol
     pub fn with_custom_protocol<F>(mut self, name: impl ToString, handler: F) -> Self
     where

+ 7 - 2
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(),