feat(statusbar): isolate / hide objects with status pill

Add a hidden-entity set to the scene that the tessellation visibility
test skips, so hidden objects neither render nor hit-test. New commands
ISOLATEOBJECTS (show only the selection), HIDEOBJECTS (hide the
selection) and UNISOLATEOBJECTS (restore). An ISO status pill lights up
while anything is hidden and restores everything on click.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-04 22:51:23 +03:00
1 parent 243a0bc923
commit e0e7897952
5 files changed
+106

No files matched your search

+38
View File
@@ -332,6 +332,44 @@ impl OpenCADStudio {
}
}
// ISOLATEOBJECTS — hide every object except the current selection
"ISOLATEOBJECTS" => {
if self.tabs[i].scene.selected.is_empty() {
self.command_line
.push_error("ISOLATEOBJECTS: select the objects to isolate first.");
} else {
let n = self.tabs[i].scene.selected.len();
self.tabs[i].scene.isolate_selected();
self.command_line.push_info(&format!(
"Isolated {n} object(s). UNISOLATEOBJECTS to restore."
));
}
}
// HIDEOBJECTS — hide the current selection
"HIDEOBJECTS" => {
if self.tabs[i].scene.selected.is_empty() {
self.command_line
.push_error("HIDEOBJECTS: select the objects to hide first.");
} else {
let n = self.tabs[i].scene.selected.len();
self.tabs[i].scene.hide_selected();
self.command_line
.push_info(&format!("Hid {n} object(s). UNISOLATEOBJECTS to restore."));
}
}
// UNISOLATEOBJECTS — bring back everything hidden by Isolate / Hide
"UNISOLATEOBJECTS" => {
if self.tabs[i].scene.is_isolation_active() {
self.tabs[i].scene.end_isolation();
self.command_line
.push_info("Isolation ended — all objects shown.");
} else {
self.command_line.push_info("No hidden objects.");
}
}
// LAYUNISO — restore all layers that were turned off by LAYISO (turn all on)
"LAYUNISO" => {
self.push_undo_snapshot(i, "LAYUNISO");
+1
View File
@@ -1159,6 +1159,7 @@ impl OpenCADStudio {
self.clean_screen,
tab.scene.document.header.insertion_units,
self.units_popup_open,
tab.scene.is_isolation_active(),
&self.statusbar_config,
)
})
+51
View File
@@ -549,6 +549,10 @@ pub struct Scene {
pub document: CadDocument,
/// Currently selected entity handles.
pub selected: HashSet<Handle>,
/// Entity handles hidden by Isolate / Hide. Empty = nothing hidden.
/// `tessellate_block`'s visibility test skips these, so they neither
/// render nor hit-test until isolation ends.
pub hidden: HashSet<Handle>,
/// In-progress preview wires while a command is active (rubber-band + object ghosts).
pub preview_wires: Vec<WireModel>,
/// Committed-segment wire drawn during multi-point commands (normal colour).
@@ -693,6 +697,7 @@ impl Scene {
selection: Rc::new(RefCell::new(SelectionState::default())),
document: CadDocument::new(),
selected: HashSet::new(),
hidden: HashSet::new(),
preview_wires: vec![],
interim_wire: None,
camera_generation: 0,
@@ -1316,6 +1321,48 @@ impl Scene {
.count()
}
/// True when any entities are hidden by Isolate / Hide.
pub fn is_isolation_active(&self) -> bool {
!self.hidden.is_empty()
}
/// Hide every drawable entity except the current selection (Isolate).
pub fn isolate_selected(&mut self) {
if self.selected.is_empty() {
return;
}
let keep = self.selected.clone();
self.hidden = self
.document
.entities()
.map(|e| e.common().handle)
.filter(|h| !h.is_null() && !keep.contains(h))
.collect();
self.selected.clear();
self.bump_geometry();
}
/// Hide the current selection (Hide Objects).
pub fn hide_selected(&mut self) {
if self.selected.is_empty() {
return;
}
for h in self.selected.iter().copied() {
self.hidden.insert(h);
}
self.selected.clear();
self.bump_geometry();
}
/// Clear isolation — bring every hidden entity back (End Isolation).
pub fn end_isolation(&mut self) {
if self.hidden.is_empty() {
return;
}
self.hidden.clear();
self.bump_geometry();
}
/// True if any currently selected entity is a Viewport.
/// Used to enable the scale picker when a viewport is selected in paper space.
pub fn has_selected_viewport(&self) -> bool {
@@ -1869,6 +1916,10 @@ impl Scene {
if c.invisible {
return false;
}
// Isolate / Hide: skip entities the user has hidden.
if !self.hidden.is_empty() && self.hidden.contains(&c.handle) {
return false;
}
// Block/BlockEnd are block-defn sentinels, not drawable geometry.
// Without this skip they fall through to fallback_geometry's `_`
// arm and emit a 1-unit phantom segment at world_offset that
+12
View File
@@ -59,6 +59,8 @@ impl StatusBar {
insertion_units: i16,
// True while the drawing-units picker is open.
units_popup_open: bool,
// True when objects are hidden by Isolate / Hide.
isolation_active: bool,
// Which pills the user has chosen to show on the bar.
config: &'a StatusBarConfig,
) -> Element<'a, Message> {
@@ -173,6 +175,16 @@ impl StatusBar {
"Drawing Units (INSUNITS)\nClick to change",
));
}
if vis(StatusPill::Isolate) {
right_status = right_status.push(tip(
toggle_pill(
"ISO",
isolation_active,
Message::Command("UNISOLATEOBJECTS".into()),
),
"Isolate Objects\nISOLATEOBJECTS / HIDEOBJECTS to hide; click to restore",
));
}
if vis(StatusPill::Vp) && !vp_label.is_empty() {
right_status = right_status.push(tip(
status_pill(vp_label).into(),
+4
View File
@@ -23,6 +23,7 @@ pub enum StatusPill {
Space,
Scale,
Units,
Isolate,
Vp,
CleanScreen,
}
@@ -43,6 +44,7 @@ impl StatusPill {
StatusPill::Space,
StatusPill::Scale,
StatusPill::Units,
StatusPill::Isolate,
StatusPill::Vp,
StatusPill::CleanScreen,
];
@@ -62,6 +64,7 @@ impl StatusPill {
StatusPill::Space => "space",
StatusPill::Scale => "scale",
StatusPill::Units => "units",
StatusPill::Isolate => "isolate",
StatusPill::Vp => "vp",
StatusPill::CleanScreen => "cleanscreen",
}
@@ -82,6 +85,7 @@ impl StatusPill {
StatusPill::Space => "Model/Paper Space",
StatusPill::Scale => "Annotation Scale",
StatusPill::Units => "Drawing Units",
StatusPill::Isolate => "Isolate Objects",
StatusPill::Vp => "Viewport Count",
StatusPill::CleanScreen => "Clean Screen",
}