feat(tablestyle): per-cell editing for Data/Header/Title rows

Each row's RowCellStyle is now editable in the Table Style manager:
text style name, text height, text & fill color (ACI), background-fill
toggle, and alignment (cycle through the 9 anchor positions). Margin and
per-cell buffers load on open/select via a shared load_tablestyle_bufs;
edits write through objects.values_mut() and bump geometry so tables
using the style re-render.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-03 20:28:58 +03:00
1 parent e0c5db9e0c
commit df44031ae8
4 files changed
+221 -20

No files matched your search

+21
View File
@@ -315,6 +315,11 @@ pub(super) struct OpenCADStudio {
/// Edit buffers for the table style's general margins.
ts_hmargin: String,
ts_vmargin: 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],
// ── TextStyle Font Browser ────────────────────────────────────────────
textstyle_selected: String,
@@ -1013,6 +1018,18 @@ pub enum Message {
TableStyleEdit { field: &'static str, value: String },
/// Write the general edit buffers back into the selected table style.
TableStyleApply,
/// Update a per-cell edit buffer (row 0=Data,1=Header,2=Title).
TableStyleCellEdit {
row: u8,
field: &'static str,
value: String,
},
/// Toggle background fill on a cell style.
TableStyleCellToggleFill(u8),
/// Cycle the alignment of a cell style.
TableStyleCellCycleAlign(u8),
/// Write a cell's edit buffers back into the selected table style.
TableStyleCellApply(u8),
// ── MLineStyle Dialog ─────────────────────────────────────────────────
MlStyleDialogOpen,
#[allow(dead_code)]
@@ -1202,6 +1219,10 @@ 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()],
// MLineStyle dialog
mlstyle_selected: "Standard".to_string(),
// MLeaderStyle dialog
+142 -9
View File
@@ -5724,6 +5724,7 @@ impl OpenCADStudio {
}
})
.unwrap_or_else(|| "Standard".to_string());
self.load_tablestyle_bufs(i);
if let Some(id) = self.tablestyle_window {
return window::gain_focus(id);
}
@@ -5744,17 +5745,9 @@ impl OpenCADStudio {
}
}
Message::TableStyleDialogSelect(name) => {
use acadrust::objects::ObjectType;
self.tablestyle_selected = name;
let i = self.active_tab;
for obj in self.tabs[i].scene.document.objects.values() {
if let ObjectType::TableStyle(s) = obj {
if s.name == self.tablestyle_selected {
self.ts_hmargin = format!("{:.4}", s.horizontal_margin);
self.ts_vmargin = format!("{:.4}", s.vertical_margin);
}
}
}
self.load_tablestyle_bufs(i);
Task::none()
}
@@ -5790,6 +5783,87 @@ impl OpenCADStudio {
Task::none()
}
Message::TableStyleCellEdit { row, field, value } => {
let r = row as usize;
if r < 3 {
match field {
"textstyle" => self.ts_cell_textstyle[r] = value,
"height" => self.ts_cell_height[r] = value,
"textcolor" => self.ts_cell_textcolor[r] = value,
"fillcolor" => self.ts_cell_fillcolor[r] = value,
_ => {}
}
}
Task::none()
}
Message::TableStyleCellToggleFill(row) => {
let i = self.active_tab;
if let Some(s) = self.tablestyle_mut(i) {
if let Some(c) = Self::ts_cell_of(s, row) {
c.fill_enabled = !c.fill_enabled;
}
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry();
}
Task::none()
}
Message::TableStyleCellCycleAlign(row) => {
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,
};
}
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry();
}
Task::none()
}
Message::TableStyleCellApply(row) => {
let i = self.active_tab;
let r = row as usize;
if r >= 3 {
return Task::none();
}
let ts = self.ts_cell_textstyle[r].trim().to_string();
let height: Option<f64> = self.ts_cell_height[r].trim().parse().ok();
let tc: Option<i16> = self.ts_cell_textcolor[r].trim().parse().ok();
let fc: Option<i16> = self.ts_cell_fillcolor[r].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;
}
if let Some(h) = height {
c.text_height = h;
}
if let Some(v) = tc {
c.text_color = acadrust::types::Color::from_index(v);
}
if let Some(v) = fc {
c.fill_color = acadrust::types::Color::from_index(v);
}
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry();
}
Task::none()
}
Message::TableStyleToggle(field) => {
use acadrust::objects::ObjectType;
let i = self.active_tab;
@@ -6844,6 +6918,65 @@ impl OpenCADStudio {
Some(task)
}
/// Mutable access to the currently selected table style.
fn tablestyle_mut(&mut self, tab: usize) -> Option<&mut acadrust::objects::TableStyle> {
use acadrust::objects::ObjectType;
let name = self.tablestyle_selected.clone();
self.tabs[tab]
.scene
.document
.objects
.values_mut()
.find_map(|o| match o {
ObjectType::TableStyle(s) if s.name == name => Some(s),
_ => None,
})
}
/// Mutable access to a table style's cell style by row (0=Data,1=Header,2=Title).
fn ts_cell_of(
s: &mut acadrust::objects::TableStyle,
row: u8,
) -> Option<&mut acadrust::objects::RowCellStyle> {
match row {
0 => Some(&mut s.data_row_style),
1 => Some(&mut s.header_row_style),
2 => Some(&mut s.title_row_style),
_ => None,
}
}
/// Populate margin + per-cell edit buffers from the selected table style.
fn load_tablestyle_bufs(&mut self, tab: usize) {
use acadrust::objects::ObjectType;
let name = self.tablestyle_selected.clone();
let Some(s) = self.tabs[tab]
.scene
.document
.objects
.values()
.find_map(|o| match o {
ObjectType::TableStyle(s) if s.name == name => Some(s),
_ => None,
})
else {
return;
};
self.ts_hmargin = format!("{:.4}", s.horizontal_margin);
self.ts_vmargin = format!("{:.4}", s.vertical_margin);
for (r, c) in [&s.data_row_style, &s.header_row_style, &s.title_row_style]
.into_iter()
.enumerate()
{
self.ts_cell_textstyle[r] = c.text_style_name.clone();
self.ts_cell_height[r] = format!("{:.4}", c.text_height);
self.ts_cell_textcolor[r] =
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();
}
}
/// Mutable access to the currently selected multileader style.
fn mleaderstyle_mut(
&mut self,
+4
View File
@@ -119,6 +119,10 @@ impl OpenCADStudio {
selected_style,
&self.ts_hmargin,
&self.ts_vmargin,
&self.ts_cell_textstyle,
&self.ts_cell_height,
&self.ts_cell_textcolor,
&self.ts_cell_fillcolor,
);
}
if Some(window_id) == self.mlstyle_window {
+54 -11
View File
@@ -131,6 +131,10 @@ pub fn view_window<'a>(
selected_style: Option<&'a acadrust::objects::TableStyle>,
hmargin_buf: &'a str,
vmargin_buf: &'a str,
cell_textstyle: &'a [String; 3],
cell_height: &'a [String; 3],
cell_textcolor: &'a [String; 3],
cell_fillcolor: &'a [String; 3],
) -> Element<'a, Message> {
// ── Toolbar ───────────────────────────────────────────────────────────
let toolbar = container(
@@ -208,17 +212,56 @@ pub fn view_window<'a>(
.into()
};
let row_info =
|row_label: &'static str, rs: &acadrust::objects::RowCellStyle| -> Element<'_, Message> {
column![
text(row_label).size(11).color(ACCENT),
info_row(" Text Style:", rs.text_style_name.clone()),
info_row(" Text Height:", format!("{:.4}", rs.text_height)),
info_row(" Alignment:", format!("{:?}", rs.alignment)),
let cell_editor = |row_label: &'static str,
row: u8,
rs: &acadrust::objects::RowCellStyle|
-> Element<'a, Message> {
let r = row as usize;
let cell_in = |label: &'static str,
placeholder: &'static str,
value: &'a str,
field: &'static str|
-> Element<'a, Message> {
row![
text(label).size(11).color(DIM).width(150),
text_input(placeholder, value)
.on_input(move |v| Message::TableStyleCellEdit { row, field, value: v })
.size(11)
.width(100),
]
.spacing(3)
.spacing(8)
.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),
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]),
]
.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),
button(text("Apply cell").size(11))
.on_press(Message::TableStyleCellApply(row))
.style(btn_s(true))
.padding([4, 12]),
]
.spacing(3)
.into()
};
let details: Element<'_, Message> = if let Some(s) = selected_style {
scrollable(
@@ -261,9 +304,9 @@ pub fn view_window<'a>(
.on_press(Message::TableStyleApply)
.style(btn_s(true))
.padding([4, 12]),
row_info("Data Row:", &s.data_row_style),
row_info("Header Row:", &s.header_row_style),
row_info("Title Row:", &s.title_row_style),
cell_editor("Data Row:", 0, &s.data_row_style),
cell_editor("Header Row:", 1, &s.header_row_style),
cell_editor("Title Row:", 2, &s.title_row_style),
]
.spacing(6)
.padding([12, 12]),