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 {...@@ -1091,6 +1091,10 @@ impl State {
1091 return;1091 return;
1092 }1092 }
1093 };1093 };
1094 #[cfg(any(windows, target_os = "linux"))]
1095 if self.prompt.strong_count() != 0 {
1096 return;
1097 }
1094 let result = (|| -> Result<(), Box<dyn Error>> {1098 let result = (|| -> Result<(), Box<dyn Error>> {
1095 let size = self.window.inner_size();1099 let size = self.window.inner_size();
1096 let size = [size.width, size.height]1100 let size = [size.width, size.height]
...@@ -1143,6 +1147,8 @@ impl State {...@@ -1143,6 +1147,8 @@ impl State {
1143 || self.link.is_some()1147 || self.link.is_some()
1144 || self.tag_list.is_some()1148 || self.tag_list.is_some()
1145 || self.server.is_some();1149 || self.server.is_some();
1150 #[cfg(any(windows, target_os = "linux"))]
1151 let modal = modal || self.prompt.strong_count() != 0;
1146 let page = (session.is_some() || self.temporary) && !modal;1152 let page = (session.is_some() || self.temporary) && !modal;
1147 let writable = page && !session.is_some_and(|session| session.read_only());1153 let writable = page && !session.is_some_and(|session| session.read_only());
1148 // Picked drawings leave the text selection behind them unseen, so it takes no edits.1154 // 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...@@ -237,6 +237,17 @@ type Continuation = Box<dyn FnOnce(&mut State) -> Result<(), Box<dyn Error>> + S
237/// one is open; one cancelled drops its reply unanswered.237/// one is open; one cancelled drops its reply unanswered.
238struct Reply<T>(Box<dyn FnOnce(T) + Send>);238struct 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
240impl<T: Send + 'static> Reply<T> {251impl<T: Send + 'static> Reply<T> {
241 fn new(252 fn new(
242 proxy: &EventLoopProxy<UserEvent>,253 proxy: &EventLoopProxy<UserEvent>,
...@@ -877,6 +888,8 @@ struct State {...@@ -877,6 +888,8 @@ struct State {
877 /// Snowbound's own dialogs, first the one shown, where the desktop has none.888 /// Snowbound's own dialogs, first the one shown, where the desktop has none.
878 #[cfg(target_os = "linux")]889 #[cfg(target_os = "linux")]
879 asking: std::collections::VecDeque<dialog::Dialog>,890 asking: std::collections::VecDeque<dialog::Dialog>,
891 #[cfg(any(windows, target_os = "linux"))]
892 prompt: std::sync::Weak<Prompt>,
880 /// What has been read in each notebook, on this computer.893 /// What has been read in each notebook, on this computer.
881 reads: unread::Reads,894 reads: unread::Reads,
882 #[cfg(feature = "live")]895 #[cfg(feature = "live")]
...@@ -1356,6 +1369,8 @@ impl State {...@@ -1356,6 +1369,8 @@ impl State {
1356 unpacking: None,1369 unpacking: None,
1357 #[cfg(target_os = "linux")]1370 #[cfg(target_os = "linux")]
1358 asking: Default::default(),1371 asking: Default::default(),
1372 #[cfg(any(windows, target_os = "linux"))]
1373 prompt: Default::default(),
1359 reads,1374 reads,
1360 #[cfg(feature = "live")]1375 #[cfg(feature = "live")]
1361 peers: Default::default(),1376 peers: Default::default(),
...@@ -3652,13 +3667,23 @@ impl State {...@@ -3652,13 +3667,23 @@ impl State {
3652 Command::RestoreVersion { page, version } => self.restore_version(page, version)?,3667 Command::RestoreVersion { page, version } => self.restore_version(page, version)?,
3653 Command::DeletePageVersion { page, version } => self.delete_version(page, version)?,3668 Command::DeletePageVersion { page, version } => self.delete_version(page, version)?,
3654 Command::DeleteAllVersions(scope) => self.delete_all_versions(scope)?,3669 Command::DeleteAllVersions(scope) => self.delete_all_versions(scope)?,
3655 Command::OpenNotebook => platform::pick_notebook(3670 Command::OpenNotebook => {
3656 "Open Notebook",3671 #[cfg(any(windows, target_os = "linux"))]
3657 self.reply(|state, path: PathBuf| {3672 let prompt = self.prompt.upgrade().unwrap_or_else(|| {
3658 state.open_path(&path);3673 let prompt = Arc::new(Prompt(self.proxy.clone()));
3659 Ok(())3674 self.prompt = Arc::downgrade(&prompt);
3660 }),3675 prompt
3661 ),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 }
3662 Command::OpenFromServer(location) => self.open_server(location.as_deref()),3687 Command::OpenFromServer(location) => self.open_server(location.as_deref()),
3663 #[cfg(feature = "live")]3688 #[cfg(feature = "live")]
3664 Command::OpenShared => self.open_shared(),3689 Command::OpenShared => self.open_shared(),