forked from 1b-agent/opencadstudio-upstream
Merge pull request #1702 from MarcelP102/Pallet-placement
[UX] Pallets you can dock, group and float (including LAYERS panel)
This commit is contained in:
commit
7ca457a6cc
54 files changed
+4245
-1098
No files matched your search
+1211
-181
File diff suppressed because it is too large.
Load diff
@@ -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
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user