From 1b25afc5251a9d40a681b22186ce7b77ff9956ed Mon Sep 17 00:00:00 2001 From: clover caruso Date: Mon, 12 Oct 2026 16:43:37 -0700 Subject: [PATCH] fix(desktop): admit native prompts before target shortcuts Refs #123, #129 Assisted-by: gpt-6.1-sol --- crates/snowbound/src/commands.rs | 6 +++++ crates/snowbound/src/main.rs | 39 ++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/crates/snowbound/src/commands.rs b/crates/snowbound/src/commands.rs index 03167dd928ae6836ee718a6af4ac27bd823b263e..e0279cbf1d6340c32102896315f74971833c7ce0 100644 --- a/crates/snowbound/src/commands.rs +++ b/crates/snowbound/src/commands.rs @@ -1091,6 +1091,10 @@ impl State { return; } }; + #[cfg(any(windows, target_os = "linux"))] + if self.prompt.strong_count() != 0 { + return; + } let result = (|| -> Result<(), Box> { let size = self.window.inner_size(); let size = [size.width, size.height] @@ -1143,6 +1147,8 @@ impl State { || self.link.is_some() || self.tag_list.is_some() || self.server.is_some(); + #[cfg(any(windows, target_os = "linux"))] + let modal = modal || self.prompt.strong_count() != 0; let page = (session.is_some() || self.temporary) && !modal; let writable = page && !session.is_some_and(|session| session.read_only()); // Picked drawings leave the text selection behind them unseen, so it takes no edits. diff --git a/crates/snowbound/src/main.rs b/crates/snowbound/src/main.rs index 8006b6aea7492c29f094fcc79a60900c09fe3cae..24842777522a09c5cf94da3e02bbac1cbf797b88 100644 --- a/crates/snowbound/src/main.rs +++ b/crates/snowbound/src/main.rs @@ -237,6 +237,17 @@ type Continuation = Box Result<(), Box> + S /// one is open; one cancelled drops its reply unanswered. struct Reply(Box); +/// Open Notebook blocks target shortcuts until its reply is consumed or cancelled. +#[cfg(any(windows, target_os = "linux"))] +struct Prompt(EventLoopProxy); + +#[cfg(any(windows, target_os = "linux"))] +impl Drop for Prompt { + fn drop(&mut self) { + let _ = self.0.send_event(UserEvent::Redraw); + } +} + impl Reply { fn new( proxy: &EventLoopProxy, @@ -877,6 +888,8 @@ struct State { /// Snowbound's own dialogs, first the one shown, where the desktop has none. #[cfg(target_os = "linux")] asking: std::collections::VecDeque, + #[cfg(any(windows, target_os = "linux"))] + prompt: std::sync::Weak, /// What has been read in each notebook, on this computer. reads: unread::Reads, #[cfg(feature = "live")] @@ -1356,6 +1369,8 @@ impl State { unpacking: None, #[cfg(target_os = "linux")] asking: Default::default(), + #[cfg(any(windows, target_os = "linux"))] + prompt: Default::default(), reads, #[cfg(feature = "live")] peers: Default::default(), @@ -3652,13 +3667,23 @@ impl State { Command::RestoreVersion { page, version } => self.restore_version(page, version)?, Command::DeletePageVersion { page, version } => self.delete_version(page, version)?, Command::DeleteAllVersions(scope) => self.delete_all_versions(scope)?, - Command::OpenNotebook => platform::pick_notebook( - "Open Notebook", - self.reply(|state, path: PathBuf| { - state.open_path(&path); - Ok(()) - }), - ), + Command::OpenNotebook => { + #[cfg(any(windows, target_os = "linux"))] + let prompt = self.prompt.upgrade().unwrap_or_else(|| { + let prompt = Arc::new(Prompt(self.proxy.clone())); + self.prompt = Arc::downgrade(&prompt); + prompt + }); + platform::pick_notebook( + "Open Notebook", + self.reply(move |state, path: PathBuf| { + #[cfg(any(windows, target_os = "linux"))] + let _prompt = prompt; + state.open_path(&path); + Ok(()) + }), + ); + } Command::OpenFromServer(location) => self.open_server(location.as_deref()), #[cfg(feature = "live")] Command::OpenShared => self.open_shared(), -- 2.54.0