From 7a523a0f4406fcda15e5e6c1f721ad4772fb063d Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sat, 29 Aug 2026 23:12:26 +0300 Subject: [PATCH] fix(clipboard): share COPY across drawings --- src/app/command_driver.rs | 30 ++++++++++++++ src/app/commands/blocks.rs | 64 ++++++++++++++--------------- src/command.rs | 2 + src/modules/draw/clipboard/paste.rs | 2 +- src/modules/draw/modify/copy.rs | 5 ++- 5 files changed, 67 insertions(+), 36 deletions(-) diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index cb72dd4a8..40eab62e1 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -1216,6 +1216,36 @@ impl OpenCADStudio { self.commit_undo_delta(i, pd); } } + CmdResult::CopyToClipboard { handles, base } => { + let count = self.copy_entities_to_clipboard(i, &handles, base); + self.command_line.push_info(crate::tf!( + "{} object(s) copied to clipboard.", + count + ).as_ref()); + let prompt = self.tabs[i] + .active_cmd + .as_ref() + .map(|command| command.prompt()); + if let Some(prompt) = prompt { + self.command_line.push_info(&prompt); + } + self.command_line.set_step_options( + self.tabs[i] + .active_cmd + .as_ref() + .map(|command| command.options()) + .unwrap_or_default(), + ); + if !self.tabs[i] + .active_cmd + .as_ref() + .is_some_and(|command| command.entity_pick_highlights_hover()) + { + self.tabs[i].scene.set_hover_highlight(None); + } + self.sync_dyn_fields(); + self.refresh_area_preview(i); + } CmdResult::CommitAndExit(entity) => { // For XATTACH: ensure the xref block definition exists before // committing the INSERT entity that references it. diff --git a/src/app/commands/blocks.rs b/src/app/commands/blocks.rs index 2cc1bd4d9..ee29c26b0 100644 --- a/src/app/commands/blocks.rs +++ b/src/app/commands/blocks.rs @@ -1,6 +1,28 @@ use super::*; impl OpenCADStudio { + pub(in crate::app) fn copy_entities_to_clipboard( + &mut self, + i: usize, + handles: &[acadrust::Handle], + base: glam::DVec3, + ) -> usize { + let (entities, deps) = { + let document = &self.tabs[i].scene.document; + let entities: Vec<_> = handles + .iter() + .filter_map(|&handle| document.get_entity(handle).cloned()) + .collect(); + let deps = super::super::ClipboardDeps::capture(document, &entities); + (entities, deps) + }; + let count = entities.len(); + self.clipboard_base = base; + self.clipboard = entities; + self.clipboard_deps = deps; + count + } + pub(super) fn dispatch_blocks(&mut self, cmd: &str, i: usize) -> Option> { match cmd { // ── BASE — drawing insertion base point ─────────────────────── @@ -79,22 +101,14 @@ impl OpenCADStudio { self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { - let entities: Vec<_> = handles - .iter() - .filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned()) - .collect(); - self.clipboard_base = super::super::helpers::entities_lower_left_by_bbox( + let base = super::super::helpers::entities_lower_left_by_bbox( &self.tabs[i].scene.document, &handles, ); - self.clipboard = entities; - self.clipboard_deps = super::super::ClipboardDeps::capture( - &self.tabs[i].scene.document, - &self.clipboard, - ); + let count = self.copy_entities_to_clipboard(i, &handles, base); self.command_line.push_info(crate::tf!( "{} object(s) copied to clipboard.", - self.clipboard.len() + count ).as_ref()); } } @@ -136,19 +150,10 @@ impl OpenCADStudio { .collect(); if coords.len() == 3 && !handles.is_empty() { let base = glam::DVec3::new(coords[0], coords[1], coords[2]); - let entities: Vec<_> = handles - .iter() - .filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned()) - .collect(); - self.clipboard_base = base; - self.clipboard = entities; - self.clipboard_deps = super::super::ClipboardDeps::capture( - &self.tabs[i].scene.document, - &self.clipboard, - ); + let count = self.copy_entities_to_clipboard(i, &handles, base); self.command_line.push_info(crate::tf!( "{} object(s) copied to clipboard (base {:.3},{:.3}).", - self.clipboard.len(), + count, base.x, base.y ).as_ref()); @@ -169,20 +174,11 @@ impl OpenCADStudio { self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { - let entities: Vec<_> = handles - .iter() - .filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned()) - .collect(); - self.clipboard_base = super::super::helpers::entities_lower_left_by_bbox( + let base = super::super::helpers::entities_lower_left_by_bbox( &self.tabs[i].scene.document, &handles, ); - let count = entities.len(); - self.clipboard = entities; - self.clipboard_deps = super::super::ClipboardDeps::capture( - &self.tabs[i].scene.document, - &self.clipboard, - ); + let count = self.copy_entities_to_clipboard(i, &handles, base); self.push_undo_snapshot(i, "CUTCLIP"); self.tabs[i].scene.erase_entities(&handles); self.tabs[i].scene.deselect_all(); @@ -193,7 +189,7 @@ impl OpenCADStudio { } } - "PASTECLIP" => { + "PASTE" | "PASTECLIP" => { if self.clipboard.is_empty() { return Some(self.read_system_clipboard_for_paste()); } else { diff --git a/src/command.rs b/src/command.rs index 405e8140e..3ab5a167a 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1299,6 +1299,8 @@ pub enum CmdResult { TransformSelected(Vec, EntityTransform), /// Copy selected entities with a transform; command stays active for more copies. CopySelected(Vec, EntityTransform), + /// Store selected entities in the shared clipboard; command stays active. + CopyToClipboard { handles: Vec, base: DVec3 }, /// Commit a hatch fill (stored in Scene::hatches, not the DXF document). CommitHatch(HatchModel), /// Commit a hatch with the selected hatch's entity colour and transparency. diff --git a/src/modules/draw/clipboard/paste.rs b/src/modules/draw/clipboard/paste.rs index 1b65b985b..fe071f8dd 100644 --- a/src/modules/draw/clipboard/paste.rs +++ b/src/modules/draw/clipboard/paste.rs @@ -132,6 +132,6 @@ impl CadCommand for PasteCommand { // ── Autocomplete registry ───────────────────────────────── -inventory::submit!(crate::command::CommandRegistration { names: &["PASTECLIP"] }); // PasteCommand +inventory::submit!(crate::command::CommandRegistration { names: &["PASTE", "PASTECLIP"] }); // PasteCommand inventory::submit!(crate::command::CommandRegistration { names: &["PASTEORIG"] }); inventory::submit!(crate::command::CommandRegistration { names: &["PASTEBLOCK"] }); diff --git a/src/modules/draw/modify/copy.rs b/src/modules/draw/modify/copy.rs index 37b3e10bf..130cf48dc 100644 --- a/src/modules/draw/modify/copy.rs +++ b/src/modules/draw/modify/copy.rs @@ -92,7 +92,10 @@ impl CadCommand for CopyCommand { match &self.step { Step::Base => { self.step = Step::Placing(pt); - CmdResult::NeedPoint + CmdResult::CopyToClipboard { + handles: self.handles.clone(), + base: pt, + } } Step::Placing(base) => { let delta = pt - *base;