Browse Source

add functionality

Benedikt Terhechte 2 years ago
parent
commit
6a705b6a0e
2 changed files with 75 additions and 8 deletions
  1. 19 0
      packages/desktop/src/cfg.rs
  2. 56 8
      packages/desktop/src/lib.rs

+ 19 - 0
packages/desktop/src/cfg.rs

@@ -11,6 +11,17 @@ use wry::{
 
 
 // pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);
 // pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);
 
 
+/// The behaviour of the application when the last window is closed.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum WindowCloseBehaviour {
+    /// Default behaviour, closing the last window exits the app
+    LastWindowExitsApp,
+    /// Closing the last window will not actually close it, just hide it
+    LastWindowHides,
+    /// Closing the last window will close it but the app will keep running so that new windows can be opened
+    CloseWindow,
+}
+
 /// The configuration for the desktop application.
 /// The configuration for the desktop application.
 pub struct Config {
 pub struct Config {
     pub(crate) window: WindowBuilder,
     pub(crate) window: WindowBuilder,
@@ -24,6 +35,7 @@ pub struct Config {
     pub(crate) custom_index: Option<String>,
     pub(crate) custom_index: Option<String>,
     pub(crate) root_name: String,
     pub(crate) root_name: String,
     pub(crate) background_color: Option<(u8, u8, u8, u8)>,
     pub(crate) background_color: Option<(u8, u8, u8, u8)>,
+    pub(crate) last_window_close_behaviour: WindowCloseBehaviour,
 }
 }
 
 
 type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>;
 type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>;
@@ -52,6 +64,7 @@ impl Config {
             custom_index: None,
             custom_index: None,
             root_name: "main".to_string(),
             root_name: "main".to_string(),
             background_color: None,
             background_color: None,
+            last_window_close_behaviour: WindowCloseBehaviour::LastWindowExitsApp,
         }
         }
     }
     }
 
 
@@ -89,6 +102,12 @@ impl Config {
         self
         self
     }
     }
 
 
+    /// Sets the behaviour of the application when the last window is closed.
+    pub fn with_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
+        self.last_window_close_behaviour = behaviour;
+        self
+    }
+
     /// Set a file drop handler
     /// Set a file drop handler
     pub fn with_file_drop_handler(
     pub fn with_file_drop_handler(
         mut self,
         mut self,

+ 56 - 8
packages/desktop/src/lib.rs

@@ -20,7 +20,7 @@ mod webview;
 mod mobile_shortcut;
 mod mobile_shortcut;
 
 
 use crate::query::QueryResult;
 use crate::query::QueryResult;
-pub use cfg::Config;
+pub use cfg::{Config, WindowCloseBehaviour};
 pub use desktop_context::DesktopContext;
 pub use desktop_context::DesktopContext;
 pub use desktop_context::{
 pub use desktop_context::{
     use_window, use_wry_event_handler, DesktopService, WryEventHandler, WryEventHandlerId,
     use_window, use_wry_event_handler, DesktopService, WryEventHandler, WryEventHandlerId,
@@ -121,6 +121,8 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
 
 
     let proxy = event_loop.create_proxy();
     let proxy = event_loop.create_proxy();
 
 
+    let window_behaviour = cfg.last_window_close_behaviour;
+
     // Intialize hot reloading if it is enabled
     // Intialize hot reloading if it is enabled
     #[cfg(all(feature = "hot-reload", debug_assertions))]
     #[cfg(all(feature = "hot-reload", debug_assertions))]
     dioxus_hot_reload::connect({
     dioxus_hot_reload::connect({
@@ -176,18 +178,33 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
             Event::WindowEvent {
             Event::WindowEvent {
                 event, window_id, ..
                 event, window_id, ..
             } => match event {
             } => match event {
-                WindowEvent::CloseRequested => {
-                    webviews.remove(&window_id);
+                WindowEvent::CloseRequested => match window_behaviour {
+                    cfg::WindowCloseBehaviour::LastWindowExitsApp => {
+                        webviews.remove(&window_id);
 
 
-                    if webviews.is_empty() {
-                        *control_flow = ControlFlow::Exit
+                        if webviews.is_empty() {
+                            *control_flow = ControlFlow::Exit
+                        }
                     }
                     }
-                }
+                    cfg::WindowCloseBehaviour::LastWindowHides => {
+                        let Some(webview) = webviews.get(&window_id) else {
+                            return;
+                        };
+                        hide_app_window(&webview.desktop_context.webview);
+                    }
+                    cfg::WindowCloseBehaviour::CloseWindow => {
+                        webviews.remove(&window_id);
+                    }
+                },
                 WindowEvent::Destroyed { .. } => {
                 WindowEvent::Destroyed { .. } => {
                     webviews.remove(&window_id);
                     webviews.remove(&window_id);
 
 
-                    if webviews.is_empty() {
-                        *control_flow = ControlFlow::Exit;
+                    if matches!(
+                        window_behaviour,
+                        cfg::WindowCloseBehaviour::LastWindowExitsApp
+                    ) && webviews.is_empty()
+                    {
+                        *control_flow = ControlFlow::Exit
                     }
                     }
                 }
                 }
                 _ => {}
                 _ => {}
@@ -411,3 +428,34 @@ fn send_edits(edits: Mutations, webview: &WebView) {
     // todo: use SSE and binary data to send the edits with lower overhead
     // todo: use SSE and binary data to send the edits with lower overhead
     _ = webview.evaluate_script(&format!("window.interpreter.handleEdits({serialized})"));
     _ = webview.evaluate_script(&format!("window.interpreter.handleEdits({serialized})"));
 }
 }
+
+/// Different hide implementations per platform
+#[allow(unused_variables)]
+fn hide_app_window(webview: &WebView) {
+    #[cfg(target_os = "windows")]
+    {
+        use wry::application::platform::windows::WindowExtWindows;
+        webview.window().set_visible(false);
+        webview.window().set_skip_taskbar(true);
+    }
+
+    #[cfg(target_os = "linux")]
+    {
+        use wry::application::platform::unix::WindowExtUnix;
+        webview.window().set_visible(false);
+    }
+
+    #[cfg(target_os = "macos")]
+    {
+        // webview.window().set_visible(false); has the wrong behaviour on macOS
+        // It will hide the window but not show it again when the user switches
+        // back to the app. `NSApplication::hide:` has the correct behaviour
+        use objc::runtime::Object;
+        use objc::{msg_send, sel, sel_impl};
+        objc::rc::autoreleasepool(|| unsafe {
+            let app: *mut Object = msg_send![objc::class!(NSApplication), sharedApplication];
+            let nil = std::ptr::null_mut::<Object>();
+            let _: () = msg_send![app, hide: nil];
+        });
+    }
+}