diff --git a/src/app/mod.rs b/src/app/mod.rs index 164c54d4a..84ca3716b 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1026,8 +1026,11 @@ pub enum Message { }, /// Toggle background fill on a cell style. TableStyleCellToggleFill(u8), - /// Cycle the alignment of a cell style. - TableStyleCellCycleAlign(u8), + /// Set the alignment of a cell style from the dropdown. + TableStyleCellSetAlign { + row: u8, + value: String, + }, /// Write a cell's edit buffers back into the selected table style. TableStyleCellApply(u8), // ── MLineStyle Dialog ───────────────────────────────────────────────── @@ -1051,7 +1054,10 @@ pub enum Message { value: String, }, MLeaderStyleToggle(&'static str), - MLeaderStyleCycle(&'static str), + MLeaderStyleSetEnum { + field: &'static str, + value: String, + }, MLeaderStyleApply, // ── DimStyle Dialog ─────────────────────────────────────────────────── DimStyleDialogOpen, @@ -1071,9 +1077,12 @@ pub enum Message { // Field edit messages: DsEdit(DsField, String), DsToggle(DsField), - /// Cycle a block/linetype Handle field on the selected dim style to the - /// next available block-record / linetype. - DsCycleHandle(&'static str), + /// Set a block/linetype Handle field on the selected dim style from a + /// dropdown of available block-records / linetypes (by name). + DsSetHandle { + field: &'static str, + value: String, + }, // ── Raster Image ────────────────────────────────────────────────────── /// Open file-picker dialog for IMAGE command (async). ImagePick, diff --git a/src/app/update.rs b/src/app/update.rs index e2e15bb0d..fd2c27178 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -5810,21 +5810,21 @@ impl OpenCADStudio { Task::none() } - Message::TableStyleCellCycleAlign(row) => { + Message::TableStyleCellSetAlign { row, value } => { use acadrust::objects::CellAlignment; let i = self.active_tab; if let Some(s) = self.tablestyle_mut(i) { if let Some(c) = Self::ts_cell_of(s, row) { - c.alignment = match c.alignment { - CellAlignment::TopLeft => CellAlignment::TopCenter, - CellAlignment::TopCenter => CellAlignment::TopRight, - CellAlignment::TopRight => CellAlignment::MiddleLeft, - CellAlignment::MiddleLeft => CellAlignment::MiddleCenter, - CellAlignment::MiddleCenter => CellAlignment::MiddleRight, - CellAlignment::MiddleRight => CellAlignment::BottomLeft, - CellAlignment::BottomLeft => CellAlignment::BottomCenter, - CellAlignment::BottomCenter => CellAlignment::BottomRight, - CellAlignment::BottomRight => CellAlignment::TopLeft, + c.alignment = match value.as_str() { + "TopLeft" => CellAlignment::TopLeft, + "TopCenter" => CellAlignment::TopCenter, + "TopRight" => CellAlignment::TopRight, + "MiddleLeft" => CellAlignment::MiddleLeft, + "MiddleRight" => CellAlignment::MiddleRight, + "BottomLeft" => CellAlignment::BottomLeft, + "BottomCenter" => CellAlignment::BottomCenter, + "BottomRight" => CellAlignment::BottomRight, + _ => CellAlignment::MiddleCenter, }; } self.push_undo_snapshot(i, "TABLESTYLE EDIT"); @@ -6298,7 +6298,7 @@ impl OpenCADStudio { } Task::none() } - Message::MLeaderStyleCycle(field) => { + Message::MLeaderStyleSetEnum { field, value } => { use acadrust::objects::{ LeaderContentType, MultiLeaderPathType, TextAlignmentType, TextAngleType, }; @@ -6306,38 +6306,34 @@ impl OpenCADStudio { if let Some(s) = self.mleaderstyle_mut(i) { match field { "path_type" => { - s.path_type = match s.path_type { - MultiLeaderPathType::Invisible => { - MultiLeaderPathType::StraightLineSegments - } - MultiLeaderPathType::StraightLineSegments => { - MultiLeaderPathType::Spline - } - MultiLeaderPathType::Spline => MultiLeaderPathType::Invisible, + s.path_type = match value.as_str() { + "Invisible" => MultiLeaderPathType::Invisible, + "Spline" => MultiLeaderPathType::Spline, + _ => MultiLeaderPathType::StraightLineSegments, }; } "content_type" => { - s.content_type = match s.content_type { - LeaderContentType::None => LeaderContentType::Block, - LeaderContentType::Block => LeaderContentType::MText, - LeaderContentType::MText => LeaderContentType::Tolerance, - LeaderContentType::Tolerance => LeaderContentType::None, + s.content_type = match value.as_str() { + "None" => LeaderContentType::None, + "Block" => LeaderContentType::Block, + "Tolerance" => LeaderContentType::Tolerance, + _ => LeaderContentType::MText, }; } "text_angle_type" => { - s.text_angle_type = match s.text_angle_type { - TextAngleType::ParallelToLastLeaderLine => TextAngleType::Horizontal, - TextAngleType::Horizontal => TextAngleType::Optimized, - TextAngleType::Optimized => { + s.text_angle_type = match value.as_str() { + "ParallelToLastLeaderLine" => { TextAngleType::ParallelToLastLeaderLine } + "Optimized" => TextAngleType::Optimized, + _ => TextAngleType::Horizontal, }; } "text_alignment" => { - s.text_alignment = match s.text_alignment { - TextAlignmentType::Left => TextAlignmentType::Center, - TextAlignmentType::Center => TextAlignmentType::Right, - TextAlignmentType::Right => TextAlignmentType::Left, + s.text_alignment = match value.as_str() { + "Center" => TextAlignmentType::Center, + "Right" => TextAlignmentType::Right, + _ => TextAlignmentType::Left, }; } _ => {} @@ -6534,7 +6530,7 @@ impl OpenCADStudio { self.apply_ds_toggle(field); Task::none() } - Message::DsCycleHandle(field) => { + Message::DsSetHandle { field, value } => { let i = self.active_tab; let name = self.dimstyle_selected.clone(); let is_lt = matches!( @@ -6542,38 +6538,31 @@ impl OpenCADStudio { "dimltex_handle" | "dimltex1_handle" | "dimltex2_handle" ); let doc = &self.tabs[i].scene.document; - let mut candidates: Vec = - vec![acadrust::types::Handle::NULL]; - if is_lt { - candidates.extend(doc.line_types.iter().map(|lt| lt.handle)); + let handle = if value == "Default" || value == "ByBlock" { + acadrust::types::Handle::NULL + } else if is_lt { + doc.line_types + .iter() + .find(|lt| lt.name == value) + .map(|lt| lt.handle) + .unwrap_or(acadrust::types::Handle::NULL) } else { - candidates.extend(doc.block_records.iter().map(|b| b.handle)); - } - let Some(ds) = doc.dim_styles.get(&name) else { - return Task::none(); + doc.block_records + .iter() + .find(|b| b.name == value) + .map(|b| b.handle) + .unwrap_or(acadrust::types::Handle::NULL) }; - let cur = match field { - "dimblk" => ds.dimblk, - "dimblk1" => ds.dimblk1, - "dimblk2" => ds.dimblk2, - "dimldrblk" => ds.dimldrblk, - "dimltex_handle" => ds.dimltex_handle, - "dimltex1_handle" => ds.dimltex1_handle, - "dimltex2_handle" => ds.dimltex2_handle, - _ => return Task::none(), - }; - let idx = candidates.iter().position(|h| *h == cur).unwrap_or(0); - let next = candidates[(idx + 1) % candidates.len()]; self.push_undo_snapshot(i, "DIMSTYLE EDIT"); if let Some(ds) = self.tabs[i].scene.document.dim_styles.get_mut(&name) { match field { - "dimblk" => ds.dimblk = next, - "dimblk1" => ds.dimblk1 = next, - "dimblk2" => ds.dimblk2 = next, - "dimldrblk" => ds.dimldrblk = next, - "dimltex_handle" => ds.dimltex_handle = next, - "dimltex1_handle" => ds.dimltex1_handle = next, - "dimltex2_handle" => ds.dimltex2_handle = next, + "dimblk" => ds.dimblk = handle, + "dimblk1" => ds.dimblk1 = handle, + "dimblk2" => ds.dimblk2 = handle, + "dimldrblk" => ds.dimldrblk = handle, + "dimltex_handle" => ds.dimltex_handle = handle, + "dimltex1_handle" => ds.dimltex1_handle = handle, + "dimltex2_handle" => ds.dimltex2_handle = handle, _ => {} } } diff --git a/src/app/view.rs b/src/app/view.rs index eb358898c..ba974ff47 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -235,6 +235,12 @@ impl OpenCADStudio { .map(|s| s.name.clone()) .collect(); let doc = &tab.scene.document; + // Dropdown options (names must match the records exactly so the + // selection can be resolved back to a handle on the update side). + let mut block_opts: Vec = vec!["Default".to_string()]; + block_opts.extend(doc.block_records.iter().map(|b| b.name.clone())); + let mut lt_opts: Vec = vec!["ByBlock".to_string()]; + lt_opts.extend(doc.line_types.iter().map(|lt| lt.name.clone())); let blk_name = |h: acadrust::types::Handle| -> String { if h.is_null() { "Default".to_string() @@ -242,8 +248,8 @@ impl OpenCADStudio { doc.block_records .iter() .find(|b| b.handle == h) - .map(|b| b.name.trim_start_matches('_').to_string()) - .unwrap_or_else(|| "?".to_string()) + .map(|b| b.name.clone()) + .unwrap_or_else(|| "Default".to_string()) } }; let lt_name = |h: acadrust::types::Handle| -> String { @@ -254,7 +260,7 @@ impl OpenCADStudio { .iter() .find(|lt| lt.handle == h) .map(|lt| lt.name.clone()) - .unwrap_or_else(|| "?".to_string()) + .unwrap_or_else(|| "ByBlock".to_string()) } }; let ds_sel = doc.dim_styles.get(&self.dimstyle_selected); @@ -359,6 +365,8 @@ impl OpenCADStudio { dimltex_name, dimltex1_name, dimltex2_name, + block_opts, + lt_opts, }, ); } diff --git a/src/ui/dimstyle.rs b/src/ui/dimstyle.rs index 2a8c4218f..31d6e4540 100644 --- a/src/ui/dimstyle.rs +++ b/src/ui/dimstyle.rs @@ -1,7 +1,9 @@ //! Dimension Style Manager window — fills the entire OS window. use crate::app::{DsField, Message}; -use iced::widget::{button, checkbox, column, container, row, scrollable, text, text_input, Space}; +use iced::widget::{ + button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space, +}; use iced::{Background, Border, Color, Element, Fill, Theme}; const TB: Color = Color { @@ -130,7 +132,7 @@ pub struct DimStyleValues<'a> { pub dimalttz: &'a str, pub dimtolj: &'a str, pub dimtzin: &'a str, - // Resolved display names for the block/linetype Handle fields. + // Resolved selected names for the block/linetype Handle fields. pub dimblk_name: String, pub dimblk1_name: String, pub dimblk2_name: String, @@ -138,6 +140,9 @@ pub struct DimStyleValues<'a> { pub dimltex_name: String, pub dimltex1_name: String, pub dimltex2_name: String, + // Dropdown option lists shared by the arrowhead / linetype fields. + pub block_opts: Vec, + pub lt_opts: Vec, } fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { @@ -384,16 +389,20 @@ pub fn view_window<'a>( .into() }; - // Block / linetype Handle cycler: shows the resolved name and a button that - // advances to the next available block-record (arrowheads) or linetype. - let hrow = move |label: &'static str, name: String, field: &'static str| -> Element<'a, Message> { + // Block / linetype Handle dropdown: pick a block-record (arrowheads) or a + // linetype by name from the available records. + let hrow = move |label: &'static str, + options: Vec, + selected: String, + field: &'static str| + -> Element<'a, Message> { row![ lbl(label), - text(name).size(11).width(110), - button(text("Cycle").size(10)) - .on_press(Message::DsCycleHandle(field)) - .style(btn_s(false)) - .padding([2, 8]), + pick_list(options, Some(selected), move |value| { + Message::DsSetHandle { field, value } + }) + .text_size(11) + .width(150), ] .spacing(8) .align_y(iced::Center) @@ -444,18 +453,18 @@ pub fn view_window<'a>( row![lbl("Ext line weight (DIMLWE)"), mk_field(DsField::Dimlwe, vals.dimlwe)].spacing(8).align_y(iced::Center), chk("Fixed-length ext lines (DIMFXLON)", vals.dimfxlon, DsField::Dimfxlon), row![lbl("Fixed length (DIMFXL)"), mk_field(DsField::Dimfxl, vals.dimfxl)].spacing(8).align_y(iced::Center), - hrow("Dim line linetype (DIMLTYPE)", vals.dimltex_name.clone(), "dimltex_handle"), - hrow("Ext line 1 linetype (DIMLTEX1)", vals.dimltex1_name.clone(), "dimltex1_handle"), - hrow("Ext line 2 linetype (DIMLTEX2)", vals.dimltex2_name.clone(), "dimltex2_handle"), + hrow("Dim line linetype (DIMLTYPE)", vals.lt_opts.clone(), vals.dimltex_name.clone(), "dimltex_handle"), + hrow("Ext line 1 linetype (DIMLTEX1)", vals.lt_opts.clone(), vals.dimltex1_name.clone(), "dimltex1_handle"), + hrow("Ext line 2 linetype (DIMLTEX2)", vals.lt_opts.clone(), vals.dimltex2_name.clone(), "dimltex2_handle"), ] .spacing(7) .into(), 1 => column![ text("Arrows").size(11).color(ACCENT), - hrow("Arrowhead (DIMBLK)", vals.dimblk_name.clone(), "dimblk"), - hrow("1st arrowhead (DIMBLK1)", vals.dimblk1_name.clone(), "dimblk1"), - hrow("2nd arrowhead (DIMBLK2)", vals.dimblk2_name.clone(), "dimblk2"), - hrow("Leader arrowhead (DIMLDRBLK)", vals.dimldrblk_name.clone(), "dimldrblk"), + hrow("Arrowhead (DIMBLK)", vals.block_opts.clone(), vals.dimblk_name.clone(), "dimblk"), + hrow("1st arrowhead (DIMBLK1)", vals.block_opts.clone(), vals.dimblk1_name.clone(), "dimblk1"), + hrow("2nd arrowhead (DIMBLK2)", vals.block_opts.clone(), vals.dimblk2_name.clone(), "dimblk2"), + hrow("Leader arrowhead (DIMLDRBLK)", vals.block_opts.clone(), vals.dimldrblk_name.clone(), "dimldrblk"), row![ lbl("Arrow size (DIMASZ)"), mk_field(DsField::Dimasz, vals.dimasz) diff --git a/src/ui/mleaderstyle.rs b/src/ui/mleaderstyle.rs index 2e97d4879..5b5fe69b3 100644 --- a/src/ui/mleaderstyle.rs +++ b/src/ui/mleaderstyle.rs @@ -1,7 +1,9 @@ //! Multileader Style Manager window — fills the entire OS window. use crate::app::Message; -use iced::widget::{button, checkbox, column, container, row, scrollable, text, text_input, Space}; +use iced::widget::{ + button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space, +}; use iced::{Background, Border, Color, Element, Fill, Theme}; const TB: Color = Color { @@ -169,16 +171,17 @@ fn num_row<'a>( fn enum_row<'a>( label: &'static str, - val: String, + options: Vec, + selected: String, field: &'static str, ) -> Element<'a, Message> { row![ text(label).size(11).color(DIM).width(150), - text(val).size(11).width(150), - button(text("Cycle").size(10)) - .on_press(Message::MLeaderStyleCycle(field)) - .style(btn_s(false)) - .padding([2, 8]), + pick_list(options, Some(selected), move |value| { + Message::MLeaderStyleSetEnum { field, value } + }) + .text_size(11) + .width(190), ] .spacing(8) .align_y(iced::Center) @@ -276,7 +279,15 @@ pub fn view_window<'a>(v: MLeaderStyleView<'a>) -> Element<'a, Message> { .spacing(8), // Leader Format section("Leader Format"), - enum_row("Path type:", format!("{:?}", s.path_type), "path_type"), + enum_row( + "Path type:", + ["Invisible", "StraightLineSegments", "Spline"] + .iter() + .map(|s| s.to_string()) + .collect(), + format!("{:?}", s.path_type), + "path_type" + ), num_row("Line color (ACI):", "256", v.line_color, "line_color"), num_row("Arrowhead size:", "0.18", v.arrowhead_size, "arrowhead_size"), num_row("Break gap size:", "0.125", v.break_gap, "break_gap"), @@ -293,17 +304,33 @@ pub fn view_window<'a>(v: MLeaderStyleView<'a>) -> Element<'a, Message> { chk("Annotative", s.is_annotative, "annotative"), // Content section("Content"), - enum_row("Content type:", format!("{:?}", s.content_type), "content_type"), + enum_row( + "Content type:", + ["None", "Block", "MText", "Tolerance"] + .iter() + .map(|s| s.to_string()) + .collect(), + format!("{:?}", s.content_type), + "content_type" + ), num_row("Default text:", "", v.default_text, "default_text"), num_row("Text height:", "0.18", v.text_height, "text_height"), num_row("Text color (ACI):", "256", v.text_color, "text_color"), enum_row( "Text angle:", + ["ParallelToLastLeaderLine", "Horizontal", "Optimized"] + .iter() + .map(|s| s.to_string()) + .collect(), format!("{:?}", s.text_angle_type), "text_angle_type" ), enum_row( "Text alignment:", + ["Left", "Center", "Right"] + .iter() + .map(|s| s.to_string()) + .collect(), format!("{:?}", s.text_alignment), "text_alignment" ), diff --git a/src/ui/tablestyle.rs b/src/ui/tablestyle.rs index 956d8f651..ec0121d9a 100644 --- a/src/ui/tablestyle.rs +++ b/src/ui/tablestyle.rs @@ -1,7 +1,9 @@ //! Table Style Manager window — fills the entire OS window. use crate::app::Message; -use iced::widget::{button, checkbox, column, container, row, scrollable, text, text_input, Space}; +use iced::widget::{ + button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space, +}; use iced::{Background, Border, Color, Element, Fill, Theme}; const TB: Color = Color { @@ -241,11 +243,26 @@ pub fn view_window<'a>( cell_in(" Fill color (ACI):", "256", &cell_fillcolor[r], "fillcolor"), row![ text(" Alignment:").size(11).color(DIM).width(150), - text(format!("{:?}", rs.alignment)).size(11).width(120), - button(text("Cycle").size(10)) - .on_press(Message::TableStyleCellCycleAlign(row)) - .style(btn_s(false)) - .padding([2, 8]), + pick_list( + [ + "TopLeft", + "TopCenter", + "TopRight", + "MiddleLeft", + "MiddleCenter", + "MiddleRight", + "BottomLeft", + "BottomCenter", + "BottomRight", + ] + .iter() + .map(|s| s.to_string()) + .collect::>(), + Some(format!("{:?}", rs.alignment)), + move |value| Message::TableStyleCellSetAlign { row, value }, + ) + .text_size(11) + .width(140), ] .spacing(8) .align_y(iced::Center),