forked from 1b-agent/opencadstudio-upstream
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:
1 parent
243a0bc923
commit
e0e7897952
5 files changed
+106
No files matched your search
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user