From d209e7bbb5ede08f3e49baedfcf34c1224800443 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Sun, 28 Jun 2026 10:28:23 +0300 Subject: [PATCH] fix(selection): clear selection when an interactive command ends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Editing tools (MOVE, COPY, STRETCH, ROTATE, OFFSET, SCALE, MIRROR) and other interactive commands left their working set selected after they finished, so the next edit silently reused it — confusing especially for copy-producing tools. Drop the selection centrally in apply_cmd_result when a command ends (active_cmd Some->None), excepting Relaunch/Dispatch which hand a deliberate selection to a chained command. Pure-selection commands run without an active command, so their result is preserved. Closes #207 Co-Authored-By: Claude Opus 4.8 --- src/app/command_driver.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/app/command_driver.rs b/src/app/command_driver.rs index a5dd08d24..3d4d08a95 100644 --- a/src/app/command_driver.rs +++ b/src/app/command_driver.rs @@ -141,7 +141,34 @@ impl OpenCADStudio { } } + /// Applies one command result, then — when that result ended the active + /// command — drops the selection. Editing tools (MOVE, COPY, ROTATE, …) and + /// every other interactive command leave nothing selected once they finish, + /// so a follow-up edit doesn't silently reuse the previous working set. + /// + /// `Relaunch`/`Dispatch` are excepted: they end the front-end command only + /// to immediately start another and hand it a deliberate selection (the + /// pick-first selector relaunching MOVE on the picked set works this way). + /// Pure-selection commands (SELECTALL, QSELECT, …) run without an active + /// command, so `was_active` is false and their selection is preserved. pub(super) fn apply_cmd_result(&mut self, result: CmdResult) -> Task { + let was_active = self.tabs[self.active_tab].active_cmd.is_some(); + let preserve_selection = + matches!(result, CmdResult::Relaunch(..) | CmdResult::Dispatch(..)); + let task = self.apply_cmd_result_inner(result); + let i = self.active_tab; + if was_active + && !preserve_selection + && self.tabs[i].active_cmd.is_none() + && !self.tabs[i].scene.selected.is_empty() + { + self.tabs[i].scene.deselect_all(); + self.refresh_properties(); + } + task + } + + fn apply_cmd_result_inner(&mut self, result: CmdResult) -> Task { let i = self.active_tab; match result { CmdResult::NeedPoint => {