Merge pull request #1702 from MarcelP102/Pallet-placement

[UX] Pallets you can dock, group and float (including LAYERS panel)
This commit is contained in:
Hakan Seven committed 2026-10-12 00:27:53 +03:00
commit 7ca457a6cc
54 files changed
+4245 -1098

No files matched your search

+1211 -181
View File
File diff suppressed because it is too large. Load diff
+4
View File
@@ -72,7 +72,11 @@ pub static COPY: &[u8] = include_bytes!("../../assets/icons/ui/copy.svg");
pub static MENU: &[u8] = include_bytes!("../../assets/icons/ui/menu.svg");
pub static MOVE: &[u8] = include_bytes!("../../assets/icons/ui/move.svg");
pub static RESIZE: &[u8] = include_bytes!("../../assets/icons/ui/resize.svg");
/// `RESIZE` mirrored for a bottom-left corner grip.
pub static RESIZE_LEFT: &[u8] = include_bytes!("../../assets/icons/ui/resize_left.svg");
pub static PIN: &[u8] = include_bytes!("../../assets/icons/ui/pin.svg");
/// `PIN` tilted 30°: the pin while auto-hide is on.
pub static PIN_ACTIVE: &[u8] = include_bytes!("../../assets/icons/ui/pin_active.svg");
pub static SPLIT_V: &[u8] = include_bytes!("../../assets/icons/ui/split_v.svg");
pub static SPLIT_H: &[u8] = include_bytes!("../../assets/icons/ui/split_h.svg");
pub static GRID: &[u8] = include_bytes!("../../assets/icons/ui/grid.svg");
+3 -11
View File
@@ -658,7 +658,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(
@@ -740,16 +740,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,
)
}
+3 -6
View File
@@ -544,14 +544,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,
);
let header =
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() {
+8 -3
View File
@@ -800,11 +800,15 @@ 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> {
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(),
auto_collapse,
chrome,
);
let tabs = {
@@ -991,7 +995,8 @@ pub fn view(palette: &BlockPalette, width: f32, auto_collapse: bool) -> Element<
};
crate::ui::dock::frame(
column![title_bar, top, body, options_card(palette)].spacing(6),
title_bar,
column![top, body, options_card(palette)].spacing(6),
width,
)
}
+4 -7
View File
@@ -100,13 +100,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,
);
let title_bar =
crate::ui::dock::title_bar(PanelId::Browser, crate::t!("Browser").into_owned(), chrome);
let mut tree = column![].spacing(1);
@@ -151,5 +148,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)
}
+4 -7
View File
@@ -660,13 +660,10 @@ 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),
_ => {
@@ -685,7 +682,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>(
+53 -23
View File
@@ -337,20 +337,40 @@ impl LayerPanel {
.collect()
}
/// Render the layer panel as the full content of its own OS window.
pub fn view_window(
/// The Layer Manager as a dock pallet: its title bar, the toolbar across
/// the pallet (search box on the right), then the table, at least as wide
/// as the pallet and scrolling sideways when the pallet is narrower.
pub fn view_panel(
&self,
name_col_w: f32,
sizing: crate::ui::modal::ModalSizing,
width: f32,
chrome: crate::ui::dock::Chrome,
) -> Element<'_, Message> {
self.view_content(name_col_w, sizing)
use crate::ui::dock::PanelId;
let title_bar = crate::ui::dock::title_bar(
PanelId::Layers,
crate::tr!("modal", "layer-manager"),
chrome,
);
// Inside the pallet's 1 px border on either side.
let body = self.view_content(name_col_w, (width - 2.0).max(0.0));
container(column![title_bar, body])
.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()
}
fn view_content(
&self,
name_col_w: f32,
sizing: crate::ui::modal::ModalSizing,
) -> Element<'_, Message> {
/// Toolbar and table for a pallet `inner_w` wide.
fn view_content(&self, name_col_w: f32, inner_w: f32) -> Element<'_, Message> {
let has_sel = self.selected.is_some();
let sel_is_zero = self
.selected
@@ -377,7 +397,7 @@ impl LayerPanel {
Message::LayerSetCurrent,
can_set_current,
),
iced::widget::Space::new().width(sizing.width),
iced::widget::Space::new().width(Length::Fill),
// Search box: filters rows by name as the user types (#343).
text_input(t!("Search…").as_ref(), &self.filter)
.on_input(Message::LayerManagerFilterChanged)
@@ -393,7 +413,7 @@ impl LayerPanel {
background: Some(Background::Color(theme.palette().background.weak.color)),
..Default::default()
})
.width(sizing.width)
.width(Length::Fill)
.padding([4, 8]);
// ── Column header ─────────────────────────────────────────────────
@@ -473,7 +493,6 @@ impl LayerPanel {
),
]
.spacing(4)
.width(sizing.width)
.align_y(iced::Center);
for vp in &self.vp_cols {
@@ -499,7 +518,7 @@ impl LayerPanel {
}
})
.padding([4, 8])
.width(sizing.width);
.width(Length::Fill);
// ── Layer rows ────────────────────────────────────────────────────
let mut rows_col = column![].spacing(0);
@@ -536,17 +555,28 @@ impl LayerPanel {
let table = scrollable(rows_col)
.id(iced::advanced::widget::Id::new(LAYER_TABLE_SCROLL_ID))
.height(sizing.height.min(240.0));
.width(Length::Fill)
.height(Length::Fill);
// ── Full-window frame ─────────────────────────────────────────────
container(column![toolbar, col_header, table].spacing(0))
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(theme.palette().background.base.color)),
..Default::default()
})
.width(sizing.width)
.height(sizing.height)
.into()
// The header and table stretch to the wider of their columns and the
// pallet (the zero-height spacer), so the header's grey band and
// borders always run to the pallet's edge; a narrower pallet scrolls
// the table sideways while the toolbar stays put.
let grid = column![
col_header,
table,
iced::widget::Space::new()
.width(Length::Fixed(inner_w))
.height(0),
]
.height(Length::Fill);
let grid = scrollable(grid)
.direction(iced::widget::scrollable::Direction::Horizontal(
iced::widget::scrollable::Scrollbar::new(),
))
.width(Length::Fill)
.height(Length::Fill);
column![toolbar, grid].height(Length::Fill).into()
}
}
+3 -3
View File
@@ -277,12 +277,12 @@ 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,
chrome,
);
let query = state.search.trim().to_lowercase();
let matches = |label: &str| query.is_empty() || label.to_lowercase().contains(&query);
@@ -416,5 +416,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)
}
+4 -3
View File
@@ -1153,12 +1153,12 @@ fn heading(label: &str) -> Element<'static, Message> {
pub fn view<'a>(
state: &'a SheetSetManager,
width: f32,
auto_collapse: bool,
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,
chrome,
);
let mut picks: Vec<Pick> = state
.sets
@@ -1305,7 +1305,8 @@ pub fn view<'a>(
.size(10)
.style(muted_style);
crate::ui::dock::frame(
column![title_bar, combo, tabs, card, hint].spacing(6),
title_bar,
column![combo, tabs, card, hint].spacing(6),
width,
)
}
+2 -2
View File
@@ -590,7 +590,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> {
@@ -602,7 +602,7 @@ impl XrefManagerPanel {
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.