UI: a cut's text pastes again once the clipboard has held other text

This commit is contained in:
neilcawse
2026-10-12 21:19:05 -04:00
parent 4d654c5185
commit 95012e03b4
2 changed files with 43 additions and 5 deletions
+10 -5
View File
@@ -255,8 +255,8 @@ pub struct SheetApp {
/// text, GridCraft's copy is the newer one and Paste uses it.
clipboard_text_at_copy: Option<String>,
/// The system clipboard's text right after GridCraft's last cut (the published text, or the
/// text noted for an unpublished cut); `None` after a copy. Once the cut has been pasted, Paste
/// leaves this text alone.
/// text noted for an unpublished cut); `None` after a copy, or once the clipboard is seen
/// holding other text. Once the cut has been pasted, Paste leaves this text alone.
cut_text: Option<String>,
pub started: f64,
}
@@ -414,10 +414,15 @@ impl SheetApp {
///
/// Excel pastes a cut once: after GridCraft's cut has been pasted, the system clipboard text
/// that came with it is not pasted again over the moved block; GridCraft's own (empty)
/// clipboard is pasted instead, which reports "Nothing to paste.".
/// clipboard is pasted instead, which reports "Nothing to paste.". Other text means the
/// clipboard has moved on, so the cut's text is forgotten and pastes normally later.
pub fn paste_external(&mut self, text: String) {
if self.session.clipboard.is_none() && Self::same_clipboard_text(Some(&text), self.cut_text.as_deref()) {
return self.run_or_alert("edit.paste", json!({}));
if Self::same_clipboard_text(Some(&text), self.cut_text.as_deref()) {
if self.session.clipboard.is_none() {
return self.run_or_alert("edit.paste", json!({}));
}
} else {
self.cut_text = None;
}
let html = self.services.paste_html.as_mut().and_then(|read| read());
let params = match html {
+33
View File
@@ -428,3 +428,36 @@ fn cmd_v_after_a_pasted_cut_does_not_paste_the_cut_text_again() {
assert_eq!(value_at(&h, "D4"), Some(Value::from("Name")));
assert!(h.state().toast.is_some() || h.state().message.is_some(), "Paste reports there is nothing to paste");
}
#[test]
fn the_cut_text_pastes_again_once_the_clipboard_has_held_other_text() {
// Once other text has been pasted, the clipboard has moved on from the cut: the same text
// copied again later (from another app, or a cell editor) is a new copy and pastes.
let mut h = harness(Services::default());
h.state_mut().run("selection.set", json!({"cell": "A1"})).unwrap();
h.input_mut().events.push(Event::Cut);
h.step();
let text = h
.output()
.platform_output
.commands
.iter()
.find_map(|c| match c {
OutputCommand::CopyText(text) => Some(text.clone()),
_ => None,
})
.expect("the cut is published");
h.run_steps(2);
h.state_mut().run("selection.set", json!({"cell": "D4"})).unwrap();
h.input_mut().events.push(Event::Paste(text.clone()));
h.run_steps(2);
assert_eq!(value_at(&h, "D4"), Some(Value::from("Name")), "the cut moves A1");
h.state_mut().run("selection.set", json!({"cell": "G1"})).unwrap();
h.input_mut().events.push(Event::Paste("other".into()));
h.run_steps(2);
assert_eq!(value_at(&h, "G1"), Some(Value::from("other")));
h.state_mut().run("selection.set", json!({"cell": "H1"})).unwrap();
h.input_mut().events.push(Event::Paste(text.replace('\n', "\r\n")));
h.run_steps(2);
assert_eq!(value_at(&h, "H1"), Some(Value::from("Name")), "the same text copied afresh pastes");
}