fix(ui): make modal layouts intrinsic

Pin Iced 0.15 so dialogs can measure content before distributing the resolved frame across child panels. Keep the web plugin notice compact and non-resizable.\n\nCloses #582
This commit is contained in:
Hakan Seven committed 2026-07-30 14:47:28 +03:00
1 parent 284d6ef517
commit 3ab8dc3ca5
59 files changed
+2197 -1652

No files matched your search

+85 -47
View File
@@ -17,7 +17,7 @@ use crate::app::Message;
use acadrust::entities::{HorizontalAlignment, VerticalAlignment};
use acadrust::types::{Color as AcadColor, LineWeight};
use iced::widget::{
button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space,
button, checkbox, column, container, row, scrollable, text, text_input, Space,
};
use iced::{Background, Border, Element, Length, Theme};
@@ -135,12 +135,12 @@ pub fn color_from_label(label: &str) -> Option<AcadColor> {
fn muted_style(theme: &Theme) -> iced::widget::text::Style {
iced::widget::text::Style {
color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)),
color: Some(theme.palette().background.base.text.scale_alpha(0.68)),
}
}
fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style {
let palette = theme.extended_palette();
let palette = theme.palette();
let border = match status {
text_input::Status::Focused { .. } => palette.primary.base.color,
_ => palette.background.neutral.color,
@@ -156,13 +156,13 @@ fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style {
}
/// Horizontal 1px divider in the shared border colour (matches style windows).
fn hdivider<'a>() -> Element<'a, Message> {
container(Space::new().width(Length::Fill).height(1))
.width(Length::Fill)
fn hdivider<'a>(width: Length) -> Element<'a, Message> {
container(Space::new().width(width).height(1))
.width(width)
.height(1)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.extended_palette().background.neutral.color
theme.palette().background.neutral.color
)),
..Default::default()
})
@@ -170,13 +170,18 @@ fn hdivider<'a>() -> Element<'a, Message> {
}
/// One `label : widget` row with a fixed-width label column.
fn field_row<'a>(label: &'a str, widget: Element<'a, Message>) -> Element<'a, Message> {
fn field_row<'a>(
label: &'a str,
widget: Element<'a, Message>,
width: Length,
) -> Element<'a, Message> {
row![
container(text(label).size(12).style(muted_style)).width(LABEL_W),
widget,
]
.spacing(8)
.align_y(iced::Center)
.width(width)
.into()
}
@@ -185,6 +190,7 @@ fn edit_field<'a>(
label: &'a str,
value: &'a str,
on_input: impl Fn(String) -> Message + 'a,
width: Length,
) -> Element<'a, Message> {
let ti = text_input("", value)
.on_input(on_input)
@@ -192,8 +198,8 @@ fn edit_field<'a>(
.style(field_style)
.size(13)
.padding([3, 6])
.width(Length::Fill);
field_row(label, ti.into())
.width(width);
field_row(label, ti.into(), width)
}
/// A labelled pick_list of owned string options.
@@ -202,12 +208,13 @@ fn pick_field<'a>(
options: Vec<String>,
selected: Option<String>,
on_select: impl Fn(String) -> Message + 'a,
width: Length,
) -> Element<'a, Message> {
let pl = pick_list(options, selected, on_select)
let pl = crate::ui::pick_list(options, selected, on_select)
.text_size(13)
.padding([3, 6])
.width(Length::Fill);
field_row(label, pl.into())
.width(width);
field_row(label, pl.into(), width)
}
fn tab_button<'a>(label: &'a str, this: AttrTab, active: AttrTab) -> Element<'a, Message> {
@@ -216,7 +223,7 @@ fn tab_button<'a>(label: &'a str, this: AttrTab, active: AttrTab) -> Element<'a,
.padding([4, 12])
.on_press(Message::AttrEditorTab(this))
.style(move |theme: &Theme, status| {
let palette = theme.extended_palette();
let palette = theme.palette();
let pair = match (is_active, status) {
(true, _) => palette.primary.strong,
(false, button::Status::Hovered | button::Status::Pressed) => {
@@ -250,7 +257,10 @@ pub fn view_window<'a>(
layers: Vec<String>,
linetypes: Vec<String>,
styles: Vec<String>,
sizing: crate::ui::modal::ModalSizing,
) -> Element<'a, Message> {
let width = sizing.width;
let height = sizing.height;
// ── Top toolbar: block name on the left, Apply on the right ───────────
// Mirrors the style-manager windows (actions left, primary action right).
let apply = button(text("Apply").size(11))
@@ -260,18 +270,18 @@ pub fn view_window<'a>(
let toolbar = container(
row![
text(format!("Block: {block}")).size(12).style(muted_style),
Space::new().width(Length::Fill),
Space::new().width(width),
apply,
]
.align_y(iced::Center),
)
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.extended_palette().background.weak.color
theme.palette().background.weak.color
)),
..Default::default()
})
.width(Length::Fill)
.width(width)
.padding([5, 8]);
let tabs = row![
@@ -285,36 +295,49 @@ pub fn view_window<'a>(
text("This block has no attributes.").size(13).style(muted_style).into()
} else {
match tab {
AttrTab::Attribute => attribute_tab(rows, selected),
AttrTab::TextOptions => text_options_tab(&rows[selected.min(rows.len() - 1)], styles),
AttrTab::Attribute => attribute_tab(rows, selected, width, height),
AttrTab::TextOptions => {
text_options_tab(&rows[selected.min(rows.len() - 1)], styles, width, height)
}
AttrTab::Properties => {
properties_tab(&rows[selected.min(rows.len() - 1)], layers, linetypes)
properties_tab(
&rows[selected.min(rows.len() - 1)],
layers,
linetypes,
width,
height,
)
}
}
};
let content = container(column![tabs, body].spacing(8))
.width(Length::Fill)
.height(Length::Fill)
.width(width)
.height(height)
.padding(12);
container(column![toolbar, hdivider(), content])
container(column![toolbar, hdivider(width), content])
.style(|theme: &Theme| container::Style {
background: Some(Background::Color(
theme.extended_palette().background.base.color
theme.palette().background.base.color
)),
..Default::default()
})
.width(Length::Fill)
.height(Length::Fill)
.width(width)
.height(height)
.into()
}
/// Attribute tab: tag / prompt / value list with row-select, plus a value box.
fn attribute_tab<'a>(rows: &'a [AttrRow], selected: usize) -> Element<'a, Message> {
fn attribute_tab<'a>(
rows: &'a [AttrRow],
selected: usize,
width: Length,
height: Length,
) -> Element<'a, Message> {
let head = row![
container(text("Tag").size(11).style(muted_style)).width(130),
container(text("Prompt").size(11).style(muted_style)).width(Length::Fill),
container(text("Prompt").size(11).style(muted_style)).width(width),
container(text("Value").size(11).style(muted_style)).width(140),
]
.spacing(6);
@@ -324,16 +347,16 @@ fn attribute_tab<'a>(rows: &'a [AttrRow], selected: usize) -> Element<'a, Messag
let is_sel = idx == selected;
let line = row![
container(text(r.tag.as_str()).size(12)).width(130),
container(text(r.prompt.as_str()).size(12).style(muted_style)).width(Length::Fill),
container(text(r.prompt.as_str()).size(12).style(muted_style)).width(width),
container(text(r.value.as_str()).size(12)).width(140),
]
.spacing(6);
let btn = button(line)
.on_press(Message::AttrEditorSelect(idx))
.padding([3, 4])
.width(Length::Fill)
.width(width)
.style(move |theme: &Theme, status| {
let palette = theme.extended_palette();
let palette = theme.palette();
let hovered = matches!(status, button::Status::Hovered);
let pair = if is_sel {
palette.primary.strong
@@ -359,20 +382,27 @@ fn attribute_tab<'a>(rows: &'a [AttrRow], selected: usize) -> Element<'a, Messag
.style(field_style)
.size(13)
.padding([4, 6])
.width(Length::Fill);
.width(width);
column![
head,
scrollable(list).height(Length::Fill),
scrollable(list).width(width).height(height),
Space::new().height(8),
field_row("Value:", value_box.into()),
field_row("Value:", value_box.into(), width),
]
.spacing(6)
.width(width)
.height(height)
.into()
}
/// Text Options tab: the selected attribute's text formatting.
fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec<String>) -> Element<'a, Message> {
fn text_options_tab<'a>(
r: &'a AttrRow,
styles: Vec<String>,
width: Length,
height: Length,
) -> Element<'a, Message> {
let style_sel = if r.text_style.is_empty() {
None
} else {
@@ -384,14 +414,14 @@ fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec<String>) -> Element<'a, Mess
column![
pick_field("Text Style", styles, style_sel, |s| {
Message::AttrEditorTextStyle(s)
}),
}, width),
pick_field("Justification", justify_opts, justify_sel, |s| {
Message::AttrEditorJustify(s)
}),
edit_field("Height", &r.height, Message::AttrEditorHeight),
edit_field("Rotation", &r.rotation, Message::AttrEditorRotation),
edit_field("Width Factor", &r.width_factor, Message::AttrEditorWidth),
edit_field("Oblique Angle", &r.oblique, Message::AttrEditorOblique),
}, width),
edit_field("Height", &r.height, Message::AttrEditorHeight, width),
edit_field("Rotation", &r.rotation, Message::AttrEditorRotation, width),
edit_field("Width Factor", &r.width_factor, Message::AttrEditorWidth, width),
edit_field("Oblique Angle", &r.oblique, Message::AttrEditorOblique, width),
field_row(
"",
checkbox(r.backwards)
@@ -400,6 +430,7 @@ fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec<String>) -> Element<'a, Mess
.size(15)
.text_size(12)
.into(),
width,
),
field_row(
"",
@@ -409,9 +440,12 @@ fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec<String>) -> Element<'a, Mess
.size(15)
.text_size(12)
.into(),
width,
),
]
.spacing(8)
.width(width)
.height(height)
.into()
}
@@ -420,6 +454,8 @@ fn properties_tab<'a>(
r: &'a AttrRow,
layers: Vec<String>,
linetypes: Vec<String>,
width: Length,
height: Length,
) -> Element<'a, Message> {
let layer_sel = Some(r.layer.clone());
let lt_sel = Some(if r.linetype.is_empty() {
@@ -439,21 +475,23 @@ fn properties_tab<'a>(
let lw_opts = lw_options();
let lw_sel = LwItem(r.line_weight);
let lw = pick_list(lw_opts, Some(lw_sel), |it: LwItem| {
let lw = crate::ui::pick_list(lw_opts, Some(lw_sel), |it: LwItem| {
Message::AttrEditorLineweight(it.0)
})
.text_size(13)
.padding([3, 6])
.width(Length::Fill);
.width(width);
column![
pick_field("Layer", layers, layer_sel, Message::AttrEditorLayer),
pick_field("Linetype", linetypes, lt_sel, Message::AttrEditorLinetype),
pick_field("Layer", layers, layer_sel, Message::AttrEditorLayer, width),
pick_field("Linetype", linetypes, lt_sel, Message::AttrEditorLinetype, width),
pick_field("Color", color_opts, color_sel, |s| {
Message::AttrEditorColor(s)
}),
field_row("Lineweight", lw.into()),
}, width),
field_row("Lineweight", lw.into(), width),
]
.spacing(8)
.width(width)
.height(height)
.into()
}