diff --git a/src/app/plugin_host.rs b/src/app/plugin_host.rs index bfb53916f..7d2be9b82 100644 --- a/src/app/plugin_host.rs +++ b/src/app/plugin_host.rs @@ -9701,7 +9701,7 @@ step('undo_move', lambda: M.move([u], (0, 0, 0), (7, 0, 0))) // Verify COGO_LINES does not exist initially assert!(!app.tabs[0].scene.document.layers.contains("COGO_LINES")); - assert!(!app.ribbon.layer_names.contains(&"COGO_LINES".to_string())); + assert!(!app.ribbon.layer_infos.iter().any(|l| l.name == "COGO_LINES")); // 1. Add entity via HostSession (simulating plugin add_entity / add_line) { @@ -9714,7 +9714,7 @@ step('undo_move', lambda: M.move([u], (0, 0, 0), (7, 0, 0))) // Verify layer is created in document AND refreshed in ribbon + layer panel assert!(app.tabs[0].scene.document.layers.contains("COGO_LINES")); - assert!(app.ribbon.layer_names.contains(&"COGO_LINES".to_string()), + assert!(app.ribbon.layer_infos.iter().any(|l| l.name == "COGO_LINES"), "Ribbon layers should contain COGO_LINES after add_entity"); assert!(app.tabs[0].layers.layers.iter().any(|l| l.name == "COGO_LINES"), "Layer panel should contain COGO_LINES after add_entity"); @@ -9726,7 +9726,7 @@ step('undo_move', lambda: M.move([u], (0, 0, 0), (7, 0, 0))) // Verify SURVEY_TRAVERSE layer is created and refreshed in ribbon + layer panel assert!(app.tabs[0].scene.document.layers.contains("SURVEY_TRAVERSE")); - assert!(app.ribbon.layer_names.contains(&"SURVEY_TRAVERSE".to_string()), + assert!(app.ribbon.layer_infos.iter().any(|l| l.name == "SURVEY_TRAVERSE"), "Ribbon layers should contain SURVEY_TRAVERSE after commit_entity"); assert!(app.tabs[0].layers.layers.iter().any(|l| l.name == "SURVEY_TRAVERSE"), "Layer panel should contain SURVEY_TRAVERSE after commit_entity"); diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index ed633bf42..7709bf41c 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -11233,6 +11233,57 @@ mod anno_scale_toggle_tests { } } +#[cfg(test)] +mod dock_visibility_tests { + // #57: one visibility predicate for edge rendering and drag math. Pins + // the full 8-panel table so a newly added panel cannot silently diverge + // (behavior-preserving dedup: passes before and after by construction). + use crate::app::OpenCADStudio; + use crate::ui::dock::PanelId; + + const ALL: [PanelId; 8] = [ + PanelId::Properties, + PanelId::BlockPalette, + PanelId::ExternalReferences, + PanelId::Browser, + PanelId::NodeGraph, + PanelId::PointCloudManager, + PanelId::Count, + PanelId::SheetSetManager, + ]; + + #[test] + fn dock_panel_visible_covers_every_panel() { + let mut app = OpenCADStudio::new_for_test(); + app.automation_op(r#"{"op":"new"}"#); + app.show_properties = true; + app.show_block_palette = true; + app.show_external_references = true; + app.show_browser = true; + app.show_node_graph = true; + app.pc_manager.show = true; + app.count_palette.show = true; + app.sheet_set.show = true; + for id in ALL { + assert!(app.dock_panel_visible(id), "{id:?} must be visible"); + } + app.clean_screen = true; + for id in ALL { + assert!(!app.dock_panel_visible(id), "{id:?} must hide on clean screen"); + } + } + + #[test] + fn dock_panel_hidden_on_start_tab() { + // Fresh app, no drawing opened: the welcome tab shows no panels. + let app = OpenCADStudio::new_for_test(); + assert!(app.tabs[app.active_tab].is_start); + for id in ALL { + assert!(!app.dock_panel_visible(id), "{id:?} must hide on Start"); + } + } +} + #[cfg(test)] mod free_text_entry_tests { //! The free-form text prompts (table cell content, TEXT / MTEXT bodies) diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index 832a5a8f0..52e1ece26 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -1885,20 +1885,10 @@ bg={bg_ms:.1}ms n={view_count}" // view. Auto-collapsing (pinned) panels that aren't being hovered // reduce to a rail sharing 1/N of the edge column's height; the // hovered or unpinned panel expands to the full column height on top. + // Single source of truth with dock_visible_len (#57): one new panel + // updated in only one place used to break drag math vs rendering. let visible_panel = |id: crate::ui::dock::PanelId| -> bool { - if tab.is_start || self.clean_screen { - return false; - } - match id { - crate::ui::dock::PanelId::Properties => self.show_properties, - crate::ui::dock::PanelId::BlockPalette => self.show_block_palette, - crate::ui::dock::PanelId::ExternalReferences => self.show_external_references, - crate::ui::dock::PanelId::Browser => self.show_browser, - crate::ui::dock::PanelId::NodeGraph => self.show_node_graph, - crate::ui::dock::PanelId::PointCloudManager => self.pc_manager.show, - crate::ui::dock::PanelId::Count => self.count_palette.show, - crate::ui::dock::PanelId::SheetSetManager => self.sheet_set.show, - } + self.dock_panel_visible(id) }; let edge_stack = |side: crate::app::config::DockSide| -> Option> { let ids: Vec = match side { diff --git a/src/ui/ribbon/mod.rs b/src/ui/ribbon/mod.rs index 69ac52ac5..c7d7a4f8e 100644 --- a/src/ui/ribbon/mod.rs +++ b/src/ui/ribbon/mod.rs @@ -73,7 +73,6 @@ pub struct Ribbon { /// shows this tool on its button, defaulting to the panel's first tool. last_panel_tool: HashMap<&'static str, &'static str>, last_cmd: HashMap<&'static str, &'static str>, - pub layer_names: Vec, pub active_layer: String, pub layer_infos: Vec, /// Live filter for the layer dropdown's search box (#343). Cleared on @@ -189,7 +188,6 @@ impl Ribbon { last_panel_tool: HashMap::default(), last_cmd: HashMap::default(), // Empty until a document is open — populated by sync_ribbon_layers. - layer_names: vec![], active_layer: String::new(), layer_infos: vec![], layer_filter: String::new(), @@ -285,7 +283,6 @@ impl Ribbon { pub fn set_layers(&mut self, infos: Vec, active: &str) { self.active_layer = active.to_string(); - self.layer_names = infos.iter().map(|l| l.name.clone()).collect(); self.layer_infos = infos; }