macOS: catch a Finder open aimed at the already-running app (#1039)

Double-click / "Open With" never puts the file on argv — Finder always
delivers it as a kAEOpenDocuments Apple Event instead, which nothing in
this app listened for, so it just showed "cannot open files in this
format" even though the file association itself was registered
correctly.

The standard fix, implementing application:openURLs: on this app's own
NSApplicationDelegate, isn't available here: winit already installs
its own delegate to own window/lifecycle events, NSApplication has
exactly one delegate slot, and winit's class doesn't implement the
open-document method.

This bypasses the delegate protocol and registers directly with the
lower-level Apple Event Manager (AEInstallEventHandler) instead — the
same mechanism AppKit's delegate forwarding is itself built on, and
independent of who owns the delegate. Registration happens on the
iced Subscription's first poll rather than in main() before app::run()
— registering earlier gets silently overwritten by winit's own
NSApplication startup, confirmed by testing against a real `open -a`
rather than assumed from Apple's docs.

This handles a file opened while the app is already running. A cold
launch (app not running yet) needs a different mechanism, in the
following commit.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Felix RiestraandClaude Sonnet 5 committed 2026-09-07 16:44:44 +02:00
1 parent 143703d809
commit b617734d5c
3 files changed
+295

No files matched your search

+10
View File
@@ -2346,6 +2346,15 @@ impl OpenCADStudio {
let single_instance = crate::io::single_instance::subscribe().map(Message::OpenExternal);
#[cfg(target_arch = "wasm32")]
let single_instance = Subscription::none();
// A second file opened via Finder while this process is already
// running (#1039) — delivered as a warm Apple Event straight to this
// process, bypassing `src/bin/ocs_launcher.rs` entirely (that only
// runs for the initial cold launch). See `io::macos_open_events`.
#[cfg(target_os = "macos")]
let macos_open_events =
crate::io::macos_open_events::subscribe().map(Message::OpenExternal);
#[cfg(not(target_os = "macos"))]
let macos_open_events = Subscription::none();
// Drain plugin-to-host requests on a timer when any plugin is loaded.
// This lets long-lived plugin sessions (e.g. the Python REPL) mutate the
// host document without requiring a user-generated message.
@@ -2521,6 +2530,7 @@ impl OpenCADStudio {
autosave,
plugin_drain,
single_instance,
macos_open_events,
hatch_pattern_keys,
keyboard_events,
])