mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
Fix #867: macOS file dialogs no longer run inside the window's event handler
On macOS the Open and Save panels ran modally on the UI thread, inside the window's event handler. Resizing the panel delivered window events to that handler again, and the app crashed when the mouse was released. They now open from a thread of their own, as on Linux (#592): rfd runs the panel on the main thread from the run loop, outside the handler, and the app picks the answer up when it comes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
6c131431ed
commit
dd8f144770
2 files changed
+13
-9
No files matched your search
@@ -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<std::sync::mpsc::Receiver<Vec<String>>> {
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in new issue
Block a user