fix(matchprop): match text style and height between TEXT/MTEXT objects (#361)

MATCHPROP only copied the generic properties (layer, color, linetype,
linetype scale, lineweight), so matching text to text left the
destination's style and height unchanged. Copy the source's text style
and height to TEXT and MTEXT destinations, in both directions, like
AutoCAD's "Text" special property.

Adds an in-process regression test that drives the real interactive
command path (source pick -> destination selection) and fails on the
old code with the reported symptom.

Fixes #361

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Kevin GriffinandClaude Fable 5 committed 2026-07-14 18:22:48 -07:00
1 parent 0907d2912d
commit e35c557f64
2 files changed
+89

No files matched your search

+63
View File
@@ -541,6 +541,69 @@ mod tests {
);
}
#[test]
fn matchprop_matches_text_style_and_height() {
// MATCHPROP between text objects must carry the text-specific
// properties (style, height) to TEXT and MTEXT destinations, not just
// the generic layer/color/linetype set. Regression for #361.
use crate::command::StepInput;
use acadrust::{EntityType, MText, Text};
let mut app = OpenCADStudio::new_for_test();
app.automation_op(r#"{"op":"new"}"#);
let i = app.active_tab;
let mut src = Text::new();
src.value = "SRC".into();
src.height = 5.0;
src.style = "BIG".into();
let src_h = app.tabs[i].scene.add_entity(EntityType::Text(src));
let mut dst_text = Text::new();
dst_text.value = "DST".into();
dst_text.height = 1.0;
let dst_text_h = app.tabs[i].scene.add_entity(EntityType::Text(dst_text));
let mut dst_mtext = MText::new();
dst_mtext.value = "DSTM".into();
dst_mtext.height = 2.0;
let dst_mtext_h = app.tabs[i].scene.add_entity(EntityType::MText(dst_mtext));
// Drive the interactive command exactly as the viewport does:
// phase 1 source pick, phase 2 destination selection.
let _ = app.run_command_line("MATCHPROP");
assert!(app.tabs[i].active_cmd.is_some(), "MATCHPROP must start");
let _ = app.feed_command(StepInput::EntityPick(src_h, glam::DVec3::ZERO));
let _ = app.feed_command(StepInput::SelectionComplete(vec![
dst_text_h,
dst_mtext_h,
]));
let doc = &app.tabs[i].scene.document;
match doc.get_entity(dst_text_h) {
Some(EntityType::Text(t)) => {
assert_eq!(t.style, "BIG", "TEXT destination must take source style");
assert!(
(t.height - 5.0).abs() < 1e-9,
"TEXT destination must take source height, got {}",
t.height
);
}
other => panic!("dest TEXT missing: {other:?}"),
}
match doc.get_entity(dst_mtext_h) {
Some(EntityType::MText(m)) => {
assert_eq!(m.style, "BIG", "MTEXT destination must take source style");
assert!(
(m.height - 5.0).abs() < 1e-9,
"MTEXT destination must take source height, got {}",
m.height
);
}
other => panic!("dest MTEXT missing: {other:?}"),
}
}
#[test]
fn save_then_open_round_trips() {
let mut app = OpenCADStudio::new_for_test();
+26
View File
@@ -759,6 +759,19 @@ impl OpenCADStudio {
c.line_weight,
)
});
// Text-specific properties travel between text-like objects
// (TEXT ↔ MTEXT, both directions), like AutoCAD's "Text"
// special property (#361).
let text_props =
self.tabs[i]
.scene
.document
.get_entity(src)
.and_then(|e| match e {
acadrust::EntityType::Text(t) => Some((t.style.clone(), t.height)),
acadrust::EntityType::MText(m) => Some((m.style.clone(), m.height)),
_ => None,
});
if let Some((layer, color, linetype, lt_scale, lw)) = props {
self.push_undo_snapshot(i, "MATCHPROP");
@@ -769,6 +782,19 @@ impl OpenCADStudio {
crate::scene::view::dispatch::apply_line_weight(e, lw);
e.common_mut().linetype = linetype.clone();
e.common_mut().linetype_scale = lt_scale;
if let Some((style, height)) = &text_props {
match e {
acadrust::EntityType::Text(t) => {
t.style = style.clone();
t.height = *height;
}
acadrust::EntityType::MText(m) => {
m.style = style.clone();
m.height = *height;
}
_ => {}
}
}
}
}
self.tabs[i].dirty = true;