forked from 1b-agent/opencadstudio-upstream
feat(graph): node library as a docked panel; shared dock chrome
The node library leaves the overlay and becomes a dock panel (PanelId:: NodeGraph), docked on the right the first time the graph opens and movable like any other panel; closing it closes the graph. It follows the block palette's layout: title bar, then a search box with New / Open / Save beside it, then the categories, of which only Objects starts open. Entries are clicked (placed at the canvas top-left) or dragged onto the canvas. The title bar (title, auto-collapse pin, close, drag to re-dock), the toolbar icon button and the panel frame were copied across Properties, Block Palette, External References and Browser; they are now dock::title_bar, dock::tool_button and dock::frame, used by all of them. Block Palette's two tool buttons gain tooltips, and External References' Auto / Close tooltips are now translated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
857f2f2e58
commit
3013c1b570
9 files changed
+245
-314
No files matched your search
+110
@@ -44,6 +44,8 @@ pub enum PanelId {
|
||||
ExternalReferences,
|
||||
/// Outline of the drawing's origin planes, open sketch and solid bodies.
|
||||
Browser,
|
||||
/// Node library of the node graph overlay.
|
||||
NodeGraph,
|
||||
}
|
||||
|
||||
impl PanelId {
|
||||
@@ -54,6 +56,7 @@ impl PanelId {
|
||||
PanelId::BlockPalette => "Block Palette",
|
||||
PanelId::ExternalReferences => "External References",
|
||||
PanelId::Browser => "Browser",
|
||||
PanelId::NodeGraph => "Node Graph",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +67,7 @@ impl PanelId {
|
||||
PanelId::BlockPalette => 260.0,
|
||||
PanelId::ExternalReferences => 460.0,
|
||||
PanelId::Browser => 230.0,
|
||||
PanelId::NodeGraph => 220.0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +156,7 @@ impl DockState {
|
||||
PanelId::BlockPalette,
|
||||
PanelId::ExternalReferences,
|
||||
PanelId::Browser,
|
||||
PanelId::NodeGraph,
|
||||
] {
|
||||
self.panels.entry(id).or_insert_with(|| DockPanel::for_id(id));
|
||||
}
|
||||
@@ -240,6 +245,111 @@ pub const DOCK_MIN_W: f32 = 200.0;
|
||||
/// Largest docked width a panel may be dragged or sized to.
|
||||
pub const DOCK_MAX_W: f32 = 600.0;
|
||||
|
||||
// ── Shared panel chrome ─────────────────────────────────────────────────
|
||||
|
||||
use crate::app::Message;
|
||||
use iced::widget::{button, container, mouse_area, row, text, tooltip, Space};
|
||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||
|
||||
/// 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> {
|
||||
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 = tooltip(pin, text(crate::t!("Auto")).size(10), tooltip::Position::Bottom).gap(4);
|
||||
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 = tooltip(close, text(crate::t!("Close")).size(10), tooltip::Position::Bottom).gap(4);
|
||||
mouse_area(
|
||||
container(
|
||||
row![text(title).size(12), Space::new().width(Length::Fill), pin, close]
|
||||
.spacing(3)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.weak.color)),
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.padding([3, 6]),
|
||||
)
|
||||
.on_press(Message::Dock(DockMsg::DockGrab(id)))
|
||||
.interaction(iced::mouse::Interaction::Grab)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Side length of a panel toolbar button's icon.
|
||||
pub const TOOL_H: f32 = 22.0;
|
||||
|
||||
/// A panel toolbar icon button, placed on the row under the title bar.
|
||||
pub fn tool_button<'a>(
|
||||
icon: Element<'a, Message>,
|
||||
tip: String,
|
||||
message: Message,
|
||||
) -> Element<'a, Message> {
|
||||
let button = button(icon)
|
||||
.on_press(message)
|
||||
.width(Length::Fixed(TOOL_H + 8.0))
|
||||
.height(Length::Fixed(TOOL_H + 8.0))
|
||||
.style(|theme: &Theme, status| button::Style {
|
||||
background: Some(Background::Color(match status {
|
||||
button::Status::Hovered | button::Status::Pressed => {
|
||||
theme.palette().background.strong.color
|
||||
}
|
||||
_ => Color::TRANSPARENT,
|
||||
})),
|
||||
border: Border {
|
||||
radius: 3.0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
text_color: crate::ui::window::block_palette::block_icon_button_text_color(
|
||||
theme, status,
|
||||
),
|
||||
..Default::default()
|
||||
});
|
||||
tooltip(button, text(tip).size(10), tooltip::Position::Bottom)
|
||||
.gap(4)
|
||||
.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<Element<'a, Message>>, width: f32) -> Element<'a, Message> {
|
||||
container(content)
|
||||
.padding(6)
|
||||
.width(Length::Fixed(width))
|
||||
.height(Length::Fill)
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.base.color)),
|
||||
border: Border {
|
||||
color: theme.palette().background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 0.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in new issue
Block a user