forked from 1b-agent/opencadstudio-upstream
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:
1 parent
e0c5db9e0c
commit
df44031ae8
4 files changed
+221
-20
No files matched your search
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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]),
|
||||
|
||||
Reference in new issue
Block a user