diff --git a/apps/vectorcraft/src/main.rs b/apps/vectorcraft/src/main.rs index f7b6771d5..92ab9d889 100644 --- a/apps/vectorcraft/src/main.rs +++ b/apps/vectorcraft/src/main.rs @@ -274,9 +274,12 @@ fn write_file(path: &str, bytes: &[u8]) -> Result<(), String> { fileio::write_atomic(std::path::Path::new(path), bytes).map_err(|e| e.to_string()) } -/// Linux: show the dialog for `request` over `parent` on a thread of its own, answering on the -/// receiver. Shown on the UI thread, nothing would answer the compositor meanwhile, which then -/// offers to kill the window as not responding (#592). +/// Linux and macOS: show the dialog for `request` over `parent` on a thread of its own, answering +/// on the receiver. Shown on the UI thread, nothing would answer the compositor meanwhile on Linux, +/// which then offers to kill the window as not responding (#592); on macOS the panel ran modally +/// inside the window's event handler, and resizing it delivered window events to that handler +/// again, which crashed the app (#867). From another thread, rfd runs the panel on the main thread +/// from the run loop, outside the handler. fn start_pick(request: PickRequest, parent: &Parent) -> Option>> { let dialog = file_dialog(&request, parent); let (tx, rx) = std::sync::mpsc::channel(); @@ -296,9 +299,9 @@ fn services(parent: Parent) -> Services { pick_open: Some(Box::new(move |pick: &FilePick| pick_now(PickRequest::Open(pick.clone()), &p1).into_iter().next())), pick_open_multi: Some(Box::new(move || pick_now(PickRequest::OpenMany, &p2))), pick_save: Some(Box::new(move |pick: &FilePick| pick_now(PickRequest::Save(pick.clone()), &p3).into_iter().next())), - // Windows and macOS dialogs run the window's events while they are open; Linux's don't. - start_pick: cfg!(all(unix, not(target_os = "macos"))) - .then(|| Box::new(move |request: PickRequest| start_pick(request, &p5)) as vectorcraft_ui_egui::picks::StartPick), + // Windows dialogs run the window's events while they are open; Linux's don't, and macOS + // ones re-enter the window's event handler (#867). + start_pick: cfg!(unix).then(|| Box::new(move |request: PickRequest| start_pick(request, &p5)) as vectorcraft_ui_egui::picks::StartPick), read: Some(Box::new(|p: &str| std::fs::read(p).map_err(|e| e.to_string()))), write: Some(Box::new(write_file)), // Background Save and Export write from a worker thread. diff --git a/crates/ui-egui/src/picks.rs b/crates/ui-egui/src/picks.rs index 463a47fc4..8ee508531 100644 --- a/crates/ui-egui/src/picks.rs +++ b/crates/ui-egui/src/picks.rs @@ -2,9 +2,10 @@ //! //! The app asks for a path where it needs one (Open, Save As, Export, Place, Relink, presets…) //! and goes on with it at once, so a dialog shown on the UI thread holds the window until it -//! closes. On Windows and macOS the dialog runs the window's events meanwhile; on Linux nothing -//! does, so a Wayland compositor finds the window not answering and offers to kill it. There the -//! host shows dialogs on another thread ([`Services::start_pick`]): asking returns no path for now, +//! closes. On Windows the dialog runs the window's events meanwhile; on Linux nothing does, so a +//! Wayland compositor finds the window not answering and offers to kill it, and on macOS they +//! re-enter the window's event handler, which crashed the app when the panel was resized (#867). +//! There the host shows dialogs on another thread ([`Services::start_pick`]): asking returns no path for now, //! and when the dialog answers, what asked (the command, the dialog's OK, a panel's button: the //! *entry*) runs again and gets the picked path at once.