From 03763ff19767c21668256ddc4de4396b0336e7a9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 10:11:02 +0000 Subject: [PATCH] Dock: flush title bars and a vertical title bar for floating panels Every docked panel now shows its title bar flush along the top, like the Properties panel: dock::frame takes the title bar separately and pads only the content below it. A floating panel drops its horizontal title bar for a vertical one on the side facing the nearer workspace edge. Close and the auto-hide pin sit at the top; the panel's icon sits at the bottom with the title reading upward above it. The free part of the bar drags the panel. With auto-hide on, a floating panel shrinks to its title bar until hovered. Panel views take a dock::Chrome (auto-collapse plus floating) in place of the bare auto-collapse flag, so the shared title bar knows when to step aside. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013RyGiMXcsf9HTKfqDxaQGt --- src/app/view/mod.rs | 220 ++++++++++++++++++++++++++------- src/ui/dock.rs | 84 +++++++++++-- src/ui/node_graph.rs | 10 +- src/ui/properties.rs | 4 +- src/ui/window/block_palette.rs | 6 +- src/ui/window/browser.rs | 6 +- src/ui/window/count_palette.rs | 6 +- src/ui/window/pc_manager.rs | 6 +- src/ui/window/sheet_set.rs | 6 +- src/ui/window/xref_manager.rs | 4 +- 10 files changed, 271 insertions(+), 81 deletions(-) diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index 6ef6979fd..294311ca6 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -2848,6 +2848,7 @@ impl OpenCADStudio { .collect::>() .join(" · "), clockwise: side == crate::app::config::DockSide::Left, + from_start: false, }) .width(Fill) .height(Fill); @@ -2884,22 +2885,27 @@ impl OpenCADStudio { .into() } - /// The content of panel `id` at `width`. + /// The content of panel `id` at `width`. A `floating` panel leaves its + /// title to the floating frame's vertical title bar. fn panel_body<'a>( &'a self, id: crate::ui::dock::PanelId, width: f32, + floating: bool, tab: &'a DocumentTab, ) -> Element<'a, Message> { - let auto_collapse = self.dock.auto_collapse(id); + let chrome = crate::ui::dock::Chrome { + auto_collapse: self.dock.auto_collapse(id), + floating, + }; match id { - crate::ui::dock::PanelId::Properties => tab.properties.view(width, auto_collapse), + crate::ui::dock::PanelId::Properties => tab.properties.view(width, chrome), crate::ui::dock::PanelId::BlockPalette => { - crate::ui::window::block_palette::view(&self.block_palette, width, auto_collapse) + crate::ui::window::block_palette::view(&self.block_palette, width, chrome) } crate::ui::dock::PanelId::ExternalReferences => self.xref_manager.view( width, - auto_collapse, + chrome, tab.xref_missing, &tab.scene.document, ), @@ -2907,9 +2913,9 @@ impl OpenCADStudio { &tab.scene.document, tab.sketch_session.as_ref().map(|session| session.name.as_str()), width, - auto_collapse, + chrome, ), - crate::ui::dock::PanelId::NodeGraph => tab.graph.panel(width, auto_collapse), + crate::ui::dock::PanelId::NodeGraph => tab.graph.panel(width, chrome), crate::ui::dock::PanelId::Count => crate::ui::window::count_palette::view( &self.count_palette, tab.count.as_ref(), @@ -2917,17 +2923,17 @@ impl OpenCADStudio { tab.id, tab.scene.geometry_epoch, width, - auto_collapse, + chrome, ), crate::ui::dock::PanelId::PointCloudManager => crate::ui::window::pc_manager::view( &self.pc_manager, &tab.scene.document, &tab.scene.selected_handles_in_order(), width, - auto_collapse, + chrome, ), crate::ui::dock::PanelId::SheetSetManager => { - crate::ui::window::sheet_set::view(&self.sheet_set, width, auto_collapse) + crate::ui::window::sheet_set::view(&self.sheet_set, width, chrome) } } } @@ -2946,7 +2952,7 @@ impl OpenCADStudio { ) -> Element<'a, Message> { let tabs = self.dock_group_visible_tabs(side, gi); let id = self.dock_group_shown(side, gi).expect("visible group"); - let body = self.panel_body(id, width, tab); + let body = self.panel_body(id, width, false, tab); let panel: Element<'_, Message> = if tabs.len() > 1 { column![dock_tab_strip(&tabs, id, width), body] .height(Fill) @@ -2961,7 +2967,10 @@ impl OpenCADStudio { } } - /// A floating panel at its saved place, with a corner grip to resize it. + /// A floating panel at its saved place: a vertical title bar on the side + /// facing the nearer workspace edge, the panel beside it, and a corner + /// grip to resize it. With its pin on, the panel hides down to the title + /// bar until hovered. fn floating_panel<'a>( &'a self, f: crate::ui::dock::FloatPanel, @@ -2969,45 +2978,62 @@ impl OpenCADStudio { ) -> Element<'a, Message> { use crate::ui::dock::DockMsg; let (ww, wh) = self.dock_workspace_size(); - let grip = mouse_area( - container(crate::ui::icons::themed_secondary(crate::ui::icons::RESIZE, 12.0)) - .width(Length::Fixed(14.0)) - .height(Length::Fixed(14.0)) - .center_x(Length::Fixed(14.0)) - .center_y(Length::Fixed(14.0)), - ) - .on_press(Message::Dock(DockMsg::FloatResizeGrab(f.id))) - .interaction(iced::mouse::Interaction::ResizingDiagonallyDown); - let body = stack![ - self.panel_body(f.id, f.w, tab), - container(grip) - .width(Fill) - .height(Fill) - .align_x(iced::alignment::Horizontal::Right) - .align_y(iced::alignment::Vertical::Bottom), - ] - .width(Length::Fixed(f.w)) - .height(Length::Fixed(f.h)); - let framed = container(body).style(|theme: &Theme| container::Style { - border: Border { - color: theme.palette().background.strong.color, - width: 1.0, - radius: 0.0.into(), - }, - shadow: iced::Shadow { - color: Color::from_rgba(0.0, 0.0, 0.0, 0.35), - offset: iced::Vector::new(0.0, 4.0), - blur_radius: 14.0, - }, - ..Default::default() - }); + let bar_left = f.x + (f.w + DOCK_FLOAT_BAR_W) * 0.5 < ww * 0.5; + let hidden = self.dock.auto_collapse(f.id) && self.dock_expanded != Some(f.id); + let bar = floating_title_bar(f.id, self.dock.auto_collapse(f.id), bar_left); + let content: Element<'_, Message> = if hidden { + bar + } else { + let grip = mouse_area( + container(crate::ui::icons::themed_secondary(crate::ui::icons::RESIZE, 12.0)) + .center_x(Length::Fixed(14.0)) + .center_y(Length::Fixed(14.0)), + ) + .on_press(Message::Dock(DockMsg::FloatResizeGrab(f.id))) + .interaction(iced::mouse::Interaction::ResizingDiagonallyDown); + let body = stack![ + self.panel_body(f.id, f.w, true, tab), + container(grip) + .width(Fill) + .height(Fill) + .align_x(iced::alignment::Horizontal::Right) + .align_y(iced::alignment::Vertical::Bottom), + ] + .width(Length::Fixed(f.w)) + .height(Fill); + if bar_left { + row![bar, body].into() + } else { + row![body, bar].into() + } + }; + let total_w = if hidden { DOCK_FLOAT_BAR_W } else { f.w + DOCK_FLOAT_BAR_W }; + let framed = container(content) + .height(Length::Fixed(f.h)) + .style(|theme: &Theme| container::Style { + border: Border { + color: theme.palette().background.strong.color, + width: 1.0, + radius: 0.0.into(), + }, + shadow: iced::Shadow { + color: Color::from_rgba(0.0, 0.0, 0.0, 0.35), + offset: iced::Vector::new(0.0, 4.0), + blur_radius: 14.0, + }, + ..Default::default() + }); // The idle cursor marks the panel as opaque to the pointer, so clicks // on its empty areas don't fall through to the drawing below. let panel = mouse_area(framed) .on_press(Message::Dock(DockMsg::FloatRaise(f.id))) + .on_enter(Message::Dock(DockMsg::Hover(f.id))) + .on_exit(Message::Dock(DockMsg::HoverExit)) .interaction(iced::mouse::Interaction::Idle); + // A hidden panel keeps its title bar where it sits when shown. + let x = if hidden && !bar_left { f.x + f.w } else { f.x }; // Keep the panel on screen when the window shrank since it was placed. - let x = f.x.min(ww - f.w).max(0.0); + let x = x.min(ww - total_w).max(0.0); let y = f.y.min(wh - f.h).max(0.0); place_at(panel.into(), x, y) } @@ -3081,7 +3107,11 @@ impl OpenCADStudio { } DropTarget::Float { x, y } => { let (w, h) = self.dock_float_size(id); - layers.push(place_at(dock_ghost(&[id], id, w, h), x, y)); + layers.push(place_at( + dock_ghost(&[id], id, w + DOCK_FLOAT_BAR_W, h), + x, + y, + )); } } iced::widget::Stack::with_children(layers) @@ -3467,6 +3497,9 @@ fn pane_mouse_area<'a>(idx: usize) -> Element<'a, Message> { struct VBarLabel { text: String, clockwise: bool, + /// Start the text at the reading-start end of the bar instead of + /// centring it. + from_start: bool, } impl canvas::Program for VBarLabel { @@ -3488,12 +3521,22 @@ impl canvas::Program for VBarLabel { } else { -std::f32::consts::FRAC_PI_2 })); + // After the rotation the text runs along local +x, so the + // reading start sits at -height/2. + let (position, align_x) = if self.from_start { + ( + iced::Point::new(-bounds.height / 2.0, 0.0), + iced::advanced::text::Alignment::Left, + ) + } else { + (iced::Point::ORIGIN, iced::advanced::text::Alignment::Center) + }; frame.fill_text(canvas::Text { content: self.text.clone(), - position: iced::Point::ORIGIN, + position, color: theme.palette().background.base.text.scale_alpha(0.72), size: iced::Pixels(13.0), - align_x: iced::advanced::text::Alignment::Center, + align_x, align_y: iced::alignment::Vertical::Center, shaping: iced::advanced::text::Shaping::Advanced, ..Default::default() @@ -3507,6 +3550,87 @@ impl canvas::Program for VBarLabel { const DOCK_RAIL_W: f32 = 28.0; /// Width of the grabbable divider between a docked panel and the viewport. const DOCK_DIVIDER_W: f32 = 5.0; +/// Width of a floating panel's vertical title bar. +const DOCK_FLOAT_BAR_W: f32 = 28.0; + +/// A floating panel's vertical title bar: close then the hide (auto-collapse) +/// pin at the top, the panel's icon at the bottom with its title reading +/// upward above it. Pressing the bar's free area drags the panel. +fn floating_title_bar( + id: crate::ui::dock::PanelId, + auto_collapse: bool, + on_left: bool, +) -> Element<'static, Message> { + use crate::ui::dock::DockMsg; + let close = button(crate::ui::icons::themed_secondary(crate::ui::icons::CLOSE, 12.0)) + .on_press(Message::Dock(DockMsg::Close(id))) + .style(button::subtle) + .padding([3, 5]); + let close = iced::widget::tooltip( + close, + text(t!("Close")).size(10), + if on_left { + iced::widget::tooltip::Position::Left + } else { + iced::widget::tooltip::Position::Right + }, + ) + .gap(4); + let pin_icon = if auto_collapse { + crate::ui::icons::themed_primary_weak_text(crate::ui::icons::PIN, 12.0) + } else { + crate::ui::icons::themed_secondary(crate::ui::icons::PIN, 12.0) + }; + let pin = button(pin_icon) + .on_press(Message::Dock(DockMsg::AutoCollapseToggle(id))) + .style(move |theme: &Theme, status| { + let mut style = button::subtle(theme, status); + if auto_collapse { + let palette = theme.palette(); + style.background = Some(Background::Color(palette.primary.weak.color)); + style.text_color = palette.primary.weak.text; + style.border.color = palette.primary.base.color; + style.border.width = 1.0; + } + style + }) + .padding([3, 5]); + let pin = iced::widget::tooltip( + pin, + text(t!("Auto-hide")).size(10), + if on_left { + iced::widget::tooltip::Position::Left + } else { + iced::widget::tooltip::Position::Right + }, + ) + .gap(4); + let title = canvas(VBarLabel { + text: id.title().to_string(), + clockwise: false, + from_start: true, + }) + .width(Fill) + .height(Fill); + let bar = column![ + close, + pin, + container(title).width(Fill).height(Fill).padding([6, 0]), + container(crate::ui::icons::themed(id.icon(), 16.0)).center_x(Fill), + ] + .spacing(2) + .padding([4, 0]) + .align_x(iced::Center) + .width(Length::Fixed(DOCK_FLOAT_BAR_W)) + .height(Fill); + mouse_area(container(bar).style(|theme: &Theme| container::Style { + background: Some(Background::Color(theme.palette().background.weak.color)), + ..Default::default() + })) + .on_press(Message::Dock(DockMsg::DockGrab(id))) + .interaction(iced::mouse::Interaction::Grab) + .into() +} /// Grabbable separator for a docked panel managed by the general dock. Same /// visual as the previous per-panel divider but emits generic dock messages. diff --git a/src/ui/dock.rs b/src/ui/dock.rs index c564b804c..d3cb445a8 100644 --- a/src/ui/dock.rs +++ b/src/ui/dock.rs @@ -80,6 +80,20 @@ impl PanelId { } } + /// The panel's icon: the one on the ribbon tool that opens it. + pub fn icon(self) -> &'static [u8] { + match self { + PanelId::Properties => include_bytes!("../../assets/icons/properties.svg"), + PanelId::BlockPalette => include_bytes!("../../assets/icons/blocks/insert.svg"), + PanelId::ExternalReferences => crate::ui::icons::FOLDER_OPEN, + PanelId::Browser => include_bytes!("../../assets/icons/content_browser.svg"), + PanelId::NodeGraph => crate::ui::icons::NODE_GRAPH, + PanelId::PointCloudManager => include_bytes!("../../assets/icons/pc_attach.svg"), + PanelId::Count => include_bytes!("../../assets/icons/data_extract.svg"), + PanelId::SheetSetManager => include_bytes!("../../assets/icons/sheetset.svg"), + } + } + /// Default dock width for a freshly-created panel instance. fn default_width(self) -> f32 { match self { @@ -559,9 +573,25 @@ use crate::app::Message; use iced::widget::{button, container, mouse_area, row, text, tooltip, Space}; use iced::{Background, Border, Color, Element, Length, Theme}; +/// How a panel is framed where it is shown, passed to its view so the shared +/// chrome can adapt. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct Chrome { + /// The panel auto-collapses (its pin is on). + pub auto_collapse: bool, + /// The panel floats; its title lives in the vertical side bar drawn by + /// the floating frame instead of the horizontal title bar. + pub floating: bool, +} + /// The row every docked panel starts with: its title, the auto-collapse pin -/// and close. Pressing the row starts re-docking the panel. -pub fn title_bar<'a>(id: PanelId, title: String, auto_collapse: bool) -> Element<'a, Message> { +/// and close. Pressing the row starts re-docking the panel. A floating panel +/// gets nothing here: its frame draws a vertical title bar beside it. +pub fn title_bar<'a>(id: PanelId, title: String, chrome: Chrome) -> Element<'a, Message> { + if chrome.floating { + return Space::new().width(0).height(0).into(); + } + let auto_collapse = chrome.auto_collapse; let pin_icon = if auto_collapse { crate::ui::icons::themed_primary_weak_text(crate::ui::icons::PIN, 12.0) } else { @@ -639,11 +669,18 @@ pub fn tool_button<'a>( .into() } -/// A docked panel's body: fixed width, full height, padded, on the base -/// background with a neutral edge. -pub fn frame<'a>(content: impl Into>, width: f32) -> Element<'a, Message> { - container(content) - .padding(6) +/// A docked panel's body: its title bar flush along the top (as on the +/// Properties panel), then the padded content; fixed width, full height, on +/// the base background with a neutral edge. +pub fn frame<'a>( + title_bar: Element<'a, Message>, + content: impl Into>, + width: f32, +) -> Element<'a, Message> { + container(iced::widget::column![ + title_bar, + container(content).padding(6).height(Length::Fill) + ]) .width(Length::Fixed(width)) .height(Length::Fill) .style(|theme: &Theme| container::Style { @@ -910,6 +947,39 @@ mod tests { assert_eq!(state.right.len(), 1); } + #[test] + fn every_panel_has_its_own_icon() { + let ids = [ + PanelId::Properties, + PanelId::BlockPalette, + PanelId::ExternalReferences, + PanelId::Browser, + PanelId::NodeGraph, + PanelId::PointCloudManager, + PanelId::Count, + PanelId::SheetSetManager, + ]; + for (i, a) in ids.iter().enumerate() { + assert!(a.icon().starts_with(b" = title_bar(PanelId::Count, "Count".into(), floating); + assert_eq!( + bar.as_widget().size(), + iced::Size::new(Length::Fixed(0.0), Length::Fixed(0.0)) + ); + } + #[test] fn slot_zone_splits_quarters() { assert_eq!(slot_zone(10.0, 0.0, 400.0), SlotZone::Above); diff --git a/src/ui/node_graph.rs b/src/ui/node_graph.rs index 7b01a9750..3b32fcf6e 100644 --- a/src/ui/node_graph.rs +++ b/src/ui/node_graph.rs @@ -584,7 +584,7 @@ impl Graph { /// The node library as a docked panel: title bar, a search box with the /// graph's New / Open / Save beside it, then the categories. Entries are /// clicked or dragged onto the canvas. - pub fn panel(&self, width: f32, auto_collapse: bool) -> Element<'_, Message> { + pub fn panel(&self, width: f32, chrome: crate::ui::dock::Chrome) -> Element<'_, Message> { use crate::ui::dock::{self, PanelId}; let tool = |icon: &'static [u8], tip: &str, message: GraphMsg| { dock::tool_button( @@ -663,12 +663,8 @@ impl Graph { .height(Length::Fill); dock::frame( - column![ - dock::title_bar(PanelId::NodeGraph, t!("Node Graph").into_owned(), auto_collapse), - toolbar, - body - ] - .spacing(6), + dock::title_bar(PanelId::NodeGraph, t!("Node Graph").into_owned(), chrome), + column![toolbar, body].spacing(6), width, ) } diff --git a/src/ui/properties.rs b/src/ui/properties.rs index fad5e2068..df65816b0 100644 --- a/src/ui/properties.rs +++ b/src/ui/properties.rs @@ -542,11 +542,11 @@ impl PropertiesPanel { .unwrap_or_default() } - pub fn view(&self, width: f32, auto_collapse: bool) -> Element<'_, Message> { + pub fn view(&self, width: f32, chrome: crate::ui::dock::Chrome) -> Element<'_, Message> { use crate::ui::dock::PanelId; // ── Header ────────────────────────────────────────────────────────── let header = - crate::ui::dock::title_bar(PanelId::Properties, t!("Properties").into_owned(), auto_collapse); + crate::ui::dock::title_bar(PanelId::Properties, t!("Properties").into_owned(), chrome); // ── Title bar (entity type / "No Selection") ───────────────────── let title_content: Element<'_, Message> = if self.selection_groups.is_empty() { diff --git a/src/ui/window/block_palette.rs b/src/ui/window/block_palette.rs index a84fda8e9..e6709c4f0 100644 --- a/src/ui/window/block_palette.rs +++ b/src/ui/window/block_palette.rs @@ -659,8 +659,8 @@ fn options_card<'a>(palette: &'a BlockPalette) -> Element<'a, Message> { } /// Build the docked panel element from the palette state. -pub fn view(palette: &BlockPalette, width: f32, auto_collapse: bool) -> Element<'_, Message> { - let title_bar = crate::ui::dock::title_bar(PanelId::BlockPalette, crate::t!("Blocks").into_owned(), auto_collapse); +pub fn view(palette: &BlockPalette, width: f32, chrome: crate::ui::dock::Chrome) -> Element<'_, Message> { + let title_bar = crate::ui::dock::title_bar(PanelId::BlockPalette, crate::t!("Blocks").into_owned(), chrome); let tabs = { let buttons = [Tab::Current, Tab::Recent, Tab::Favorites, Tab::Libraries].map(|t| { @@ -801,7 +801,7 @@ pub fn view(palette: &BlockPalette, width: f32, auto_collapse: bool) -> Element< .into() }; - crate::ui::dock::frame(column![title_bar, top, body, options_card(palette)].spacing(6), width) + crate::ui::dock::frame(title_bar, column![top, body, options_card(palette)].spacing(6), width) } /// Width of the ribbon's block gallery. diff --git a/src/ui/window/browser.rs b/src/ui/window/browser.rs index 217773a3a..4676f2e43 100644 --- a/src/ui/window/browser.rs +++ b/src/ui/window/browser.rs @@ -97,10 +97,10 @@ pub fn view<'a>( document: &'a CadDocument, open_sketch: Option<&'a str>, width: f32, - auto_collapse: bool, + chrome: crate::ui::dock::Chrome, ) -> Element<'a, Message> { let title_bar = - crate::ui::dock::title_bar(PanelId::Browser, crate::t!("Browser").into_owned(), auto_collapse); + crate::ui::dock::title_bar(PanelId::Browser, crate::t!("Browser").into_owned(), chrome); let mut tree = column![].spacing(1); @@ -143,5 +143,5 @@ pub fn view<'a>( .width(Fill) .height(Fill); - crate::ui::dock::frame(column![title_bar, body].spacing(6), width) + crate::ui::dock::frame(title_bar, column![body].spacing(6), width) } diff --git a/src/ui/window/count_palette.rs b/src/ui/window/count_palette.rs index 8f8e08d70..2b44455bb 100644 --- a/src/ui/window/count_palette.rs +++ b/src/ui/window/count_palette.rs @@ -530,9 +530,9 @@ pub fn view<'a>( doc_id: u64, epoch: u64, width: f32, - auto_collapse: bool, + chrome: crate::ui::dock::Chrome, ) -> Element<'a, Message> { - let title_bar = crate::ui::dock::title_bar(PanelId::Count, crate::t!("Count").into_owned(), auto_collapse); + let title_bar = crate::ui::dock::title_bar(PanelId::Count, crate::t!("Count").into_owned(), chrome); let body = match mode { Some(mode) if mode.target.is_some() => mode_view(palette, mode, doc), _ => { @@ -550,7 +550,7 @@ pub fn view<'a>( list_view(palette, &instances) } }; - crate::ui::dock::frame(column![title_bar, body].spacing(6), width) + crate::ui::dock::frame(title_bar, column![body].spacing(6), width) } fn bar_button<'a>(icon: Element<'a, Message>, tip: String, m: Option) -> Element<'a, Message> { diff --git a/src/ui/window/pc_manager.rs b/src/ui/window/pc_manager.rs index 4d7e4ae74..11d70ddf6 100644 --- a/src/ui/window/pc_manager.rs +++ b/src/ui/window/pc_manager.rs @@ -235,10 +235,10 @@ pub fn view<'a>( document: &'a CadDocument, selected: &[Handle], width: f32, - auto_collapse: bool, + chrome: crate::ui::dock::Chrome, ) -> Element<'a, Message> { let title_bar = - crate::ui::dock::title_bar(PanelId::PointCloudManager, crate::t!("Point Cloud Manager").into_owned(), auto_collapse); + crate::ui::dock::title_bar(PanelId::PointCloudManager, crate::t!("Point Cloud Manager").into_owned(), chrome); let query = state.search.trim().to_lowercase(); let matches = |label: &str| query.is_empty() || label.to_lowercase().contains(&query); // Searching opens every node so the matches show. @@ -354,5 +354,5 @@ pub fn view<'a>( .padding([4, 8]) .size(12); - crate::ui::dock::frame(column![title_bar, card, search].spacing(6), width) + crate::ui::dock::frame(title_bar, column![card, search].spacing(6), width) } diff --git a/src/ui/window/sheet_set.rs b/src/ui/window/sheet_set.rs index 1bf1c0d2a..be0d3682d 100644 --- a/src/ui/window/sheet_set.rs +++ b/src/ui/window/sheet_set.rs @@ -936,9 +936,9 @@ fn heading(label: &str) -> Element<'static, Message> { .into() } -pub fn view<'a>(state: &'a SheetSetManager, width: f32, auto_collapse: bool) -> Element<'a, Message> { +pub fn view<'a>(state: &'a SheetSetManager, width: f32, chrome: crate::ui::dock::Chrome) -> Element<'a, Message> { let title_bar = - crate::ui::dock::title_bar(PanelId::SheetSetManager, t!("Sheet Set Manager").into_owned(), auto_collapse); + crate::ui::dock::title_bar(PanelId::SheetSetManager, t!("Sheet Set Manager").into_owned(), chrome); let mut picks: Vec = state.sets.iter().enumerate().map(|(i, db)| Pick(Some(i), db.name().to_string())).collect(); picks.push(Pick(None, t!("Open...").into_owned())); @@ -1040,7 +1040,7 @@ pub fn view<'a>(state: &'a SheetSetManager, width: f32, auto_collapse: bool) -> let hint = text(t!("Double-click opens a sheet. Right-click for more commands.").into_owned()) .size(10) .style(muted_style); - crate::ui::dock::frame(column![title_bar, combo, tabs, card, hint].spacing(6), width) + crate::ui::dock::frame(title_bar, column![combo, tabs, card, hint].spacing(6), width) } // ── dialogs ────────────────────────────────────────────────────────────────── diff --git a/src/ui/window/xref_manager.rs b/src/ui/window/xref_manager.rs index a79f5328c..df5436abd 100644 --- a/src/ui/window/xref_manager.rs +++ b/src/ui/window/xref_manager.rs @@ -542,7 +542,7 @@ impl XrefManagerPanel { pub fn view<'a>( &'a self, width: f32, - auto_collapse: bool, + chrome: crate::ui::dock::Chrome, missing: usize, doc: &'a CadDocument, ) -> Element<'a, Message> { @@ -550,7 +550,7 @@ impl XrefManagerPanel { let title_bar = crate::ui::dock::title_bar( PanelId::ExternalReferences, format!("{} ({})", crate::t!("External References").as_ref(), self.entries.len()), - auto_collapse, + chrome, ); // Table content width: column widths plus gutters, stretched to the // dock when wider so rows fill the panel; narrower docks sidescroll.