forked from 1b-agent/opencadstudio-upstream
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:
1 parent
2c2bb9bba5
commit
2824b2943f
3 files changed
+79
-3
No files matched your search
@@ -309,7 +309,8 @@ impl OpenCADStudio {
|
|||||||
StyleKind::Text => self.load_textstyle_bufs(i),
|
StyleKind::Text => self.load_textstyle_bufs(i),
|
||||||
StyleKind::Dim => self.load_dimstyle_bufs(i),
|
StyleKind::Dim => self.load_dimstyle_bufs(i),
|
||||||
StyleKind::MLeader => self.load_mleaderstyle_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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)]
|
#[cfg(test)]
|
||||||
mod plot_style_ctb_guard_tests {
|
mod plot_style_ctb_guard_tests {
|
||||||
use crate::app::OpenCADStudio;
|
use crate::app::OpenCADStudio;
|
||||||
|
|||||||
+15
-2
@@ -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(),
|
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
|
if let Some(c) = self
|
||||||
.tablestyle_mut(i)
|
.tablestyle_mut(i)
|
||||||
.and_then(|s| Self::ts_cell_of(s, row))
|
.and_then(|s| Self::ts_cell_of(s, row))
|
||||||
{
|
{
|
||||||
if !ts.is_empty() {
|
if !ts.is_empty() && ts_known {
|
||||||
c.text_style_name = ts;
|
c.text_style_name = ts.clone();
|
||||||
}
|
}
|
||||||
if let Some(h) = height {
|
if let Some(h) = height {
|
||||||
c.text_height = h;
|
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()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user