diff --git a/src/app/mod.rs b/src/app/mod.rs index 04568bab..68a87ae1 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -246,6 +246,10 @@ pub(super) struct OpenCADStudio { clipboard: Vec, /// Centroid of the clipboard entities (XZ plane, Y-up). clipboard_centroid: glam::Vec3, + /// True while the Shift key is held — drives subtractive pick (Shift+click + /// removes the picked entity from the selection). Tracked from keyboard + /// modifier-change events since mouse click messages carry no modifiers. + shift_down: bool, /// Which layout tab has its context menu open (None = closed). layout_context_menu: Option, /// Inline rename state: (original_name, current_edit_value). @@ -702,6 +706,9 @@ pub enum Message { /// Extend the current selection with every entity in the active /// layout that matches a selected entity by (type, layer). SelectSimilar, + /// Keyboard modifier state changed — tracks whether Shift is held so the + /// pick path can do subtractive (Shift+click) selection. + SetShiftDown(bool), // ── Draw Order context menu ───────────────────────────────────────── /// Toggle the Draw Order sub-items in the viewport context menu. DrawOrderSubmenuToggle, @@ -941,6 +948,7 @@ impl OpenCADStudio { update_notice_body: None, clipboard: Vec::new(), clipboard_centroid: glam::Vec3::ZERO, + shift_down: false, layout_context_menu: None, layout_rename_state: None, last_vp_click_time: None, diff --git a/src/app/update.rs b/src/app/update.rs index 79a31a92..7bbd1690 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -2782,8 +2782,16 @@ impl OpenCADStudio { ) }); if let Some(handle) = hit { - self.tabs[i].scene.select_entity(handle, true); - self.tabs[i].scene.expand_selection_for_groups(&[handle]); + // Individual picks accumulate (issue #47): + // each plain click adds to the selection, + // Shift+click removes the picked entity. + // Esc / empty-space click clears. + if self.shift_down { + self.tabs[i].scene.deselect_entity(handle); + } else { + self.tabs[i].scene.select_entity(handle, false); + self.tabs[i].scene.expand_selection_for_groups(&[handle]); + } self.refresh_properties(); selection_just_completed = true; } else { @@ -3380,6 +3388,11 @@ impl OpenCADStudio { Task::none() } + Message::SetShiftDown(down) => { + self.shift_down = down; + Task::none() + } + Message::DrawOrderSubmenuToggle => { let i = self.active_tab; let mut sel = self.tabs[i].scene.selection.borrow_mut(); diff --git a/src/app/view.rs b/src/app/view.rs index a1997a9c..454a3f1f 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -995,6 +995,9 @@ impl OpenCADStudio { iced::Event::Window(window::Event::Resized(sz)) => { Some(Message::WindowResized(sz.width as f32, sz.height as f32)) } + iced::Event::Keyboard(keyboard::Event::ModifiersChanged(m)) => { + Some(Message::SetShiftDown(m.shift())) + } iced::Event::Keyboard(keyboard::Event::KeyPressed { key, modifiers, text, .. }) => { diff --git a/src/scene/mod.rs b/src/scene/mod.rs index dc99dc21..61230869 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -3808,6 +3808,13 @@ impl Scene { self.bump_geometry(); } + /// Remove a single entity from the selection (Shift+click subtractive pick). + pub fn deselect_entity(&mut self, handle: Handle) { + if self.selected.remove(&handle) { + self.bump_geometry(); + } + } + pub fn selected_entities(&self) -> Vec<(Handle, &EntityType)> { self.selected .iter()