From 55022ae60e1520b5227cf4715ada442572b8fa26 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 5 Jun 2026 10:44:31 +0300 Subject: [PATCH] feat(grip): Esc cancels an in-progress grip drag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The grip drag mutates the document live (so grips and the properties panel track the cursor), which meant Esc left the entity at its dragged position. Back up the edited entity when the drag starts (the same first-move point that hides it for the overlay preview) and, on Esc, restore that backup before un-hiding and re-tessellating — reverting the entity to where it was when the drag began. A normal release drops the backup and keeps the dragged geometry. Co-Authored-By: Claude Opus 4.8 --- src/app/mod.rs | 6 ++++++ src/app/update.rs | 21 ++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 8ac1d22e4..3f79d0e10 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -237,6 +237,11 @@ pub(super) struct OpenCADStudio { /// updates only the overlay rather than re-tessellating the whole model. /// Committed (un-hidden + one re-tess) when the drag ends. `None` = idle. grip_preview_handle: Option, + /// Snapshot of the edited entity taken at the start of a grip drag, used to + /// restore it if the user presses Esc to cancel the drag. The drag mutates + /// the document live (so grips / properties track), so cancel reverts from + /// this backup. Dropped (kept) on a normal commit. + grip_original: Option, /// Open Quick Select panel state. `None` = panel closed. Filters are /// applied via `Message::QSelectApply`; the panel is dismissed on /// Apply / Cancel / Esc / outside-click. @@ -1279,6 +1284,7 @@ impl OpenCADStudio { grip_pending: None, grip_add_provisional: None, grip_preview_handle: None, + grip_original: None, qselect: None, show_ucs_icon: true, show_viewcube: true, diff --git a/src/app/update.rs b/src/app/update.rs index 48c3bff4b..50abeeaba 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -1313,13 +1313,20 @@ impl OpenCADStudio { self.tabs[i].scene.bump_geometry(); self.refresh_selected_grips(); } - // Commit/cleanup an in-progress grip drag: un-hide the - // edited entity, re-tessellate once, drop the preview. + // Cancel an in-progress grip drag: restore the edited + // entity from its pre-drag backup, un-hide it, re-tessellate + // once, and drop the preview. if let Some(h) = self.grip_preview_handle.take() { let i = self.active_tab; + if let Some(orig) = self.grip_original.take() { + if let Some(e) = self.tabs[i].scene.document.get_entity_mut(h) { + *e = orig; + } + } self.tabs[i].scene.hidden.remove(&h); self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.bump_geometry(); + self.refresh_selected_grips(); } self.tabs[self.active_tab].snap_result = None; self.refresh_properties(); @@ -2116,6 +2123,9 @@ impl OpenCADStudio { if let Some(prev) = self.grip_preview_handle.take() { self.tabs[i].scene.hidden.remove(&prev); } + // Back up the original geometry so Esc can cancel the drag. + self.grip_original = + self.tabs[i].scene.document.get_entity(grip.handle).cloned(); self.tabs[i].scene.hidden.insert(grip.handle); self.tabs[i].scene.bump_geometry(); self.grip_preview_handle = Some(grip.handle); @@ -2633,10 +2643,11 @@ impl OpenCADStudio { return Task::none(); } self.tabs[i].active_grip = None; - // Commit the grip drag: un-hide the edited entity and - // re-tessellate the base once with its final geometry, - // dropping the overlay preview. + // Commit the grip drag: keep the doc's dragged geometry + // (drop the cancel backup), un-hide the edited entity and + // re-tessellate the base once, dropping the overlay preview. if let Some(h) = self.grip_preview_handle.take() { + self.grip_original = None; self.tabs[i].scene.hidden.remove(&h); self.tabs[i].scene.clear_preview_wire(); self.tabs[i].scene.bump_geometry();