From 6a705b6a0e05ea21cba04cb3df73512d22dc4bc5 Mon Sep 17 00:00:00 2001 From: Benedikt Terhechte Date: Tue, 4 Jul 2023 14:47:45 +0200 Subject: [PATCH] add functionality --- packages/desktop/src/cfg.rs | 19 +++++++++++ packages/desktop/src/lib.rs | 64 ++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/packages/desktop/src/cfg.rs b/packages/desktop/src/cfg.rs index 619a6801a..07281e364 100644 --- a/packages/desktop/src/cfg.rs +++ b/packages/desktop/src/cfg.rs @@ -11,6 +11,17 @@ use wry::{ // 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. pub struct Config { pub(crate) window: WindowBuilder, @@ -24,6 +35,7 @@ pub struct Config { pub(crate) custom_index: Option, pub(crate) root_name: String, pub(crate) background_color: Option<(u8, u8, u8, u8)>, + pub(crate) last_window_close_behaviour: WindowCloseBehaviour, } type DropHandler = Box bool>; @@ -52,6 +64,7 @@ impl Config { custom_index: None, root_name: "main".to_string(), background_color: None, + last_window_close_behaviour: WindowCloseBehaviour::LastWindowExitsApp, } } @@ -89,6 +102,12 @@ impl Config { 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 pub fn with_file_drop_handler( mut self, diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index fe5c862ed..4d5f42ad4 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -20,7 +20,7 @@ mod webview; mod mobile_shortcut; use crate::query::QueryResult; -pub use cfg::Config; +pub use cfg::{Config, WindowCloseBehaviour}; pub use desktop_context::DesktopContext; pub use desktop_context::{ use_window, use_wry_event_handler, DesktopService, WryEventHandler, WryEventHandlerId, @@ -121,6 +121,8 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) let proxy = event_loop.create_proxy(); + let window_behaviour = cfg.last_window_close_behaviour; + // Intialize hot reloading if it is enabled #[cfg(all(feature = "hot-reload", debug_assertions))] dioxus_hot_reload::connect({ @@ -176,18 +178,33 @@ pub fn launch_with_props(root: Component

, props: P, cfg: Config) Event::WindowEvent { event, window_id, .. } => 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 { .. } => { 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 _ = 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::(); + let _: () = msg_send![app, hide: nil]; + }); + } +}