From 0ec30345d4253c1766e02db5d74ede20402e680c Mon Sep 17 00:00:00 2001 From: Benedikt Terhechte Date: Tue, 4 Jul 2023 14:47:36 +0200 Subject: [PATCH 1/4] add required dependency --- packages/desktop/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index d5bc040a7..1453e9e66 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -48,6 +48,7 @@ objc_id = "0.1.1" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9.3" +objc = "0.2.7" [features] default = ["tokio_runtime", "hot-reload"] From 6a705b6a0e05ea21cba04cb3df73512d22dc4bc5 Mon Sep 17 00:00:00 2001 From: Benedikt Terhechte Date: Tue, 4 Jul 2023 14:47:45 +0200 Subject: [PATCH 2/4] 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]; + }); + } +} From 44025db5b06e98700ede630687a708b17bd6fb5d Mon Sep 17 00:00:00 2001 From: Benedikt Terhechte Date: Tue, 4 Jul 2023 14:48:05 +0200 Subject: [PATCH 3/4] add to example --- examples/window_focus.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/window_focus.rs b/examples/window_focus.rs index 59b4d5166..79a5e5044 100644 --- a/examples/window_focus.rs +++ b/examples/window_focus.rs @@ -2,9 +2,12 @@ use dioxus::prelude::*; use dioxus_desktop::tao::event::WindowEvent; use dioxus_desktop::use_wry_event_handler; use dioxus_desktop::wry::application::event::Event as WryEvent; +use dioxus_desktop::{Config, WindowCloseBehaviour}; fn main() { - dioxus_desktop::launch(app); + let cfg = Config::new().with_close_behaviour(WindowCloseBehaviour::CloseWindow); + + dioxus_desktop::launch_cfg(app, cfg); } fn app(cx: Scope) -> Element { From 4abf996b864154fa0f72fa23f2fa5b0145a45010 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 6 Jul 2023 10:57:52 -0700 Subject: [PATCH 4/4] fix clippy hinting around trait --- packages/desktop/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 1e5069c9d..cb295a853 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -442,7 +442,7 @@ fn send_edits(edits: Mutations, webview: &WebView) { } /// Different hide implementations per platform -#[allow(unused_variables)] +#[allow(unused)] fn hide_app_window(webview: &WebView) { #[cfg(target_os = "windows")] {