Pārlūkot izejas kodu

tweaks to the LastWindowClose to make it more imperative

Jonathan Kelley 2 dienas atpakaļ
vecāks
revīzija
ff762cec84

+ 4 - 25
Cargo.lock

@@ -3965,7 +3965,7 @@ dependencies = [
  "jni",
  "lazy-js-bundle",
  "libc",
- "muda 0.17.0",
+ "muda",
  "ndk",
  "ndk-context",
  "ndk-sys 0.6.0+11769913",
@@ -8985,27 +8985,6 @@ dependencies = [
  "nasm-rs",
 ]
 
-[[package]]
-name = "muda"
-version = "0.16.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492"
-dependencies = [
- "crossbeam-channel",
- "dpi",
- "gtk",
- "keyboard-types",
- "libxdo",
- "objc2 0.6.1",
- "objc2-app-kit 0.3.1",
- "objc2-core-foundation",
- "objc2-foundation 0.3.1",
- "once_cell",
- "png",
- "thiserror 2.0.12",
- "windows-sys 0.59.0",
-]
-
 [[package]]
 name = "muda"
 version = "0.17.0"
@@ -15177,14 +15156,14 @@ dependencies = [
 
 [[package]]
 name = "tray-icon"
-version = "0.20.1"
+version = "0.21.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7eee98ec5c90daf179d55c20a49d8c0d043054ce7c26336c09a24d31f14fa0"
+checksum = "2da75ec677957aa21f6e0b361df0daab972f13a5bee3606de0638fd4ee1c666a"
 dependencies = [
  "crossbeam-channel",
  "dirs 6.0.0",
  "libappindicator",
- "muda 0.16.1",
+ "muda",
  "objc2 0.6.1",
  "objc2-app-kit 0.3.1",
  "objc2-core-foundation",

+ 1 - 1
Cargo.toml

@@ -325,7 +325,7 @@ cocoa = "0.26"
 core-foundation = "0.10.1"
 objc = { version = "0.2.7", features = ["exception"] }
 objc_id = "0.1.1"
-tray-icon = "0.20.0"
+tray-icon = "0.21.0"
 open = "5.1.2"
 
 # web

+ 2 - 10
examples/multiwindow.rs

@@ -5,22 +5,14 @@
 //! own context, root elements, etc.
 
 use dioxus::prelude::*;
-use dioxus::{desktop::Config, desktop::DefaultWindowCloseBehaviour};
 
 fn main() {
-    dioxus::LaunchBuilder::desktop()
-        // We can choose the close behavior of the last window to hide. See DefaultWindowCloseBehaviour for more options.
-        .with_cfg(
-            Config::new()
-                .with_default_window_close_behaviour(DefaultWindowCloseBehaviour::LastWindowHides),
-        )
-        .launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {
     let onclick = move |_| {
-        let dom = VirtualDom::new(popup);
-        dioxus::desktop::window().new_window(dom, Default::default());
+        dioxus::desktop::window().new_window(VirtualDom::new(popup), Default::default());
     };
 
     rsx! {

+ 18 - 12
examples/multiwindow_with_tray_icon.rs

@@ -3,31 +3,37 @@
 //! This example shows how to implement a simple multiwindow application and tray icon using dioxus.
 //! This works by spawning a new window when the user clicks a button. We have to build a new virtualdom which has its
 //! own context, root elements, etc.
+//!
+//! This is useful for apps that incorporate settings panels or persistent windows like Raycast.
 
 use dioxus::desktop::{
     trayicon::{default_tray_icon, init_tray_icon},
-    Config, WindowCloseBehaviour,
+    window, WindowCloseBehaviour,
 };
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus::LaunchBuilder::desktop()
-        // We can choose the close behavior of this window to hide. See WindowCloseBehaviour for more options.
-        .with_cfg(Config::new().with_window_close_behaviour(WindowCloseBehaviour::WindowHides))
-        .launch(app);
+    dioxus::launch(app);
 }
 
 fn app() -> Element {
-    // async should not be needed, check if issue 3542 has been resolved
-    let onclick = move |_| async {
-        let dom = VirtualDom::new(popup);
-        dioxus::desktop::window().new_window(dom, Default::default());
-    };
+    use_hook(|| {
+        // Set the close behavior for the main window
+        // This will hide the window instead of closing it when the user clicks the close button
+        window().set_close_behavior(WindowCloseBehaviour::WindowHides);
 
-    init_tray_icon(default_tray_icon(), None);
+        // Initialize the tray icon with a default icon and no menu
+        // This will provide the tray into context for the application
+        init_tray_icon(default_tray_icon(), None)
+    });
 
     rsx! {
-        button { onclick, "New Window" }
+        button {
+            onclick: move |_| {
+                window().new_window(VirtualDom::new(popup), Default::default());
+            },
+            "New Window"
+        }
     }
 }
 

+ 19 - 106
packages/desktop/src/app.rs

@@ -1,5 +1,5 @@
 use crate::{
-    config::{Config, DefaultWindowCloseBehaviour, WindowCloseBehaviour},
+    config::{Config, WindowCloseBehaviour},
     edits::EditWebsocket,
     event_handlers::WindowEventHandlers,
     file_upload::{DesktopFileUploadForm, FileDialogRequest, NativeFileEngine},
@@ -23,7 +23,7 @@ use tao::{
     dpi::PhysicalSize,
     event::Event,
     event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget},
-    window::{Window, WindowId},
+    window::WindowId,
 };
 
 /// The single top-level object that manages all the running windows, assets, shortcuts, etc
@@ -36,7 +36,7 @@ pub(crate) struct App {
     // Stuff we need mutable access to
     pub(crate) control_flow: ControlFlow,
     pub(crate) is_visible_before_start: bool,
-    pub(crate) default_window_close_behavior: DefaultWindowCloseBehaviour,
+    pub(crate) exit_on_last_window_close: bool,
     pub(crate) webviews: HashMap<WindowId, WebviewInstance>,
     pub(crate) float_all: bool,
     pub(crate) show_devtools: bool,
@@ -65,7 +65,7 @@ impl App {
             .unwrap_or_else(|| EventLoopBuilder::<UserWindowEvent>::with_user_event().build());
 
         let app = Self {
-            default_window_close_behavior: cfg.default_window_close_behaviour,
+            exit_on_last_window_close: cfg.exit_on_last_window_close,
             is_visible_before_start: true,
             webviews: HashMap::new(),
             control_flow: ControlFlow::Wait,
@@ -188,89 +188,37 @@ impl App {
         }
     }
 
-    pub fn change_window_close_behaviour(
-        &mut self,
-        id: WindowId,
-        behaviour: Option<WindowCloseBehaviour>,
-    ) {
-        if let Some(webview) = self.webviews.get_mut(&id) {
-            webview.close_behaviour = behaviour
-        }
-    }
-
     pub fn handle_close_requested(&mut self, id: WindowId) {
-        use DefaultWindowCloseBehaviour::*;
-        use WindowCloseBehaviour::*;
-
-        let mut remove = false;
-
-        if let Some(webview) = self.webviews.get(&id) {
-            if let Some(close_behaviour) = &webview.close_behaviour {
-                match close_behaviour {
-                    WindowExitsApp => {
-                        self.control_flow = ControlFlow::Exit;
-                        return;
-                    }
-                    WindowHides => {
-                        hide_window(&webview.desktop_context.window);
-                        return;
-                    }
-                    WindowCloses => {
-                        remove = true;
-                    }
-                }
-            }
-        }
-
-        // needed in case of `default_window_close_behavior WindowsHides | LastWindowHides` since they may not remove a window on `WindowCloses`
-        if remove {
-            #[cfg(debug_assertions)]
-            self.persist_window_state();
+        let Some(window) = self.webviews.get(&id) else {
+            // If the window is not found, we can just return
+            return;
+        };
 
-            self.webviews.remove(&id);
-            if matches!(self.default_window_close_behavior, LastWindowExitsApp)
-                && self.webviews.is_empty()
-            {
-                self.control_flow = ControlFlow::Exit
+        match window.desktop_context.close_behaviour.get() {
+            // If the window is just set to hide when closed, we can just hide it
+            WindowCloseBehaviour::WindowHides => {
+                window.desktop_context.window.set_visible(false);
             }
-            return;
-        }
 
-        match self.default_window_close_behavior {
-            LastWindowExitsApp => {
+            // If the window is set to close, we can remove it from the list of webviews
+            // If the app is set to exit when the last window closes, we should also exit the app
+            WindowCloseBehaviour::WindowCloses => {
                 #[cfg(debug_assertions)]
                 self.persist_window_state();
 
                 self.webviews.remove(&id);
-                if self.webviews.is_empty() {
-                    self.control_flow = ControlFlow::Exit
-                }
-            }
-
-            LastWindowHides if self.webviews.len() > 1 => {
-                self.webviews.remove(&id);
-            }
 
-            WindowsHides | LastWindowHides => {
-                if let Some(webview) = self.webviews.get(&id) {
-                    hide_window(&webview.desktop_context.window);
+                if self.exit_on_last_window_close && self.webviews.is_empty() {
+                    self.control_flow = ControlFlow::Exit
                 }
             }
-
-            WindowsCloses => {
-                self.webviews.remove(&id);
-            }
-        }
+        };
     }
 
     pub fn window_destroyed(&mut self, id: WindowId) {
         self.webviews.remove(&id);
 
-        if matches!(
-            self.default_window_close_behavior,
-            DefaultWindowCloseBehaviour::LastWindowExitsApp
-        ) && self.webviews.is_empty()
-        {
+        if self.exit_on_last_window_close && self.webviews.is_empty() {
             self.control_flow = ControlFlow::Exit
         }
     }
@@ -723,41 +671,6 @@ struct PreservedWindowState {
     url: Option<String>,
 }
 
-/// Hides a window.
-///
-/// On macOS, if we use `set_visibility(false)` on the window, it will hide the window but not show
-/// it again when the user switches back to the app. `NSApplication::hide:` has the correct behaviour,
-/// so we need to special case it.
-#[allow(unused)]
-fn hide_window(window: &Window) {
-    #[cfg(target_os = "windows")]
-    {
-        use tao::platform::windows::WindowExtWindows;
-        window.set_visible(false);
-    }
-
-    #[cfg(target_os = "linux")]
-    {
-        use tao::platform::unix::WindowExtUnix;
-        window.set_visible(false);
-    }
-
-    #[cfg(target_os = "macos")]
-    {
-        // 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};
-        #[allow(unexpected_cfgs)]
-        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];
-        });
-    }
-}
-
 /// Return the location of a tempfile with our window state in it such that we can restore it later
 fn restore_file() -> std::path::PathBuf {
     let dir = dioxus_cli_config::session_cache_dir().unwrap_or_else(std::env::temp_dir);

+ 15 - 29
packages/desktop/src/config.rs

@@ -17,28 +17,13 @@ type CustomEventHandler = Box<
         ),
 >;
 
-/// The closing behaviour of the application when the last window is closed, you can overwrite this behaviour for specific window with WindowCloseBehaviour.
-#[derive(Copy, Clone, Eq, PartialEq)]
-#[non_exhaustive]
-pub enum DefaultWindowCloseBehaviour {
-    /// Default behaviour, closing the last window will exit the app, others will close,
-    LastWindowExitsApp,
-    /// Closing the last window will hide it, others will close
-    LastWindowHides,
-    /// Every window will hide
-    WindowsHides,
-    /// Every window will close
-    WindowsCloses,
-}
-
 /// The closing behaviour of specific application window.
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 #[non_exhaustive]
 pub enum WindowCloseBehaviour {
-    /// Closing the window will exit the app
-    WindowExitsApp,
-    /// Window will hide
+    /// Window will hide instead of closing
     WindowHides,
+
     /// Window will close
     WindowCloses,
 }
@@ -75,8 +60,8 @@ pub struct Config {
     pub(crate) custom_index: Option<String>,
     pub(crate) root_name: String,
     pub(crate) background_color: Option<(u8, u8, u8, u8)>,
-    pub(crate) default_window_close_behaviour: DefaultWindowCloseBehaviour,
-    pub(crate) window_close_behaviour: Option<WindowCloseBehaviour>,
+    pub(crate) exit_on_last_window_close: bool,
+    pub(crate) window_close_behavior: WindowCloseBehaviour,
     pub(crate) custom_event_handler: Option<CustomEventHandler>,
     pub(crate) disable_file_drop_handler: bool,
 }
@@ -122,8 +107,8 @@ impl Config {
             custom_index: None,
             root_name: "main".to_string(),
             background_color: None,
-            default_window_close_behaviour: DefaultWindowCloseBehaviour::LastWindowExitsApp,
-            window_close_behaviour: None,
+            exit_on_last_window_close: true,
+            window_close_behavior: WindowCloseBehaviour::WindowCloses,
             custom_event_handler: None,
             disable_file_drop_handler: false,
         }
@@ -185,18 +170,19 @@ impl Config {
         self
     }
 
-    /// Sets the behaviour of the application when the last window is closed.
-    pub fn with_default_window_close_behaviour(
-        mut self,
-        behaviour: DefaultWindowCloseBehaviour,
-    ) -> Self {
-        self.default_window_close_behaviour = behaviour;
+    /// When the last window is closed, the application will exit.
+    ///
+    /// This is the default behaviour.
+    ///
+    /// If the last window is hidden, the application will not exit.
+    pub fn with_exits_when_last_window_closes(mut self, exit: bool) -> Self {
+        self.exit_on_last_window_close = exit;
         self
     }
 
     /// Sets the behaviour of the application when the last window is closed.
-    pub fn with_window_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
-        self.window_close_behaviour = Some(behaviour);
+    pub fn with_close_behaviour(mut self, behaviour: WindowCloseBehaviour) -> Self {
+        self.window_close_behavior = behaviour;
         self
     }
 

+ 12 - 20
packages/desktop/src/desktop_context.rs

@@ -10,6 +10,7 @@ use crate::{
 };
 use dioxus_core::{prelude::Callback, VirtualDom};
 use std::{
+    cell::Cell,
     future::{Future, IntoFuture},
     pin::Pin,
     rc::{Rc, Weak},
@@ -66,6 +67,7 @@ pub struct DesktopService {
     pub(super) query: QueryEngine,
     pub(crate) asset_handlers: AssetHandlerRegistry,
     pub(crate) file_hover: NativeFileHover,
+    pub(crate) close_behaviour: Rc<Cell<WindowCloseBehaviour>>,
 
     #[cfg(target_os = "ios")]
     pub(crate) views: Rc<std::cell::RefCell<Vec<*mut objc::runtime::Object>>>,
@@ -87,6 +89,7 @@ impl DesktopService {
         shared: Rc<SharedContext>,
         asset_handlers: AssetHandlerRegistry,
         file_hover: NativeFileHover,
+        close_behaviour: WindowCloseBehaviour,
     ) -> Self {
         Self {
             window,
@@ -94,6 +97,7 @@ impl DesktopService {
             shared,
             asset_handlers,
             file_hover,
+            close_behaviour: Rc::new(Cell::new(close_behaviour)),
             query: Default::default(),
             #[cfg(target_os = "ios")]
             views: Default::default(),
@@ -165,6 +169,14 @@ impl DesktopService {
         self.window.set_maximized(!self.window.is_maximized())
     }
 
+    /// Set the close behavior of this window
+    ///
+    /// By default, windows close when the user clicks the close button.
+    /// If this is set to `WindowCloseBehaviour::WindowHides`, the window will hide instead of closing.
+    pub fn set_close_behavior(&self, behaviour: WindowCloseBehaviour) {
+        self.close_behaviour.set(behaviour);
+    }
+
     /// Close this window
     pub fn close(&self) {
         let _ = self
@@ -181,26 +193,6 @@ impl DesktopService {
             .send_event(UserWindowEvent::CloseWindow(id));
     }
 
-    /// Change close behaviour of this window
-    pub fn change_close_behaviour(&self, behaviour: Option<WindowCloseBehaviour>) {
-        let _ = self
-            .shared
-            .proxy
-            .send_event(UserWindowEvent::CloseBehaviour(self.id(), behaviour));
-    }
-
-    /// Change close behaviour of a specific window, given its ID
-    pub fn change_window_close_behaviour(
-        &self,
-        id: WindowId,
-        behaviour: Option<WindowCloseBehaviour>,
-    ) {
-        let _ = self
-            .shared
-            .proxy
-            .send_event(UserWindowEvent::CloseBehaviour(id, behaviour));
-    }
-
     /// change window to fullscreen
     pub fn set_fullscreen(&self, fullscreen: bool) {
         if let Some(handle) = &self.window.current_monitor() {

+ 0 - 4
packages/desktop/src/ipc.rs

@@ -1,8 +1,6 @@
 use serde::{Deserialize, Serialize};
 use tao::window::WindowId;
 
-use crate::WindowCloseBehaviour;
-
 #[non_exhaustive]
 #[derive(Debug, Clone)]
 pub enum UserWindowEvent {
@@ -19,8 +17,6 @@ pub enum UserWindowEvent {
     #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
     TrayMenuEvent(tray_icon::menu::MenuEvent),
 
-    CloseBehaviour(WindowId, Option<WindowCloseBehaviour>),
-
     /// Poll the virtualdom
     Poll(WindowId),
 

+ 0 - 3
packages/desktop/src/launch.rs

@@ -103,9 +103,6 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, mut desktop_config:
                     IpcMethod::BrowserOpen => app.handle_browser_open(msg),
                     IpcMethod::Other(_) => {}
                 },
-                UserWindowEvent::CloseBehaviour(window_id, window_close_behaviour) => {
-                    app.change_window_close_behaviour(window_id, window_close_behaviour)
-                }
             },
             _ => {}
         }

+ 1 - 1
packages/desktop/src/lib.rs

@@ -47,7 +47,7 @@ pub mod trayicon;
 
 // Public exports
 pub use assets::AssetRequest;
-pub use config::{Config, DefaultWindowCloseBehaviour, WindowCloseBehaviour};
+pub use config::{Config, WindowCloseBehaviour};
 pub use desktop_context::{
     window, DesktopContext, DesktopService, PendingDesktopContext, WeakDesktopContext,
 };

+ 1 - 2
packages/desktop/src/webview.rs

@@ -161,7 +161,6 @@ pub(crate) struct WebviewInstance {
     pub edits: WebviewEdits,
     pub desktop_context: DesktopContext,
     pub waker: Waker,
-    pub close_behaviour: Option<WindowCloseBehaviour>,
 
     // Wry assumes the webcontext is alive for the lifetime of the webview.
     // We need to keep the webcontext alive, otherwise the webview will crash
@@ -414,6 +413,7 @@ impl WebviewInstance {
             shared.clone(),
             asset_handlers,
             file_hover,
+            WindowCloseBehaviour::WindowCloses,
         ));
 
         // Provide the desktop context to the virtual dom and edit handler
@@ -433,7 +433,6 @@ impl WebviewInstance {
             desktop_context,
             _menu: menu,
             _web_context: web_context,
-            close_behaviour: cfg.window_close_behaviour,
         }
     }