From d32119c377c795cbf67e02c9790b9c6c8c053c4e Mon Sep 17 00:00:00 2001 From: Marco Arnulfo <103679419+marcoarnulfo@users.noreply.github.com> Date: Sun, 18 Oct 2026 10:44:23 +0200 Subject: [PATCH] Copying a picture or shape replaces the clipboard's old text, so Paste duplicates it (#2306) --- crates/ui-egui/src/lib.rs | 33 ++++++++++++++++++++++++++ crates/ui-egui/src/system_clipboard.rs | 5 ++++ 2 files changed, 38 insertions(+) diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 968fc6db9..b7142da10 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -4158,6 +4158,39 @@ mod tests { assert!(copied(&ctx).contains(&text), "copy put {text:?} on the clipboard: {:?}", copied(&ctx)); } + /// #2276: copying a selected picture, which has no text, still replaces what the system + /// clipboard held, so Paste inserts the picture instead of the text copied before it. + #[test] + fn copying_a_picture_replaces_the_clipboards_old_text() { + let last_copied = |ctx: &egui::Context| -> Option { + ctx.output(|o| { + o.commands.iter().rev().find_map(|c| match c { + egui::OutputCommand::CopyText(t) => Some(t.clone()), + _ => None, + }) + }) + }; + let ctx = egui::Context::default(); + let mut a = typed(); + a.ctx = Some(ctx.clone()); + a.run("select.all", json!({})).unwrap(); + a.run("edit.copy", json!({})).unwrap(); + assert_eq!(last_copied(&ctx).as_deref(), Some(UNSAVED)); + + a.run("select.collapse", json!({"end": true})).unwrap(); + let data = wordcraft_engine::cmd::insert::base64_encode(&png_bytes([200, 30, 30, 255])); + a.run("insert.picture", json!({"data": data})).unwrap(); + assert!(crate::objects::selected(&a).is_some()); + a.run("edit.copy", json!({})).unwrap(); + assert_eq!(last_copied(&ctx).as_deref(), Some(""), "the old text is gone from the system clipboard"); + + // The system clipboard now holds no text, so Paste finds no text to send: our copy. + a.run("select.collapse", json!({"end": true})).unwrap(); + a.run("edit.paste", json!({})).unwrap(); + assert_eq!(object_count(&mut a), 2, "Paste duplicated the picture"); + assert_eq!(body_text(&a).matches(UNSAVED).count(), 1, "and didn't paste the old text"); + } + /// #1265: every user entry point publishes Cut to the one clipboard sink — the ribbon's button /// and a real Cmd/Ctrl+X key event, not only the generic command dispatch the sink sits behind. #[test] diff --git a/crates/ui-egui/src/system_clipboard.rs b/crates/ui-egui/src/system_clipboard.rs index f057a55bf..893e1e017 100644 --- a/crates/ui-egui/src/system_clipboard.rs +++ b/crates/ui-egui/src/system_clipboard.rs @@ -73,6 +73,11 @@ pub(crate) fn fill(app: &mut WordApp, id: &str, params: &mut Value) -> Option