fix(styles): reload Table/MLine buffers on structural ops, validate cell text style

style_new/copy/delete funneled through a load_style_bufs no-op for Table/MLine, leaving stale editor buffers pointed at the fresh style; both arms now route to the existing loaders. Table cell text-style typos persisted as dangling names; unknown names are now rejected with command-line feedback, keeping the buffer text for correction. Silent numeric parse-drops deliberately untouched (codebase-wide keep-old-valid norm). Tests: table_new_reloads_cell_buffers plus table_cell_unknown_text_style_is_rejected incl. retention assert (RED each verified by temporary revert). Full suite: 2048 passed / 0 failed / 25 ignored, zero warnings.
This commit is contained in:
Karim Jerbi committed 2026-10-11 02:17:18 +01:00
1 parent 2c2bb9bba5
commit 2824b2943f
3 files changed
+79 -3

No files matched your search

+2 -1
View File
@@ -309,7 +309,8 @@ impl OpenCADStudio {
StyleKind::Text => self.load_textstyle_bufs(i),
StyleKind::Dim => self.load_dimstyle_bufs(i),
StyleKind::MLeader => self.load_mleaderstyle_bufs(i),
StyleKind::Table | StyleKind::MLine => {}
StyleKind::Table => self.load_tablestyle_bufs(i),
StyleKind::MLine => self.load_mlstyle_bufs(i),
}
}
+62
View File
@@ -4924,6 +4924,68 @@ mod tablestyle_ribbon_tests {
}
}
#[cfg(test)]
mod tablestyle_buffer_tests {
// #53: New/Copy/Delete must reload Table/MLine buffers (no stale edits
// into the fresh style), and cell text-style typos must not persist.
use crate::app::{OpenCADStudio, StyleKind};
#[test]
fn table_new_reloads_cell_buffers() {
let mut app = OpenCADStudio::new_for_test();
app.automation_op(r#"{"op":"new"}"#);
app.ts_cell_textstyle[0] = "STALE_SENTINEL".to_string();
app.style_new(StyleKind::Table);
assert_ne!(
app.ts_cell_textstyle[0], "STALE_SENTINEL",
"buffers must reload from the fresh style"
);
}
#[test]
fn table_cell_unknown_text_style_is_rejected() {
let mut app = OpenCADStudio::new_for_test();
app.automation_op(r#"{"op":"new"}"#);
let i = app.active_tab;
let style_name = app
.tabs[i]
.scene
.document
.objects
.values()
.find_map(|o| match o {
codec::objects::ObjectType::TableStyle(s) => Some(s.name.clone()),
_ => None,
})
.expect("fresh doc ships a table style");
app.tablestyle_selected = style_name.clone();
app.ts_cell_textstyle[0] = "NOPE".to_string();
let before = app.command_line.history.len();
let _ = app.on_table_style_cell_apply(0);
let cell_name = app.tabs[i]
.scene
.document
.objects
.values()
.find_map(|o| match o {
codec::objects::ObjectType::TableStyle(s) if s.name == style_name => {
Some(s.data_row_style.text_style_name.clone())
}
_ => None,
})
.unwrap();
assert_ne!(cell_name, "NOPE", "dangling name must not persist");
assert_eq!(
app.ts_cell_textstyle[0], "NOPE",
"rejected text stays in the buffer for correction"
);
assert!(
app.command_line.history.len() > before,
"rejection must say something"
);
}
}
#[cfg(test)]
mod plot_style_ctb_guard_tests {
use crate::app::OpenCADStudio;
+15 -2
View File
@@ -692,12 +692,20 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
self.ts_border_spacing[r][b].trim().parse().ok(),
)
});
// Reject dangling references: a typo must not persist as a text
// style that does not exist (#53). Decided up front so the
// cell borrow below does not conflict with command-line I/O.
let ts_known = ts.is_empty()
|| self.tabs[i].scene.document.text_styles.iter().any(|s| {
s.name.eq_ignore_ascii_case(&ts)
});
let ts_rejected = !ts.is_empty() && !ts_known;
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 !ts.is_empty() && ts_known {
c.text_style_name = ts.clone();
}
if let Some(h) = height {
c.text_height = h;
@@ -729,6 +737,11 @@ pub(super) fn on_text_style_dialog_open(&mut self) -> Task<Message> {
}
}
}
if ts_rejected {
self.command_line.push_error(
crate::tf!("Unknown text style '{ts}'.").as_ref(),
);
}
Task::none()
}