diff --git a/src/app/mod.rs b/src/app/mod.rs index e10cf2e08..58502e920 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -323,11 +323,21 @@ pub(super) struct OpenCADStudio { /// Edit buffers for the table style's general margins. ts_hmargin: String, ts_vmargin: String, + /// General table-style description buffer. + ts_description: String, /// Per-cell edit buffers, indexed 0=Data, 1=Header, 2=Title. ts_cell_textstyle: [String; 3], ts_cell_height: [String; 3], ts_cell_textcolor: [String; 3], ts_cell_fillcolor: [String; 3], + ts_cell_datatype: [String; 3], + ts_cell_unittype: [String; 3], + ts_cell_format: [String; 3], + /// Per-cell, per-border numeric buffers ([cell][border], border order: + /// 0=left 1=right 2=top 3=bottom 4=horizontal-inside 5=vertical-inside). + ts_border_lw: [[String; 6]; 3], + ts_border_color: [[String; 6]; 3], + ts_border_spacing: [[String; 6]; 3], // ── TextStyle Font Browser ──────────────────────────────────────────── textstyle_selected: String, @@ -1043,6 +1053,26 @@ pub enum Message { }, /// Write a cell's edit buffers back into the selected table style. TableStyleCellApply(u8), + /// Set the table flow direction from the dropdown. + TableStyleSetFlow(String), + /// Update a per-cell, per-border numeric edit buffer. + TableStyleBorderEdit { + cell: u8, + border: u8, + field: &'static str, + value: String, + }, + /// Set a border's line type (Single / Double). + TableStyleBorderSetType { + cell: u8, + border: u8, + value: String, + }, + /// Toggle a border's visibility. + TableStyleBorderToggleInvisible { + cell: u8, + border: u8, + }, // ── MLineStyle Dialog ───────────────────────────────────────────────── MlStyleDialogOpen, #[allow(dead_code)] @@ -1247,10 +1277,17 @@ impl OpenCADStudio { tablestyle_selected: "Standard".to_string(), ts_hmargin: "1.5".to_string(), ts_vmargin: "1.5".to_string(), - ts_cell_textstyle: [String::new(), String::new(), String::new()], - ts_cell_height: [String::new(), String::new(), String::new()], - ts_cell_textcolor: [String::new(), String::new(), String::new()], - ts_cell_fillcolor: [String::new(), String::new(), String::new()], + ts_description: String::new(), + ts_cell_textstyle: Default::default(), + ts_cell_height: Default::default(), + ts_cell_textcolor: Default::default(), + ts_cell_fillcolor: Default::default(), + ts_cell_datatype: Default::default(), + ts_cell_unittype: Default::default(), + ts_cell_format: Default::default(), + ts_border_lw: Default::default(), + ts_border_color: Default::default(), + ts_border_spacing: Default::default(), // MLineStyle dialog mlstyle_selected: "Standard".to_string(), // MLeaderStyle dialog diff --git a/src/app/update.rs b/src/app/update.rs index c4b51b6af..4a76cefe7 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -5755,6 +5755,7 @@ impl OpenCADStudio { match field { "hmargin" => self.ts_hmargin = value, "vmargin" => self.ts_vmargin = value, + "description" => self.ts_description = value, _ => {} } Task::none() @@ -5766,6 +5767,7 @@ impl OpenCADStudio { let name = self.tablestyle_selected.clone(); let h: Option = self.ts_hmargin.trim().parse().ok(); let v: Option = self.ts_vmargin.trim().parse().ok(); + let desc = self.ts_description.clone(); self.push_undo_snapshot(i, "TABLESTYLE EDIT"); for obj in self.tabs[i].scene.document.objects.values_mut() { if let ObjectType::TableStyle(s) = obj { @@ -5776,6 +5778,7 @@ impl OpenCADStudio { if let Some(v) = v { s.vertical_margin = v; } + s.description = desc.clone(); } } } @@ -5783,6 +5786,20 @@ impl OpenCADStudio { Task::none() } + Message::TableStyleSetFlow(value) => { + use acadrust::objects::TableFlowDirection; + let i = self.active_tab; + if let Some(s) = self.tablestyle_mut(i) { + s.flow_direction = match value.as_str() { + "Up" => TableFlowDirection::Up, + _ => TableFlowDirection::Down, + }; + self.push_undo_snapshot(i, "TABLESTYLE EDIT"); + self.tabs[i].dirty = true; + } + Task::none() + } + Message::TableStyleCellEdit { row, field, value } => { let r = row as usize; if r < 3 { @@ -5791,12 +5808,69 @@ impl OpenCADStudio { "height" => self.ts_cell_height[r] = value, "textcolor" => self.ts_cell_textcolor[r] = value, "fillcolor" => self.ts_cell_fillcolor[r] = value, + "datatype" => self.ts_cell_datatype[r] = value, + "unittype" => self.ts_cell_unittype[r] = value, + "format" => self.ts_cell_format[r] = value, _ => {} } } Task::none() } + Message::TableStyleBorderEdit { + cell, + border, + field, + value, + } => { + let (c, b) = (cell as usize, border as usize); + if c < 3 && b < 6 { + match field { + "lw" => self.ts_border_lw[c][b] = value, + "color" => self.ts_border_color[c][b] = value, + "spacing" => self.ts_border_spacing[c][b] = value, + _ => {} + } + } + Task::none() + } + + Message::TableStyleBorderSetType { + cell, + border, + value, + } => { + use acadrust::objects::TableBorderType; + let i = self.active_tab; + if let Some(s) = self.tablestyle_mut(i) { + if let Some(bd) = Self::ts_cell_of(s, cell).and_then(|c| Self::ts_border_of(c, border)) + { + bd.border_type = match value.as_str() { + "Double" => TableBorderType::Double, + _ => TableBorderType::Single, + }; + } + self.push_undo_snapshot(i, "TABLESTYLE EDIT"); + self.tabs[i].dirty = true; + self.tabs[i].scene.bump_geometry(); + } + Task::none() + } + + Message::TableStyleBorderToggleInvisible { cell, border } => { + let i = self.active_tab; + if let Some(s) = self.tablestyle_mut(i) { + if let Some(bd) = Self::ts_cell_of(s, cell).and_then(|c| Self::ts_border_of(c, border)) + { + bd.is_invisible = !bd.is_invisible; + } + self.push_undo_snapshot(i, "TABLESTYLE EDIT"); + self.tabs[i].dirty = true; + self.tabs[i].scene.bump_geometry(); + } + Task::none() + } + Message::TableStyleCellToggleFill(row) => { let i = self.active_tab; if let Some(s) = self.tablestyle_mut(i) { @@ -5844,6 +5918,18 @@ impl OpenCADStudio { let height: Option = self.ts_cell_height[r].trim().parse().ok(); let tc: Option = self.ts_cell_textcolor[r].trim().parse().ok(); let fc: Option = self.ts_cell_fillcolor[r].trim().parse().ok(); + let dtype: Option = self.ts_cell_datatype[r].trim().parse().ok(); + let utype: Option = self.ts_cell_unittype[r].trim().parse().ok(); + let fmt = self.ts_cell_format[r].clone(); + // Per-border numeric edits for this cell. + let border_vals: [(Option, Option, Option); 6] = + std::array::from_fn(|b| { + ( + self.ts_border_lw[r][b].trim().parse().ok(), + self.ts_border_color[r][b].trim().parse().ok(), + self.ts_border_spacing[r][b].trim().parse().ok(), + ) + }); if let Some(c) = self.tablestyle_mut(i).and_then(|s| Self::ts_cell_of(s, row)) { if !ts.is_empty() { c.text_style_name = ts; @@ -5857,6 +5943,26 @@ impl OpenCADStudio { if let Some(v) = fc { c.fill_color = acadrust::types::Color::from_index(v); } + if let Some(v) = dtype { + c.data_type = v; + } + if let Some(v) = utype { + c.unit_type = v; + } + c.format_string = fmt; + for (b, (lw, color, spacing)) in border_vals.into_iter().enumerate() { + if let Some(bd) = Self::ts_border_of(c, b as u8) { + if let Some(v) = lw { + bd.line_weight = acadrust::types::LineWeight::from_value(v); + } + if let Some(v) = color { + bd.color = acadrust::types::Color::from_index(v); + } + if let Some(v) = spacing { + bd.double_line_spacing = v; + } + } + } self.push_undo_snapshot(i, "TABLESTYLE EDIT"); self.tabs[i].dirty = true; self.tabs[i].scene.bump_geometry(); @@ -7115,6 +7221,23 @@ impl OpenCADStudio { } } + /// Mutable access to a cell's border by index + /// (0=left 1=right 2=top 3=bottom 4=horizontal-inside 5=vertical-inside). + fn ts_border_of( + c: &mut acadrust::objects::RowCellStyle, + border: u8, + ) -> Option<&mut acadrust::objects::TableCellBorder> { + match border { + 0 => Some(&mut c.left_border), + 1 => Some(&mut c.right_border), + 2 => Some(&mut c.top_border), + 3 => Some(&mut c.bottom_border), + 4 => Some(&mut c.horizontal_inside_border), + 5 => Some(&mut c.vertical_inside_border), + _ => None, + } + } + /// Populate margin + per-cell edit buffers from the selected table style. fn load_tablestyle_bufs(&mut self, tab: usize) { use acadrust::objects::ObjectType; @@ -7133,6 +7256,7 @@ impl OpenCADStudio { }; self.ts_hmargin = format!("{:.4}", s.horizontal_margin); self.ts_vmargin = format!("{:.4}", s.vertical_margin); + self.ts_description = s.description.clone(); for (r, c) in [&s.data_row_style, &s.header_row_style, &s.title_row_style] .into_iter() .enumerate() @@ -7143,6 +7267,23 @@ impl OpenCADStudio { c.text_color.index().map(|v| v.to_string()).unwrap_or_default(); self.ts_cell_fillcolor[r] = c.fill_color.index().map(|v| v.to_string()).unwrap_or_default(); + self.ts_cell_datatype[r] = c.data_type.to_string(); + self.ts_cell_unittype[r] = c.unit_type.to_string(); + self.ts_cell_format[r] = c.format_string.clone(); + let borders = [ + &c.left_border, + &c.right_border, + &c.top_border, + &c.bottom_border, + &c.horizontal_inside_border, + &c.vertical_inside_border, + ]; + for (b, bd) in borders.into_iter().enumerate() { + self.ts_border_lw[r][b] = bd.line_weight.value().to_string(); + self.ts_border_color[r][b] = + bd.color.index().map(|v| v.to_string()).unwrap_or_default(); + self.ts_border_spacing[r][b] = format!("{:.4}", bd.double_line_spacing); + } } } diff --git a/src/app/view.rs b/src/app/view.rs index 7f3075453..1717b5fd0 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -119,10 +119,17 @@ impl OpenCADStudio { selected_style, &self.ts_hmargin, &self.ts_vmargin, + &self.ts_description, &self.ts_cell_textstyle, &self.ts_cell_height, &self.ts_cell_textcolor, &self.ts_cell_fillcolor, + &self.ts_cell_datatype, + &self.ts_cell_unittype, + &self.ts_cell_format, + &self.ts_border_lw, + &self.ts_border_color, + &self.ts_border_spacing, ); } if Some(window_id) == self.mlstyle_window { diff --git a/src/ui/tablestyle.rs b/src/ui/tablestyle.rs index 42199277b..5a478f236 100644 --- a/src/ui/tablestyle.rs +++ b/src/ui/tablestyle.rs @@ -2,7 +2,8 @@ use crate::app::Message; use iced::widget::{ - button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space, + button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Column, + Space, }; use iced::{Background, Border, Color, Element, Fill, Theme}; @@ -133,10 +134,17 @@ pub fn view_window<'a>( selected_style: Option<&'a acadrust::objects::TableStyle>, hmargin_buf: &'a str, vmargin_buf: &'a str, + description_buf: &'a str, cell_textstyle: &'a [String; 3], cell_height: &'a [String; 3], cell_textcolor: &'a [String; 3], cell_fillcolor: &'a [String; 3], + cell_datatype: &'a [String; 3], + cell_unittype: &'a [String; 3], + cell_format: &'a [String; 3], + border_lw: &'a [[String; 6]; 3], + border_color: &'a [[String; 6]; 3], + border_spacing: &'a [[String; 6]; 3], ) -> Element<'a, Message> { // ── Toolbar ─────────────────────────────────────────────────────────── let toolbar = container( @@ -235,48 +243,99 @@ pub fn view_window<'a>( .align_y(iced::Center) .into() }; - column![ - text(row_label).size(11).color(ACCENT), - cell_in(" Text style:", "Standard", &cell_textstyle[r], "textstyle"), - cell_in(" Text height:", "0.18", &cell_height[r], "height"), - cell_in(" Text color (ACI):", "256", &cell_textcolor[r], "textcolor"), - cell_in(" Fill color (ACI):", "256", &cell_fillcolor[r], "fillcolor"), - row![ - text(" Alignment:").size(11).color(DIM).width(150), - 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), - checkbox(rs.fill_enabled) - .label(" Background fill enabled") - .on_toggle(move |_| Message::TableStyleCellToggleFill(row)) - .size(14) - .text_size(11), + let mut col = Column::new() + .spacing(3) + .push(text(row_label).size(11).color(ACCENT)) + .push(cell_in(" Text style:", "Standard", &cell_textstyle[r], "textstyle")) + .push(cell_in(" Text height:", "0.18", &cell_height[r], "height")) + .push(cell_in(" Text color (ACI):", "256", &cell_textcolor[r], "textcolor")) + .push(cell_in(" Fill color (ACI):", "256", &cell_fillcolor[r], "fillcolor")) + .push( + row![ + text(" Alignment:").size(11).color(DIM).width(150), + 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), + ) + .push( + checkbox(rs.fill_enabled) + .label(" Background fill enabled") + .on_toggle(move |_| Message::TableStyleCellToggleFill(row)) + .size(14) + .text_size(11), + ) + .push(cell_in(" Data type:", "0", &cell_datatype[r], "datatype")) + .push(cell_in(" Unit type:", "0", &cell_unittype[r], "unittype")) + .push(cell_in(" Format string:", "", &cell_format[r], "format")) + .push(text(" Borders (type / weight / color / spacing / hidden)").size(10).color(DIM)); + + let borders: [(&'static str, &acadrust::objects::TableCellBorder); 6] = [ + ("L", &rs.left_border), + ("R", &rs.right_border), + ("T", &rs.top_border), + ("B", &rs.bottom_border), + ("H", &rs.horizontal_inside_border), + ("V", &rs.vertical_inside_border), + ]; + for (b, (bname, bd)) in borders.into_iter().enumerate() { + let bu = b as u8; + col = col.push( + row![ + text(format!(" {bname}")).size(11).color(DIM).width(28), + pick_list( + ["Single", "Double"].iter().map(|s| s.to_string()).collect::>(), + Some(format!("{:?}", bd.border_type)), + move |value| Message::TableStyleBorderSetType { cell: row, border: bu, value }, + ) + .text_size(10) + .width(74), + text_input("wt", &border_lw[r][b]) + .on_input(move |v| Message::TableStyleBorderEdit { cell: row, border: bu, field: "lw", value: v }) + .size(10) + .width(46), + text_input("clr", &border_color[r][b]) + .on_input(move |v| Message::TableStyleBorderEdit { cell: row, border: bu, field: "color", value: v }) + .size(10) + .width(46), + text_input("gap", &border_spacing[r][b]) + .on_input(move |v| Message::TableStyleBorderEdit { cell: row, border: bu, field: "spacing", value: v }) + .size(10) + .width(46), + checkbox(bd.is_invisible) + .on_toggle(move |_| Message::TableStyleBorderToggleInvisible { cell: row, border: bu }) + .size(13), + ] + .spacing(5) + .align_y(iced::Center), + ); + } + + col.push( button(text("Apply cell").size(11)) .on_press(Message::TableStyleCellApply(row)) .style(btn_s(true)) .padding([4, 12]), - ] - .spacing(3) + ) .into() }; @@ -284,6 +343,27 @@ pub fn view_window<'a>( scrollable( column![ info_row("Name:", s.name.clone()), + row![ + text("Description:").size(11).color(DIM).width(160), + text_input("", description_buf) + .on_input(|v| Message::TableStyleEdit { field: "description", value: v }) + .size(11) + .width(160), + ] + .spacing(8) + .align_y(iced::Center), + row![ + text("Flow direction:").size(11).color(DIM).width(160), + pick_list( + ["Down", "Up"].iter().map(|s| s.to_string()).collect::>(), + Some(format!("{:?}", s.flow_direction)), + Message::TableStyleSetFlow, + ) + .text_size(11) + .width(100), + ] + .spacing(8) + .align_y(iced::Center), checkbox(s.annotative) .label("Annotative") .on_toggle(|_| Message::TableStyleToggleAnnotative)