diff --git a/src/app/commands.rs b/src/app/commands.rs index f1e6224ce..686e0fc15 100644 --- a/src/app/commands.rs +++ b/src/app/commands.rs @@ -2090,7 +2090,11 @@ impl H7CAD { let parts: Vec<&str> = raw_rest.split_whitespace().collect(); let sub = parts.get(0).map(|s| s.to_uppercase()).unwrap_or_default(); match sub.as_str() { - "" | "LIST" | "?" => { + // No sub-command or "DIALOG" → open the DimStyle Manager dialog + "" | "DIALOG" | "UI" => { + return Task::done(Message::DimStyleDialogOpen); + } + "LIST" | "?" => { let styles: Vec = self.tabs[i].scene.document .dim_styles.iter() .map(|s| format!("{}(txt:{:.2} asz:{:.2})", s.name, s.dimtxt, s.dimasz)) diff --git a/src/app/mod.rs b/src/app/mod.rs index 3060b7e1b..46411238c 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -85,6 +85,37 @@ pub(super) struct H7CAD { page_setup_rotation: String, /// Plot scale: "Fit" | "1:1" | "1:2" | "1:4" | "1:5" | "1:10" | "1:20" | "1:50" | "1:100" | "2:1". page_setup_scale: String, + + // ── DimStyle Dialog ─────────────────────────────────────────────────── + dimstyle_open: bool, + /// Name of the style currently shown in the dialog. + dimstyle_selected: String, + /// Active tab: 0=Lines, 1=Arrows, 2=Text, 3=Scale/Units, 4=Tolerances. + dimstyle_tab: u8, + // Edit buffers (strings while typing): + ds_dimdle: String, ds_dimdli: String, ds_dimgap: String, + ds_dimexe: String, ds_dimexo: String, + ds_dimsd1: bool, ds_dimsd2: bool, + ds_dimse1: bool, ds_dimse2: bool, + ds_dimasz: String, ds_dimcen: String, ds_dimtsz: String, + ds_dimtxt: String, ds_dimtxsty: String, ds_dimtad: String, + ds_dimtih: bool, ds_dimtoh: bool, + ds_dimscale: String, ds_dimlfac: String, + ds_dimlunit: String, ds_dimdec: String, ds_dimpost: String, + ds_dimtol: bool, ds_dimlim: bool, + ds_dimtp: String, ds_dimtm: String, + ds_dimtdec: String, ds_dimtfac: String, +} + +/// Identifies a DimStyle field that can be edited in the dialog. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DsField { + Dimdle, Dimdli, Dimgap, Dimexe, Dimexo, + Dimsd1, Dimsd2, Dimse1, Dimse2, + Dimasz, Dimcen, Dimtsz, + Dimtxt, Dimtxsty, Dimtad, Dimtih, Dimtoh, + Dimscale, Dimlfac, Dimlunit, Dimdec, Dimpost, + Dimtol, Dimlim, Dimtp, Dimtm, Dimtdec, Dimtfac, } #[derive(Debug, Clone)] @@ -293,6 +324,24 @@ pub enum Message { PlotExport, /// Callback after the user picks (or cancels) the export path. PlotExportPath(Option), + // ── DimStyle Dialog ─────────────────────────────────────────────────── + DimStyleDialogOpen, + DimStyleDialogClose, + /// Apply edits to the selected style. + DimStyleDialogApply, + /// Select a different style in the dialog list. + DimStyleDialogSelect(String), + /// Switch the active tab. + DimStyleDialogTab(u8), + /// Create a new empty style (prompts via command line). + DimStyleDialogNew, + /// Set the selected style as the document's current dim style. + DimStyleDialogSetCurrent, + /// Delete the selected style. + DimStyleDialogDelete, + // Field edit messages: + DsEdit(DsField, String), + DsToggle(DsField), } impl H7CAD { @@ -334,6 +383,26 @@ impl H7CAD { page_setup_offset_y: "0.0".to_string(), page_setup_rotation: "0".to_string(), page_setup_scale: "Fit".to_string(), + // DimStyle dialog + dimstyle_open: false, + dimstyle_selected: "Standard".to_string(), + dimstyle_tab: 0, + ds_dimdle: "0".to_string(), ds_dimdli: "3.75".to_string(), + ds_dimgap: "0.625".to_string(), ds_dimexe: "1.25".to_string(), + ds_dimexo: "0.625".to_string(), + ds_dimsd1: false, ds_dimsd2: false, + ds_dimse1: false, ds_dimse2: false, + ds_dimasz: "0.18".to_string(), ds_dimcen: "0.09".to_string(), + ds_dimtsz: "0".to_string(), + ds_dimtxt: "0.18".to_string(), ds_dimtxsty: "Standard".to_string(), + ds_dimtad: "1".to_string(), + ds_dimtih: false, ds_dimtoh: false, + ds_dimscale: "1".to_string(), ds_dimlfac: "1".to_string(), + ds_dimlunit: "2".to_string(), ds_dimdec: "2".to_string(), + ds_dimpost: "<>".to_string(), + ds_dimtol: false, ds_dimlim: false, + ds_dimtp: "0".to_string(), ds_dimtm: "0".to_string(), + ds_dimtdec: "2".to_string(), ds_dimtfac: "1".to_string(), }; app.sync_ribbon_layers(); app diff --git a/src/app/update.rs b/src/app/update.rs index a8898418e..c9384e436 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -2101,6 +2101,185 @@ impl H7CAD { } Task::none() } + + // ── DimStyle Dialog ─────────────────────────────────────────────── + Message::DimStyleDialogOpen => { + let i = self.active_tab; + // Pick the document's current dim style or "Standard". + let cur = self.tabs[i].scene.document.header.current_dimstyle_name.clone(); + let selected = if self.tabs[i].scene.document.dim_styles.get(&cur).is_some() { + cur + } else { + self.tabs[i].scene.document.dim_styles + .iter().next().map(|s| s.name.clone()) + .unwrap_or_else(|| "Standard".to_string()) + }; + self.dimstyle_selected = selected.clone(); + self.dimstyle_open = true; + self.load_dimstyle_bufs(i); + Task::none() + } + Message::DimStyleDialogClose => { + self.dimstyle_open = false; + Task::none() + } + Message::DimStyleDialogApply => { + let i = self.active_tab; + self.apply_dimstyle_bufs(i); + Task::none() + } + Message::DimStyleDialogSelect(name) => { + let i = self.active_tab; + self.dimstyle_selected = name; + self.load_dimstyle_bufs(i); + Task::none() + } + Message::DimStyleDialogTab(tab) => { + self.dimstyle_tab = tab; + Task::none() + } + Message::DimStyleDialogNew => { + // Delegate to the DIMSTYLE NEW command via command line prompt. + self.command_line.push_info("Enter new DimStyle name:"); + self.dimstyle_open = false; + Task::none() + } + Message::DimStyleDialogSetCurrent => { + let i = self.active_tab; + self.push_undo_snapshot(i, "DIMSTYLE SETCURRENT"); + self.tabs[i].scene.document.header.current_dimstyle_name = + self.dimstyle_selected.clone(); + self.tabs[i].dirty = true; + self.command_line.push_output(&format!( + "Current dim style set to '{}'.", self.dimstyle_selected + )); + Task::none() + } + Message::DimStyleDialogDelete => { + let i = self.active_tab; + let name = self.dimstyle_selected.clone(); + if name == "Standard" { + self.command_line.push_error("Cannot delete the Standard dim style."); + } else if self.tabs[i].scene.document.dim_styles.remove(&name).is_some() { + self.tabs[i].dirty = true; + // Select first remaining style. + self.dimstyle_selected = self.tabs[i].scene.document.dim_styles + .iter().next().map(|s| s.name.clone()) + .unwrap_or_else(|| "Standard".to_string()); + self.load_dimstyle_bufs(i); + self.command_line.push_output(&format!("DimStyle '{}' deleted.", name)); + } + Task::none() + } + Message::DsEdit(field, val) => { + self.apply_ds_edit(field, val); + Task::none() + } + Message::DsToggle(field) => { + self.apply_ds_toggle(field); + Task::none() + } + } + } +} + +// ── DimStyle dialog helpers ───────────────────────────────────────────────── + +impl H7CAD { + /// Populate all edit buffers from the currently selected dim style. + fn load_dimstyle_bufs(&mut self, tab: usize) { + let doc = &self.tabs[tab].scene.document; + let Some(ds) = doc.dim_styles.get(&self.dimstyle_selected) else { return }; + self.ds_dimdle = format!("{}", ds.dimdle); + self.ds_dimdli = format!("{}", ds.dimdli); + self.ds_dimgap = format!("{}", ds.dimgap); + self.ds_dimexe = format!("{}", ds.dimexe); + self.ds_dimexo = format!("{}", ds.dimexo); + self.ds_dimsd1 = ds.dimsd1; self.ds_dimsd2 = ds.dimsd2; + self.ds_dimse1 = ds.dimse1; self.ds_dimse2 = ds.dimse2; + self.ds_dimasz = format!("{}", ds.dimasz); + self.ds_dimcen = format!("{}", ds.dimcen); + self.ds_dimtsz = format!("{}", ds.dimtsz); + self.ds_dimtxt = format!("{}", ds.dimtxt); + self.ds_dimtxsty = ds.dimtxsty.clone(); + self.ds_dimtad = format!("{}", ds.dimtad); + self.ds_dimtih = ds.dimtih; self.ds_dimtoh = ds.dimtoh; + self.ds_dimscale = format!("{}", ds.dimscale); + self.ds_dimlfac = format!("{}", ds.dimlfac); + self.ds_dimlunit = format!("{}", ds.dimlunit); + self.ds_dimdec = format!("{}", ds.dimdec); + self.ds_dimpost = ds.dimpost.clone(); + self.ds_dimtol = ds.dimtol; self.ds_dimlim = ds.dimlim; + self.ds_dimtp = format!("{}", ds.dimtp); + self.ds_dimtm = format!("{}", ds.dimtm); + self.ds_dimtdec = format!("{}", ds.dimtdec); + self.ds_dimtfac = format!("{}", ds.dimtfac); + } + + /// Write edit buffers back into the selected dim style document entry. + fn apply_dimstyle_bufs(&mut self, tab: usize) { + self.push_undo_snapshot(tab, "DIMSTYLE EDIT"); + let doc = &mut self.tabs[tab].scene.document; + let Some(ds) = doc.dim_styles.get_mut(&self.dimstyle_selected) else { return }; + macro_rules! set_f64 { ($field:ident, $buf:expr) => { + if let Ok(v) = $buf.trim().parse::() { ds.$field = v; } + }} + macro_rules! set_i16 { ($field:ident, $buf:expr) => { + if let Ok(v) = $buf.trim().parse::() { ds.$field = v; } + }} + set_f64!(dimdle, self.ds_dimdle); set_f64!(dimdli, self.ds_dimdli); + set_f64!(dimgap, self.ds_dimgap); set_f64!(dimexe, self.ds_dimexe); + set_f64!(dimexo, self.ds_dimexo); set_f64!(dimasz, self.ds_dimasz); + set_f64!(dimcen, self.ds_dimcen); set_f64!(dimtsz, self.ds_dimtsz); + set_f64!(dimtxt, self.ds_dimtxt); set_f64!(dimscale, self.ds_dimscale); + set_f64!(dimlfac, self.ds_dimlfac); set_f64!(dimtp, self.ds_dimtp); + set_f64!(dimtm, self.ds_dimtm); set_f64!(dimtfac, self.ds_dimtfac); + set_i16!(dimtad, self.ds_dimtad); set_i16!(dimlunit, self.ds_dimlunit); + set_i16!(dimdec, self.ds_dimdec); set_i16!(dimtdec, self.ds_dimtdec); + ds.dimsd1 = self.ds_dimsd1; ds.dimsd2 = self.ds_dimsd2; + ds.dimse1 = self.ds_dimse1; ds.dimse2 = self.ds_dimse2; + ds.dimtih = self.ds_dimtih; ds.dimtoh = self.ds_dimtoh; + ds.dimtol = self.ds_dimtol; ds.dimlim = self.ds_dimlim; + ds.dimpost = self.ds_dimpost.clone(); + ds.dimtxsty = self.ds_dimtxsty.clone(); + self.tabs[tab].dirty = true; + self.command_line.push_output(&format!( + "DimStyle '{}' updated.", self.dimstyle_selected + )); + } + + /// Update a single string buffer field. + fn apply_ds_edit(&mut self, field: super::DsField, val: String) { + use super::DsField::*; + match field { + Dimdle => self.ds_dimdle = val, Dimdli => self.ds_dimdli = val, + Dimgap => self.ds_dimgap = val, Dimexe => self.ds_dimexe = val, + Dimexo => self.ds_dimexo = val, Dimasz => self.ds_dimasz = val, + Dimcen => self.ds_dimcen = val, Dimtsz => self.ds_dimtsz = val, + Dimtxt => self.ds_dimtxt = val, Dimtxsty => self.ds_dimtxsty = val, + Dimtad => self.ds_dimtad = val, Dimscale => self.ds_dimscale = val, + Dimlfac => self.ds_dimlfac = val, Dimlunit => self.ds_dimlunit = val, + Dimdec => self.ds_dimdec = val, Dimpost => self.ds_dimpost = val, + Dimtp => self.ds_dimtp = val, Dimtm => self.ds_dimtm = val, + Dimtdec => self.ds_dimtdec = val, Dimtfac => self.ds_dimtfac = val, + // Bool fields — no-op for string edit + _ => {} + } + } + + /// Toggle a boolean buffer field. + fn apply_ds_toggle(&mut self, field: super::DsField) { + use super::DsField::*; + match field { + Dimsd1 => self.ds_dimsd1 = !self.ds_dimsd1, + Dimsd2 => self.ds_dimsd2 = !self.ds_dimsd2, + Dimse1 => self.ds_dimse1 = !self.ds_dimse1, + Dimse2 => self.ds_dimse2 = !self.ds_dimse2, + Dimtih => self.ds_dimtih = !self.ds_dimtih, + Dimtoh => self.ds_dimtoh = !self.ds_dimtoh, + Dimtol => self.ds_dimtol = !self.ds_dimtol, + Dimlim => self.ds_dimlim = !self.ds_dimlim, + _ => {} } } } diff --git a/src/app/view.rs b/src/app/view.rs index 36e7c98a1..6b7927b7e 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -227,7 +227,21 @@ impl H7CAD { iced::widget::Space::new().width(0).height(0).into() }; - stack![main_ui, self.app_menu.view(), snap_layer, dropdown_layer, layout_ctx_layer, page_setup_layer].into() + let dimstyle_layer: Element<'_, Message> = if self.dimstyle_open { + let tab = &self.tabs[self.active_tab]; + let styles: Vec = tab.scene.document.dim_styles + .iter().map(|s| s.name.clone()).collect(); + dimstyle_overlay( + styles, + &self.dimstyle_selected, + self.dimstyle_tab, + self, + ) + } else { + iced::widget::Space::new().width(0).height(0).into() + }; + + stack![main_ui, self.app_menu.view(), snap_layer, dropdown_layer, layout_ctx_layer, page_setup_layer, dimstyle_layer].into() } pub fn subscription(&self) -> Subscription { @@ -756,3 +770,254 @@ fn page_setup_overlay<'a>( stack![catcher, positioned].into() } + +// ── DimStyle Dialog overlay ───────────────────────────────────────────────── + +fn dimstyle_overlay<'a>( + styles: Vec, + selected: &'a str, + tab: u8, + app: &'a super::H7CAD, +) -> Element<'a, Message> { + use super::DsField; + use iced::widget::checkbox; + use iced::Length::Shrink; + + const PANEL_BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 }; + const BORDER: Color = Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 }; + const TEXT_COL: Color = Color { r: 0.88, g: 0.88, b: 0.88, a: 1.0 }; + const DIM_COL: Color = Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0 }; + const ACCENT: Color = Color { r: 0.25, g: 0.50, b: 0.85, a: 1.0 }; + const ACTIVE_BG: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 }; + + let lbl = |s: &'static str| text(s).size(11).color(DIM_COL).width(150); + + let field_style = |_: &Theme, _: text_input::Status| text_input::Style { + background: Background::Color(Color { r: 0.10, g: 0.10, b: 0.10, a: 1.0 }), + border: Border { color: BORDER, width: 1.0, radius: 3.0.into() }, + icon: TEXT_COL, placeholder: DIM_COL, value: TEXT_COL, selection: ACCENT, + }; + + let mk_field = |fld: DsField, val: &'a str| -> Element<'a, Message> { + text_input("", val) + .on_input(move |s| Message::DsEdit(fld.clone(), s)) + .style(field_style) + .size(11) + .width(90) + .into() + }; + + let btn_style = |accent: bool| move |_: &Theme, status: button::Status| button::Style { + background: Some(Background::Color(match (accent, status) { + (true, button::Status::Hovered | button::Status::Pressed) => + Color { r: 0.20, g: 0.42, b: 0.72, a: 1.0 }, + (false, button::Status::Hovered | button::Status::Pressed) => + Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 }, + (true, _) => ACCENT, + _ => Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0 }, + })), + text_color: TEXT_COL, + border: Border { color: BORDER, width: 1.0, radius: 4.0.into() }, + ..Default::default() + }; + + let tab_btn = |label: &'static str, idx: u8| { + let active = tab == idx; + button(text(label).size(11).color(TEXT_COL)) + .on_press(Message::DimStyleDialogTab(idx)) + .style(move |_: &Theme, st| button::Style { + background: Some(Background::Color(match (active, st) { + (true, _) => ACTIVE_BG, + (false, button::Status::Hovered | button::Status::Pressed) => + Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 }, + _ => Color { r: 0.20, g: 0.20, b: 0.20, a: 1.0 }, + })), + text_color: TEXT_COL, + border: Border { color: BORDER, width: 1.0, radius: 3.0.into() }, + ..Default::default() + }) + .padding([4, 10]) + }; + + // ── Style list ──────────────────────────────────────────────────────── + let style_list: Element<'_, Message> = { + let mut col = column![].spacing(2); + for name in styles { + let active = name == selected; + col = col.push( + button(text(name.clone()).size(11).color(TEXT_COL)) + .on_press(Message::DimStyleDialogSelect(name)) + .style(move |_: &Theme, st| button::Style { + background: Some(Background::Color(match (active, st) { + (true, _) => ACTIVE_BG, + (false, button::Status::Hovered | button::Status::Pressed) => + Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 }, + _ => Color { r: 0.18, g: 0.18, b: 0.18, a: 1.0 }, + })), + text_color: TEXT_COL, + border: Border { color: BORDER, width: 0.0, radius: 3.0.into() }, + ..Default::default() + }) + .padding([3, 8]) + .width(Fill) + ); + } + iced::widget::scrollable(col).height(160).into() + }; + + let style_panel = column![ + text("Styles").size(11).color(DIM_COL), + container(style_list) + .style(|_: &Theme| container::Style { + background: Some(Background::Color(Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 })), + border: Border { color: BORDER, width: 1.0, radius: 3.0.into() }, + ..Default::default() + }) + .width(150) + .padding(2), + row![ + button(text("New").size(10).color(TEXT_COL)) + .on_press(Message::DimStyleDialogNew) + .style(btn_style(false)).padding([3, 8]), + button(text("Delete").size(10).color(TEXT_COL)) + .on_press(Message::DimStyleDialogDelete) + .style(btn_style(false)).padding([3, 8]), + ].spacing(4), + button(text("Set Current").size(10).color(TEXT_COL)) + .on_press(Message::DimStyleDialogSetCurrent) + .style(btn_style(false)).padding([3, 8]).width(Fill), + ].spacing(6).width(150); + + // ── Tab bar ─────────────────────────────────────────────────────────── + let tabs = row![ + tab_btn("Lines", 0), + tab_btn("Arrows", 1), + tab_btn("Text", 2), + tab_btn("Scale/Units", 3), + tab_btn("Tolerances", 4), + ].spacing(2); + + // ── Checkbox helper ─────────────────────────────────────────────────── + let chk = |label: &'static str, val: bool, fld: DsField| -> Element<'a, Message> { + checkbox(val) + .label(label) + .on_toggle(move |_| Message::DsToggle(fld.clone())) + .size(14) + .text_size(11) + .into() + }; + + // ── Tab content ─────────────────────────────────────────────────────── + let tab_content: Element<'_, Message> = match tab { + 0 => column![ + text("Dimension Line").size(11).color(ACCENT), + row![lbl("Extension (DIMDLE)"), mk_field(DsField::Dimdle, &app.ds_dimdle)].spacing(8).align_y(iced::Center), + row![lbl("Spacing (DIMDLI)"), mk_field(DsField::Dimdli, &app.ds_dimdli)].spacing(8).align_y(iced::Center), + row![lbl("Text gap (DIMGAP)"), mk_field(DsField::Dimgap, &app.ds_dimgap)].spacing(8).align_y(iced::Center), + chk("Suppress 1st line (DIMSD1)", app.ds_dimsd1, DsField::Dimsd1), + chk("Suppress 2nd line (DIMSD2)", app.ds_dimsd2, DsField::Dimsd2), + text("Extension Line").size(11).color(ACCENT), + row![lbl("Extension (DIMEXE)"), mk_field(DsField::Dimexe, &app.ds_dimexe)].spacing(8).align_y(iced::Center), + row![lbl("Offset (DIMEXO)"), mk_field(DsField::Dimexo, &app.ds_dimexo)].spacing(8).align_y(iced::Center), + chk("Suppress 1st line (DIMSE1)", app.ds_dimse1, DsField::Dimse1), + chk("Suppress 2nd line (DIMSE2)", app.ds_dimse2, DsField::Dimse2), + ].spacing(6).into(), + + 1 => column![ + text("Arrows").size(11).color(ACCENT), + row![lbl("Arrow size (DIMASZ)"), mk_field(DsField::Dimasz, &app.ds_dimasz)].spacing(8).align_y(iced::Center), + row![lbl("Center mark (DIMCEN)"), mk_field(DsField::Dimcen, &app.ds_dimcen)].spacing(8).align_y(iced::Center), + row![lbl("Tick size (DIMTSZ)"), mk_field(DsField::Dimtsz, &app.ds_dimtsz)].spacing(8).align_y(iced::Center), + ].spacing(6).into(), + + 2 => column![ + text("Text").size(11).color(ACCENT), + row![lbl("Height (DIMTXT)"), mk_field(DsField::Dimtxt, &app.ds_dimtxt)].spacing(8).align_y(iced::Center), + row![lbl("Style (DIMTXSTY)"), mk_field(DsField::Dimtxsty, &app.ds_dimtxsty)].spacing(8).align_y(iced::Center), + row![lbl("Vertical pos (DIMTAD)"), mk_field(DsField::Dimtad, &app.ds_dimtad)].spacing(8).align_y(iced::Center), + chk("Horizontal inside (DIMTIH)", app.ds_dimtih, DsField::Dimtih), + chk("Horizontal outside (DIMTOH)", app.ds_dimtoh, DsField::Dimtoh), + ].spacing(6).into(), + + 3 => column![ + text("Scale").size(11).color(ACCENT), + row![lbl("Overall scale (DIMSCALE)"), mk_field(DsField::Dimscale, &app.ds_dimscale)].spacing(8).align_y(iced::Center), + row![lbl("Linear factor (DIMLFAC)"), mk_field(DsField::Dimlfac, &app.ds_dimlfac)].spacing(8).align_y(iced::Center), + text("Units").size(11).color(ACCENT), + row![lbl("Format (DIMLUNIT)"), mk_field(DsField::Dimlunit, &app.ds_dimlunit)].spacing(8).align_y(iced::Center), + row![lbl("Decimals (DIMDEC)"), mk_field(DsField::Dimdec, &app.ds_dimdec)].spacing(8).align_y(iced::Center), + row![lbl("Suffix (DIMPOST)"), mk_field(DsField::Dimpost, &app.ds_dimpost)].spacing(8).align_y(iced::Center), + ].spacing(6).into(), + + _ => column![ + text("Tolerances").size(11).color(ACCENT), + chk("Generate tolerances (DIMTOL)", app.ds_dimtol, DsField::Dimtol), + chk("Limits generation (DIMLIM)", app.ds_dimlim, DsField::Dimlim), + row![lbl("Plus tolerance (DIMTP)"), mk_field(DsField::Dimtp, &app.ds_dimtp)].spacing(8).align_y(iced::Center), + row![lbl("Minus tolerance (DIMTM)"), mk_field(DsField::Dimtm, &app.ds_dimtm)].spacing(8).align_y(iced::Center), + row![lbl("Tol. decimals (DIMTDEC)"), mk_field(DsField::Dimtdec, &app.ds_dimtdec)].spacing(8).align_y(iced::Center), + row![lbl("Tol. scale (DIMTFAC)"), mk_field(DsField::Dimtfac, &app.ds_dimtfac)].spacing(8).align_y(iced::Center), + ].spacing(6).into(), + }; + + // ── Right panel ─────────────────────────────────────────────────────── + let right_panel = column![ + text(format!("Editing: {selected}")).size(12).color(TEXT_COL), + tabs, + container( + iced::widget::scrollable( + container(tab_content).padding(8) + ).height(220) + ) + .style(|_: &Theme| container::Style { + background: Some(Background::Color(Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 })), + border: Border { color: BORDER, width: 1.0, radius: 3.0.into() }, + ..Default::default() + }) + .width(Fill), + row![ + button(text("Apply").size(11).color(TEXT_COL)) + .on_press(Message::DimStyleDialogApply) + .style(btn_style(true)).padding([5, 16]), + button(text("Close").size(11).color(TEXT_COL)) + .on_press(Message::DimStyleDialogClose) + .style(btn_style(false)).padding([5, 12]), + ].spacing(8), + ].spacing(8).width(Fill); + + // ── Main panel ──────────────────────────────────────────────────────── + let panel = container( + column![ + row![ + text("Dimension Style Manager").size(14).color(TEXT_COL), + Space::new().width(Fill), + button(text("✕").size(12).color(DIM_COL)) + .on_press(Message::DimStyleDialogClose) + .style(|_: &Theme, _| button::Style { + background: Some(Background::Color(Color::TRANSPARENT)), + text_color: DIM_COL, + ..Default::default() + }) + .padding([2, 6]), + ].align_y(iced::Center), + row![style_panel, right_panel].spacing(12).align_y(iced::Top), + ].spacing(10).padding(16) + ) + .style(|_: &Theme| container::Style { + background: Some(Background::Color(PANEL_BG)), + border: Border { color: BORDER, width: 1.0, radius: 6.0.into() }, + ..Default::default() + }) + .width(Shrink); + + let catcher = mouse_area( + container(iced::widget::Space::new().width(Fill).height(Fill)) + ).on_press(Message::DimStyleDialogClose); + + let positioned = container(panel) + .width(Fill).height(Fill) + .align_x(iced::Alignment::Center) + .align_y(iced::Alignment::Center); + + stack![catcher, positioned].into() +}