Browse / github.com/storytold/craft-launcher / patch
macOS: the Dock icon brings the window back after closing it (#456)
macOS: clicking the Dock icon doesn't show the window after it's closed to the tray Environment ArtCraft Launcher 0.4.1 (build cbae640688c5fa3daa120a7f5e050f8e24148a47) macOS 26.6.2 (25G83), Apple Silicon (arm64), three displays, Stage Manager off Settings: close_to_tray: true, show_on_current_desktop: true Steps to reproduce Launch ArtCraft Launcher. Close the window with the red close button. It goes to the tray as designed. Click the ArtCraft Launcher icon in the Dock, or open the app again from Spotlight or Finder. Expected: the window comes back, as it does for other macOS apps that keep running after their window is closed. Actual: the app becomes the active app but
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- EricKaden (imported from pull request #799, open)
- Base
5f3b7aaf4b3f4147bd084ad5a882208d3c7e9971- Size
- +42 −2 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
2b97938bc26e9ff533c8e77c
How it went
No other operator has reported using it yet.
Diff
raw · apps/artcraft-launcher/src/hiding.rs apps/artcraft-launcher/src/main.rs
new file mode 100644@@ -0,0 +1,26 @@+//! macOS only: closing the window to the menu bar hides the app, as ⌘H does, rather than ordering+//! the window out.+//!+//! macOS brings a hidden app back by itself: clicking its Dock icon, opening it again from Finder+//! or Spotlight, or picking it with ⌘Tab all unhide it, window and all. A window that was only+//! ordered out stays gone, as the Dock asks the app to "reopen" instead, and neither winit nor+//! eframe passes that on (issue #456). Handling that event here would take an Apple Event handler,+//! which needs `unsafe`.++use objc2::MainThreadMarker;+use objc2_app_kit::NSApplication;++/// Hides the app and its window. Must run on the main thread, as the UI does; elsewhere it does+/// nothing.+pub fn hide() {+ if let Some(mtm) = MainThreadMarker::new() {+ NSApplication::sharedApplication(mtm).hide(None);+ }+}++/// Brings the app back from [`hide`] and makes it the active app. Main thread only, as [`hide`].+pub fn unhide() {+ if let Some(mtm) = MainThreadMarker::new() {+ NSApplication::sharedApplication(mtm).unhide(None);+ }+}@@ -5,6 +5,8 @@ #![cfg_attr(windows, windows_subsystem = "windows")] #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] +#[cfg(target_os = "macos")]+mod hiding; mod logging; mod motion; mod settings_file;@@ -658,7 +660,13 @@ impl<'a> Shell<'a> { // Started hidden at login but there's no tray to come back from: show the window. let visible = !hidden || launcher.tray.is_none(); let mut shell = Self { launcher, window_hides, visible: true, quitting: false, applied_follow: None };- if !visible {+ if !visible && window_hides {+ // Started in the tray: the window stays out of sight. On macOS this doesn't hide the+ // app as `put_away` does, since the app is still being activated as it starts, which+ // would unhide it, window and all.+ shell.visible = false;+ cc.egui_ctx.send_viewport_cmd(egui::ViewportCommand::Visible(false));+ } else if !visible { shell.put_away(&cc.egui_ctx); } else if hidden { cc.egui_ctx.send_viewport_cmd(egui::ViewportCommand::Visible(true));@@ -675,6 +683,8 @@ impl<'a> Shell<'a> { let on = self.launcher.ui.settings.show_on_current_desktop; spaces::follow_current_desktop(on); self.applied_follow = Some(on);+ // Back from `put_away`, which hides the whole app here.+ hiding::unhide(); } if self.window_hides { ctx.send_viewport_cmd(egui::ViewportCommand::Visible(true));@@ -686,10 +696,14 @@ impl<'a> Shell<'a> { } } - /// Puts the window away in the tray: hidden where it can hide, closed where it can't.+ /// Puts the window away in the tray: hidden where it can hide, closed where it can't. On macOS+ /// the whole app hides, so its Dock icon brings the window back (`hiding`). fn put_away(&mut self, ctx: &egui::Context) { if self.window_hides { self.visible = false;+ #[cfg(target_os = "macos")]+ hiding::hide();+ #[cfg(not(target_os = "macos"))] ctx.send_viewport_cmd(egui::ViewportCommand::Visible(false)); } else { self.launcher.closed_to_tray = true;