authorclover caruso <git@paperclover.net> 2026-10-12 16:43:37-07:00
committerclover caruso <git@paperclover.net> 2026-10-15 02:58:22-07:00
log1b25afc5251a9d40a681b22186ce7b77ff9956ed
tree23995a7b80ca8497c72654c22e619be7523207ca
parent147a2af4881d7bbc1d857cf57d191ba94c0522ca
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover)

fix(desktop): admit native prompts before target shortcuts

Refs #123, #129 Assisted-by: gpt-6.1-sol

2 files changed, 38 insertions(+), 7 deletions(-)

crates/snowbound/src/commands.rs+6
......@@ -1091,6 +1091,10 @@ impl State {
10911091 return;
10921092 }
10931093 };
1094 #[cfg(any(windows, target_os = "linux"))]
1095 if self.prompt.strong_count() != 0 {
1096 return;
1097 }
10941098 let result = (|| -> Result<(), Box<dyn Error>> {
10951099 let size = self.window.inner_size();
10961100 let size = [size.width, size.height]
......@@ -1143,6 +1147,8 @@ impl State {
11431147 || self.link.is_some()
11441148 || self.tag_list.is_some()
11451149 || self.server.is_some();
1150 #[cfg(any(windows, target_os = "linux"))]
1151 let modal = modal || self.prompt.strong_count() != 0;
11461152 let page = (session.is_some() || self.temporary) && !modal;
11471153 let writable = page && !session.is_some_and(|session| session.read_only());
11481154 // Picked drawings leave the text selection behind them unseen, so it takes no edits.
crates/snowbound/src/main.rs+32-7
......@@ -237,6 +237,17 @@ type Continuation = Box<dyn FnOnce(&mut State) -> Result<(), Box<dyn Error>> + S
237237/// one is open; one cancelled drops its reply unanswered.
238238struct Reply<T>(Box<dyn FnOnce(T) + Send>);
239239
240/// Open Notebook blocks target shortcuts until its reply is consumed or cancelled.
241#[cfg(any(windows, target_os = "linux"))]
242struct Prompt(EventLoopProxy<UserEvent>);
243
244#[cfg(any(windows, target_os = "linux"))]
245impl Drop for Prompt {
246 fn drop(&mut self) {
247 let _ = self.0.send_event(UserEvent::Redraw);
248 }
249}
250
240251impl<T: Send + 'static> Reply<T> {
241252 fn new(
242253 proxy: &EventLoopProxy<UserEvent>,
......@@ -877,6 +888,8 @@ struct State {
877888 /// Snowbound's own dialogs, first the one shown, where the desktop has none.
878889 #[cfg(target_os = "linux")]
879890 asking: std::collections::VecDeque<dialog::Dialog>,
891 #[cfg(any(windows, target_os = "linux"))]
892 prompt: std::sync::Weak<Prompt>,
880893 /// What has been read in each notebook, on this computer.
881894 reads: unread::Reads,
882895 #[cfg(feature = "live")]
......@@ -1356,6 +1369,8 @@ impl State {
13561369 unpacking: None,
13571370 #[cfg(target_os = "linux")]
13581371 asking: Default::default(),
1372 #[cfg(any(windows, target_os = "linux"))]
1373 prompt: Default::default(),
13591374 reads,
13601375 #[cfg(feature = "live")]
13611376 peers: Default::default(),
......@@ -3652,13 +3667,23 @@ impl State {
36523667 Command::RestoreVersion { page, version } => self.restore_version(page, version)?,
36533668 Command::DeletePageVersion { page, version } => self.delete_version(page, version)?,
36543669 Command::DeleteAllVersions(scope) => self.delete_all_versions(scope)?,
3655 Command::OpenNotebook => platform::pick_notebook(
3656 "Open Notebook",
3657 self.reply(|state, path: PathBuf| {
3658 state.open_path(&path);
3659 Ok(())
3660 }),
3661 ),
3670 Command::OpenNotebook => {
3671 #[cfg(any(windows, target_os = "linux"))]
3672 let prompt = self.prompt.upgrade().unwrap_or_else(|| {
3673 let prompt = Arc::new(Prompt(self.proxy.clone()));
3674 self.prompt = Arc::downgrade(&prompt);
3675 prompt
3676 });
3677 platform::pick_notebook(
3678 "Open Notebook",
3679 self.reply(move |state, path: PathBuf| {
3680 #[cfg(any(windows, target_os = "linux"))]
3681 let _prompt = prompt;
3682 state.open_path(&path);
3683 Ok(())
3684 }),
3685 );
3686 }
36623687 Command::OpenFromServer(location) => self.open_server(location.as_deref()),
36633688 #[cfg(feature = "live")]
36643689 Command::OpenShared => self.open_shared(),