Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Event::Reopen support for macOS #3499

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Unreleased` header.
- On Orbital, implement `set_window_level`.
- On Orbital, emit `DeviceEvent::MouseMotion`.
- On Wayland, fix title in CSD not updated from `AboutToWait`.
- On macOS, emit `Event::Reopen` to handle dock icon click event.

# 0.29.10

Expand Down
1 change: 1 addition & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion examples/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn Error>> {
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
let _event_loop_proxy = event_loop.create_proxy();
Expand Down Expand Up @@ -67,7 +71,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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();
}
Expand All @@ -81,6 +85,15 @@ fn main() -> Result<(), Box<dyn Error>> {
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 => (),
})?;

Expand Down
19 changes: 19 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,24 @@ pub enum Event<T: 'static> {
///
/// - **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<T> Event<T> {
Expand All @@ -269,6 +287,7 @@ impl<T> Event<T> {
Suspended => Ok(Suspended),
Resumed => Ok(Resumed),
MemoryWarning => Ok(MemoryWarning),
Reopen => Ok(Reopen),
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/platform_impl/macos/app_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

}
);

Expand Down