diff --git a/src/app/commands/display.rs b/src/app/commands/display.rs index d109c2549..188aa2441 100644 --- a/src/app/commands/display.rs +++ b/src/app/commands/display.rs @@ -81,6 +81,7 @@ impl OpenCADStudio { let at_origin = sub == "ORIGIN"; // Update model-space icon flags. self.show_ucs_icon = visible; + self.ribbon.set_ucs_icon(visible); if sub == "NOORIGIN" || sub == "ORIGIN" { self.ucs_icon_at_origin = at_origin; } @@ -104,6 +105,7 @@ impl OpenCADStudio { self.push_undo_snapshot(i, "UCSICON"); let visible = !self.show_ucs_icon; self.show_ucs_icon = visible; + self.ribbon.set_ucs_icon(visible); for entity in self.tabs[i].scene.document.entities_mut() { if let acadrust::EntityType::Viewport(vp) = entity { vp.status.ucs_icon_visible = visible; diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 1fda1a44c..fc5e68102 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -1567,14 +1567,17 @@ impl OpenCADStudio { } Message::ToggleProperties => { self.show_properties ^= true; + self.ribbon.set_properties(self.show_properties); Task::none() } Message::ToggleFileTabs => { self.show_file_tabs ^= true; + self.ribbon.set_file_tabs(self.show_file_tabs); Task::none() } Message::ToggleLayoutTabs => { self.show_layout_tabs ^= true; + self.ribbon.set_layout_tabs(self.show_layout_tabs); Task::none() } Message::ToggleOTrack => { diff --git a/src/ui/ribbon/mod.rs b/src/ui/ribbon/mod.rs index 5ae8dc433..620663041 100644 --- a/src/ui/ribbon/mod.rs +++ b/src/ui/ribbon/mod.rs @@ -37,6 +37,14 @@ pub struct Ribbon { pub ortho_mode: bool, /// ViewCube (NAVVCUBE) visibility — drives the View Cube button highlight. pub show_viewcube: bool, + /// UCS icon (UCSICON) visibility — drives the UCS Icon button highlight. + pub show_ucs_icon: bool, + /// Properties panel (PROPERTIES) visibility — drives the Properties button highlight. + pub show_properties: bool, + /// File tabs (FILETAB) visibility — drives the File Tabs button highlight. + pub show_file_tabs: bool, + /// Layout tabs (LAYOUTTAB) visibility — drives the Layout Tabs button highlight. + pub show_layout_tabs: bool, pub open_dropdown: Option, /// Title of the collapsed panel whose flyout is currently open, if any. pub collapsed_open: Option, @@ -136,6 +144,10 @@ impl Ribbon { wireframe: false, ortho_mode: true, show_viewcube: true, + show_ucs_icon: true, + show_properties: true, + show_file_tabs: true, + show_layout_tabs: true, open_dropdown: None, collapsed_open: None, last_panel_tool: HashMap::default(), @@ -280,6 +292,31 @@ impl Ribbon { pub fn set_viewcube(&mut self, on: bool) { self.show_viewcube = on; } + pub fn set_ucs_icon(&mut self, on: bool) { + self.show_ucs_icon = on; + } + pub fn set_properties(&mut self, on: bool) { + self.show_properties = on; + } + pub fn set_file_tabs(&mut self, on: bool) { + self.show_file_tabs = on; + } + pub fn set_layout_tabs(&mut self, on: bool) { + self.show_layout_tabs = on; + } + /// Snapshot of every ribbon toggle's live state, for the render path. + fn toggle_state(&self) -> widgets::ToggleState { + use widgets::ToggleState; + ToggleState { + wireframe: self.wireframe, + ortho_mode: self.ortho_mode, + show_viewcube: self.show_viewcube, + show_ucs_icon: self.show_ucs_icon, + show_properties: self.show_properties, + show_file_tabs: self.show_file_tabs, + show_layout_tabs: self.show_layout_tabs, + } + } pub fn toggle_dropdown(&mut self, id: &str) { if self.open_dropdown.as_deref() == Some(id) { @@ -532,7 +569,9 @@ impl Ribbon { // button. See `CollapsePanels`. let panels: Vec> = groups .iter() - .map(|g| Panel { + .map(|g| { + let ts = self.toggle_state(); + Panel { id: g.title.to_string(), full: render_group( false, @@ -540,9 +579,7 @@ impl Ribbon { &self.active_tool, &self.open_dropdown, &self.last_cmd, - self.wireframe, - self.ortho_mode, - self.show_viewcube, + ts, &self.layer_infos, &self.active_layer, self.active_color, @@ -556,9 +593,7 @@ impl Ribbon { &self.active_tool, &self.open_dropdown, &self.last_cmd, - self.wireframe, - self.ortho_mode, - self.show_viewcube, + ts, &self.layer_infos, &self.active_layer, self.active_color, @@ -572,9 +607,7 @@ impl Ribbon { &self.active_tool, &self.open_dropdown, &self.last_cmd, - self.wireframe, - self.ortho_mode, - self.show_viewcube, + ts, &self.layer_infos, &self.active_layer, self.active_color, @@ -589,9 +622,7 @@ impl Ribbon { &self.active_tool, &self.open_dropdown, &self.last_cmd, - self.wireframe, - self.ortho_mode, - self.show_viewcube, + ts, &self.layer_infos, &self.active_layer, self.active_color, @@ -606,9 +637,7 @@ impl Ribbon { &self.active_tool, &self.open_dropdown, &self.last_cmd, - self.wireframe, - self.ortho_mode, - self.show_viewcube, + ts, &self.layer_infos, &self.active_layer, self.active_color, @@ -626,6 +655,7 @@ impl Ribbon { ..Default::default() }) .into(), + } }) .collect(); CollapsePanels::new(panels, self.collapsed_open.clone(), TOOL_BAR_H, BORDER_DARK) @@ -1300,9 +1330,7 @@ fn render_group<'a>( active_tool: &Option, open_dd: &Option, last_cmd: &HashMap<&'static str, &'static str>, - wireframe: bool, - ortho_mode: bool, - show_viewcube: bool, + state: widgets::ToggleState, layer_infos: &'a [LayerInfo], active_layer: &'a str, active_color: AcadColor, @@ -1329,9 +1357,7 @@ fn render_group<'a>( active_tool, open_dd, last_cmd, - wireframe, - ortho_mode, - show_viewcube, + state, layer_infos, active_layer, active_color, @@ -1346,9 +1372,7 @@ fn render_group<'a>( active_tool, open_dd, last_cmd, - wireframe, - ortho_mode, - show_viewcube, + state, )); if small_buf.len() == 3 { flush_small_col(&mut small_buf, &mut items_row); @@ -1426,9 +1450,7 @@ fn collapse_button<'a>( active_tool: &Option, open_dd: &Option, last_cmd: &HashMap<&'static str, &'static str>, - wireframe: bool, - ortho_mode: bool, - show_viewcube: bool, + state: widgets::ToggleState, layer_infos: &'a [LayerInfo], active_layer: &'a str, active_color: AcadColor, @@ -1494,9 +1516,7 @@ fn collapse_button<'a>( active_tool, open_dd, last_cmd, - wireframe, - ortho_mode, - show_viewcube, + state, layer_infos, active_layer, active_color, @@ -1510,9 +1530,7 @@ fn collapse_button<'a>( active_tool, open_dd, last_cmd, - wireframe, - ortho_mode, - show_viewcube, + state, layer_infos, active_layer, active_color, diff --git a/src/ui/ribbon/widgets.rs b/src/ui/ribbon/widgets.rs index dbd92efd3..aa8ef6d02 100644 --- a/src/ui/ribbon/widgets.rs +++ b/src/ui/ribbon/widgets.rs @@ -19,6 +19,21 @@ use crate::ui::properties::{acad_color_display, LwItem}; use super::LayerInfo; +/// Live on/off state of every ribbon toggle button. Passed as a single value +/// through the render path so adding a new toggle only touches `is_active_tool` +/// plus the `Ribbon::toggle_state` builder — not every render-function signature +/// and call site. +#[derive(Clone, Copy)] +pub(super) struct ToggleState { + pub wireframe: bool, + pub ortho_mode: bool, + pub show_viewcube: bool, + pub show_ucs_icon: bool, + pub show_properties: bool, + pub show_file_tabs: bool, + pub show_layout_tabs: bool, +} + // ── Layout constants (single source of truth: ROW_H from ui::mod) ───────── use crate::ui::ROW_H; @@ -287,16 +302,18 @@ pub(super) fn make_icon(icon: IconKind, size: f32) -> Element<'static, Message> pub(super) fn is_active_tool( id: &str, active_tool: &Option, - wireframe: bool, - ortho_mode: bool, - show_viewcube: bool, + state: &ToggleState, ) -> bool { match id { - "WIREFRAME" => wireframe, - "SOLID" => !wireframe, - "ORTHO" => ortho_mode, - "PERSP" => !ortho_mode, - "NAVVCUBE" => show_viewcube, + "WIREFRAME" => state.wireframe, + "SOLID" => !state.wireframe, + "ORTHO" => state.ortho_mode, + "PERSP" => !state.ortho_mode, + "NAVVCUBE" => state.show_viewcube, + "UCSICON" => state.show_ucs_icon, + "PROPERTIES" => state.show_properties, + "FILETAB" => state.show_file_tabs, + "LAYOUTTAB" => state.show_layout_tabs, id => active_tool.as_deref() == Some(id), } } @@ -349,15 +366,13 @@ pub(super) fn render_small<'a>( active_tool: &Option, open_dd: &Option, last_cmd: &HashMap<&'static str, &'static str>, - wireframe: bool, - ortho_mode: bool, - show_viewcube: bool, + state: ToggleState, ) -> Element<'a, Message> { match item { // Large variants render small too, so the ribbon can shrink a panel of // large buttons to icon-only columns when the width is tight. RibbonItem::Tool(t) | RibbonItem::LargeTool(t) => { - let active = is_active_tool(t.id, active_tool, wireframe, ortho_mode, show_viewcube); + let active = is_active_tool(t.id, active_tool, &state); let event = t.event.clone(); let tool_id = t.id.to_string(); let tip_text = format!("{}\nCommand: {}", t.label, t.id); @@ -585,9 +600,7 @@ pub(super) fn render_large<'a>( active_tool: &Option, open_dd: &Option, last_cmd: &HashMap<&'static str, &'static str>, - wireframe: bool, - ortho_mode: bool, - show_viewcube: bool, + state: ToggleState, layer_infos: &'a [LayerInfo], active_layer: &'a str, active_color: AcadColor, @@ -601,7 +614,7 @@ pub(super) fn render_large<'a>( // A plain Tool renders large too, so a collapsed panel can show its // representative tool as a big icon. RibbonItem::LargeTool(t) | RibbonItem::Tool(t) => { - let active = is_active_tool(t.id, active_tool, wireframe, ortho_mode, show_viewcube); + let active = is_active_tool(t.id, active_tool, &state); let event = t.event.clone(); let tool_id = t.id.to_string(); let tip_text = format!("{}\nCommand: {}", t.label, t.id); @@ -785,12 +798,10 @@ pub(super) fn render_large<'a>( active_tool, open_dd, last_cmd, - wireframe, - ortho_mode, - show_viewcube, + state, ) } else { - let mp_active = is_active_tool(match_prop.id, active_tool, wireframe, ortho_mode, show_viewcube); + let mp_active = is_active_tool(match_prop.id, active_tool, &state); let mp_event = match_prop.event.clone(); let mp_id = match_prop.id.to_string(); let mp_tip = format!("{}\nCommand: {}", match_prop.label, match_prop.id);