diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3df0a59c..0fb82975fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Unreleased` header. # Unreleased +- On macOS, emit `Event::Reopen` to handle dock icon click event. - On X11, fix use after free during xinput2 handling. - On X11, filter close to zero values in mouse device events - Move `dpi` types to its own crate, and re-export it from the root crate. diff --git a/FEATURES.md b/FEATURES.md index df813490fb..e96f3d6c7c 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -138,6 +138,7 @@ If your PR makes notable changes to Winit's features, please update this section * Full-size content view * Accepts first mouse * Set a preferred theme and get current theme. +* Dock icon click event ### Unix * Window urgency diff --git a/examples/window.rs b/examples/window.rs index 3aa7f99096..10c0fcfef8 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -35,6 +35,10 @@ use winit::platform::startup_notify::{ /// The amount of points to around the window for drag resize direction calculations. const BORDER_SIZE: f64 = 20.; +/// Whether application should keep in the background even if all windows are closed. +/// Default is `true` on macOS and `false` on other platforms. +const KEEP_IN_BACKGROUND: bool = cfg!(macos_platform); + fn main() -> Result<(), Box> { let event_loop = EventLoop::::with_user_event().build()?; let _event_loop_proxy = event_loop.create_proxy(); @@ -67,7 +71,7 @@ fn main() -> Result<(), Box> { state.print_help(); } Event::AboutToWait => { - if state.windows.is_empty() { + if state.windows.is_empty() && !KEEP_IN_BACKGROUND { println!("No windows left, exiting..."); event_loop.exit(); } @@ -81,6 +85,15 @@ fn main() -> Result<(), Box> { Event::UserEvent(event) => { println!("User event: {event:?}"); } + Event::Reopen => + { + #[cfg(macos_platform)] + if state.windows.is_empty() { + state + .create_window(event_loop, None) + .expect("failed to create window"); + } + } Event::Suspended | Event::LoopExiting | Event::MemoryWarning => (), })?; diff --git a/src/event.rs b/src/event.rs index f069cc181b..2f8dd62718 100644 --- a/src/event.rs +++ b/src/event.rs @@ -253,6 +253,24 @@ pub enum Event { /// /// - **macOS / Wayland / Windows / Orbital:** Unsupported. MemoryWarning, + + /// Emitted when the application has received a reopen request from OS. + /// + /// ## Platform-specific + /// + /// ### macOS + /// + /// On macOS, the `Reopen` event is emitted in response to an [`applicationShouldHandleReopen`] + /// callback, which is usually called whenever the Finder reactivates an already running + /// application because the user double-clicked it again or used the dock to activate it. + /// Usually, the user would expect you to create a new window if there isn't any. + /// + /// [`applicationShouldHandleReopen`]: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen + /// + /// ### Others + /// + /// - **Android / iOS / Web / Wayland / Windows / Orbital:** Unsupported. + Reopen, } impl Event { @@ -269,6 +287,7 @@ impl Event { Suspended => Ok(Suspended), Resumed => Ok(Resumed), MemoryWarning => Ok(MemoryWarning), + Reopen => Ok(Reopen), } } } diff --git a/src/platform_impl/macos/app_delegate.rs b/src/platform_impl/macos/app_delegate.rs index a3358efdf8..6198d91c53 100644 --- a/src/platform_impl/macos/app_delegate.rs +++ b/src/platform_impl/macos/app_delegate.rs @@ -113,6 +113,16 @@ declare_class!( // TODO: Notify every window that it will be destroyed, like done in iOS? self.internal_exit(); } + + #[method(applicationShouldHandleReopen:hasVisibleWindows:)] + fn should_handle_reopen(&self, _sender: &Option<&AnyObject>, _has_visible_windows: bool) -> bool { + trace_scope!("applicationShouldHandleReopen:hasVisibleWindows:"); + + self.handle_event(Event::Reopen); + // return true to preserve the default behavior, such as showing the minimized window. + true + } + } );