refactor(app): single-source CHPROP/MATCHPROP 4-step recipe via apply_property_op

Extract the push-undo → per-handle → invalidate → dirty → refresh
contract into one helper on OpenCADStudio. The helper is the single
source of truth for property-op side effects, so future property
handlers can't forget the cache invalidation or the panel refresh.

Note: MATCHPROP now no-ops when the destination selection is empty
(previously it marked the tab dirty and refreshed the panel). This is
a minor correctness improvement; restore the old behaviour with a
2-line guard at the MATCHPROP call site if desired.

test app::properties::apply_property_op_tests::empty_handles_does_not_invoke_or_dirty
test app::properties::apply_property_op_tests::applies_to_each_handle_sets_dirty_and_mutates
test app::properties::apply_property_op_tests::missing_entity_does_not_abort_loop
This commit is contained in:
Karim Jerbi committed 2026-08-27 23:24:32 +01:00
1 parent dffae3f16d
commit 7beb729d85
5 files changed
+247 -157

No files matched your search

+22 -22
View File
@@ -633,11 +633,20 @@ impl OpenCADStudio {
"CHPROP: invalid value '{}' for {}.",
value, prop
).as_ref());
} else {
let known = matches!(
prop.as_str(),
"LAYER" | "LINETYPE" | "LT" | "LTSCALE" | "COLOR" | "TRANSPARENCY"
);
if !known {
self.command_line.push_error(crate::tf!(
"CHPROP: unknown property '{}'. Use: LAYER COLOR LINETYPE LTSCALE TRANSPARENCY", prop
).as_ref());
} else {
let mut changed = 0usize;
for handle in &handles {
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
if let Some(entity) =
self.tabs[i].scene.document.get_entity_mut(*handle)
app.tabs[i].scene.document.get_entity_mut(handle)
{
let common = entity.common_mut();
match prop.as_str() {
@@ -661,30 +670,21 @@ impl OpenCADStudio {
common.transparency = transparency_val.unwrap();
changed += 1;
}
_ => {
self.command_line.push_error(crate::tf!(
"CHPROP: unknown property '{}'. Use: LAYER COLOR LINETYPE LTSCALE TRANSPARENCY", prop
).as_ref());
break;
}
_ => unreachable!(),
}
}
}
if changed > 0 {
self.push_undo_snapshot(i, "CHPROP");
self.tabs[i].dirty = true;
// Colour / linetype / ltscale / transparency /
// layer are baked into the cached wire geometry —
// re-tessellate the changed entities so they
// repaint immediately (issue #231 class).
self.invalidate_property_targets(i, &handles);
self.command_line.push_output(crate::tf!(
"CHPROP: {} entity/entities updated.",
changed
).as_ref());
}
});
// Colour / linetype / ltscale / transparency /
// layer are baked into the cached wire geometry —
// re-tessellate the changed entities so they
// repaint immediately (issue #231 class).
self.command_line.push_output(crate::tf!(
"CHPROP: {} entity/entities updated.",
changed
).as_ref());
}
}
}
}
}