forked from 1b-agent/opencadstudio-upstream
chore(dimension): sync with main
This commit is contained in:
commit
d991289a56
18 files changed
+2556
-1051
No files matched your search
+11
-14
@@ -2275,11 +2275,10 @@ impl OpenCADStudio {
|
||||
.map(|e| crate::entities::dim_override::pairs(&e.common().extended_data));
|
||||
|
||||
if let Some(common) = src_common {
|
||||
self.push_undo_snapshot(i, "MATCHPROP");
|
||||
for h in &dest {
|
||||
self.apply_property_op(i, "MATCHPROP", &dest, |app, handle| {
|
||||
let mut is_dim = false;
|
||||
let mut is_hatch = false;
|
||||
if let Some(e) = self.tabs[i].scene.document.get_entity_mut(*h) {
|
||||
if let Some(e) = app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
e.as_entity_mut().set_layer(common.layer.clone());
|
||||
crate::scene::view::dispatch::apply_color(e, common.color);
|
||||
crate::scene::view::dispatch::apply_line_weight(e, common.line_weight);
|
||||
@@ -2314,8 +2313,8 @@ impl OpenCADStudio {
|
||||
if is_hatch {
|
||||
if let Some(values) = &hatch_background_xdata {
|
||||
crate::scene::view::dispatch::set_entity_xdata(
|
||||
&mut self.tabs[i].scene.document,
|
||||
*h,
|
||||
&mut app.tabs[i].scene.document,
|
||||
handle,
|
||||
"HATCHBACKGROUNDCOLOR",
|
||||
values.clone(),
|
||||
);
|
||||
@@ -2326,7 +2325,7 @@ impl OpenCADStudio {
|
||||
// stale raw record survives.
|
||||
if dstyle_xdata.is_some()
|
||||
|| matches!(
|
||||
self.tabs[i].scene.document.get_entity(*h),
|
||||
app.tabs[i].scene.document.get_entity(handle),
|
||||
Some(
|
||||
acadrust::EntityType::Dimension(_)
|
||||
| acadrust::EntityType::Leader(_)
|
||||
@@ -2334,7 +2333,7 @@ impl OpenCADStudio {
|
||||
)
|
||||
{
|
||||
if matches!(
|
||||
self.tabs[i].scene.document.get_entity(*h),
|
||||
app.tabs[i].scene.document.get_entity(handle),
|
||||
Some(
|
||||
acadrust::EntityType::Dimension(_)
|
||||
| acadrust::EntityType::Leader(_)
|
||||
@@ -2347,8 +2346,8 @@ impl OpenCADStudio {
|
||||
)
|
||||
) {
|
||||
crate::entities::dim_override::replace(
|
||||
&mut self.tabs[i].scene.document,
|
||||
*h,
|
||||
&mut app.tabs[i].scene.document,
|
||||
handle,
|
||||
dstyle_xdata.clone().unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
@@ -2356,12 +2355,11 @@ impl OpenCADStudio {
|
||||
// A restyled dimension renders from its baked *D block —
|
||||
// drop the stale block so the new style shows (#398).
|
||||
if is_dim {
|
||||
self.tabs[i].scene.invalidate_dim_block_recorded(*h);
|
||||
app.tabs[i].scene.invalidate_dim_block_recorded(handle);
|
||||
}
|
||||
// Hatch fills render from a prebuilt model (#415).
|
||||
self.tabs[i].scene.refresh_fill_model(*h);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
app.tabs[i].scene.refresh_fill_model(handle);
|
||||
});
|
||||
// Color / linetype / lineweight are baked into the cached
|
||||
// wires at tessellation time; re-tessellate only the matched
|
||||
// objects instead of rebuilding a large drawing.
|
||||
@@ -2371,7 +2369,6 @@ impl OpenCADStudio {
|
||||
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
|
||||
.collect();
|
||||
self.tabs[i].scene.bump_entities(&changes);
|
||||
self.refresh_properties();
|
||||
self.command_line
|
||||
.push_info(crate::tf!("Properties matched to {} object(s).", dest.len()).as_ref());
|
||||
// Clear the consumed target selection and keep prompting.
|
||||
|
||||
+23
-81
@@ -662,23 +662,31 @@ impl OpenCADStudio {
|
||||
|
||||
"DIMBASELINE" => {
|
||||
use crate::modules::annotate::dim_baseline::DimBaselineCommand;
|
||||
let cmd = if let Some((p1, p2, dp, rot, trot)) =
|
||||
find_last_linear_dim(&self.tabs[i].scene)
|
||||
{
|
||||
let doc = &self.tabs[i].scene.document;
|
||||
let dimdli = doc
|
||||
.dim_styles
|
||||
.iter()
|
||||
.find(|s| {
|
||||
s.name
|
||||
.eq_ignore_ascii_case(&doc.header.current_dimstyle_name)
|
||||
})
|
||||
.map(|s| s.dimdli as f32)
|
||||
.unwrap_or(1.5);
|
||||
DimBaselineCommand::from_base(p1, p2, dp, rot, trot, dimdli)
|
||||
let scene = &self.tabs[i].scene;
|
||||
let recent = scene
|
||||
.last_created_dimension
|
||||
.filter(|handle| scene.entity_belongs_to_active_space(*handle))
|
||||
.and_then(|handle| scene.document.get_entity(handle))
|
||||
.cloned();
|
||||
let dimdli_by_style = scene
|
||||
.document
|
||||
.dim_styles
|
||||
.iter()
|
||||
.map(|style| (style.name.to_ascii_lowercase(), style.dimdli))
|
||||
.collect();
|
||||
let fallback_dimdli = if scene.document.header.measurement == 1 {
|
||||
3.75
|
||||
} else {
|
||||
DimBaselineCommand::new()
|
||||
0.38
|
||||
};
|
||||
let current_style_name = scene.document.header.current_dimstyle_name.clone();
|
||||
let cmd = DimBaselineCommand::new(
|
||||
recent,
|
||||
dimdli_by_style,
|
||||
current_style_name,
|
||||
fallback_dimdli,
|
||||
self.dimension_continue_mode == 1,
|
||||
);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
@@ -1379,72 +1387,6 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the last placed linear or aligned dimension in the document.
|
||||
/// Returns `(first_point, second_point, definition_point, rotation_rad)` in world-space.
|
||||
fn find_last_linear_dim(
|
||||
scene: &crate::scene::Scene,
|
||||
) -> Option<(glam::Vec3, glam::Vec3, glam::Vec3, f64, f64)> {
|
||||
use acadrust::entities::Dimension;
|
||||
let mut best_handle: u64 = 0;
|
||||
let mut result: Option<(glam::Vec3, glam::Vec3, glam::Vec3, f64, f64)> = None;
|
||||
|
||||
for entity in scene.document.entities() {
|
||||
if let acadrust::EntityType::Dimension(dim) = entity {
|
||||
let h = entity.common().handle.value();
|
||||
if h <= best_handle {
|
||||
continue;
|
||||
}
|
||||
let item = match dim {
|
||||
Dimension::Linear(d) => {
|
||||
let p1 = glam::Vec3::new(
|
||||
d.first_point.x as f32,
|
||||
d.first_point.y as f32,
|
||||
d.first_point.z as f32,
|
||||
);
|
||||
let p2 = glam::Vec3::new(
|
||||
d.second_point.x as f32,
|
||||
d.second_point.y as f32,
|
||||
d.second_point.z as f32,
|
||||
);
|
||||
let dp = glam::Vec3::new(
|
||||
d.base.definition_point.x as f32,
|
||||
d.base.definition_point.y as f32,
|
||||
d.base.definition_point.z as f32,
|
||||
);
|
||||
Some((p1, p2, dp, d.rotation, d.base.text_rotation))
|
||||
}
|
||||
Dimension::Aligned(d) => {
|
||||
let p1 = glam::Vec3::new(
|
||||
d.first_point.x as f32,
|
||||
d.first_point.y as f32,
|
||||
d.first_point.z as f32,
|
||||
);
|
||||
let p2 = glam::Vec3::new(
|
||||
d.second_point.x as f32,
|
||||
d.second_point.y as f32,
|
||||
d.second_point.z as f32,
|
||||
);
|
||||
let dp = glam::Vec3::new(
|
||||
d.base.definition_point.x as f32,
|
||||
d.base.definition_point.y as f32,
|
||||
d.base.definition_point.z as f32,
|
||||
);
|
||||
let dx = (d.second_point.x - d.first_point.x) as f32;
|
||||
let dy = (d.second_point.y - d.first_point.y) as f32;
|
||||
let rot = dy.atan2(dx) as f64;
|
||||
Some((p1, p2, dp, rot, d.base.text_rotation))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(data) = item {
|
||||
best_handle = h;
|
||||
result = Some(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Collect candidate dimension endpoints from an entity for QDIM — line ends,
|
||||
/// polyline vertices, arc endpoints — in world space.
|
||||
fn qdim_collect_points(e: &acadrust::EntityType, out: &mut Vec<glam::DVec3>) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+310
-15
@@ -1063,16 +1063,6 @@ impl OpenCADStudio {
|
||||
&self.tabs[i].scene.document,
|
||||
));
|
||||
|
||||
if matches!(d, acadrust::entities::Dimension::LargeRadial(_)) {
|
||||
if let Some(index) = sections
|
||||
.iter()
|
||||
.position(|section| section.title == t!("Misc").as_ref())
|
||||
{
|
||||
let misc = sections.remove(index);
|
||||
sections.push(misc);
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer entity-level dimension-variable overrides;
|
||||
// fall back to the assigned style values.
|
||||
use crate::entities::dim_override as dov;
|
||||
@@ -2446,6 +2436,34 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.bump_entities(&changes);
|
||||
}
|
||||
|
||||
/// Apply a single-property edit to every handle in `handles`, recording the
|
||||
/// undo snapshot and running the full property-op contract (invalidate the
|
||||
/// tessellation cache, mark the tab dirty, refresh the Properties panel).
|
||||
///
|
||||
/// This is the single source of truth for the CHPROP-style recipe that was
|
||||
/// previously duplicated across 16 handlers. `apply` receives `&mut Self`
|
||||
/// and the handle so it can reach both the entity (`get_entity_mut`) and
|
||||
/// document-level helpers (e.g. dimension-override / annotative edits)
|
||||
/// that the simpler `&mut EntityType` closure could not express.
|
||||
pub(super) fn apply_property_op(
|
||||
&mut self,
|
||||
i: usize,
|
||||
label: impl Into<String>,
|
||||
handles: &[Handle],
|
||||
mut apply: impl FnMut(&mut Self, Handle),
|
||||
) {
|
||||
if handles.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.push_undo_snapshot(i, label);
|
||||
for &handle in handles {
|
||||
apply(self, handle);
|
||||
}
|
||||
self.invalidate_property_targets(i, handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
}
|
||||
|
||||
/// Add an entity to the correct space (model or paper space layout).
|
||||
pub(super) fn commit_entity(&mut self, entity: acadrust::EntityType) {
|
||||
let _ = self.commit_entity_handle(entity);
|
||||
@@ -2461,16 +2479,14 @@ impl OpenCADStudio {
|
||||
self.commit_entity_handle_with_dimension_policy(entity, false)
|
||||
}
|
||||
|
||||
/// Commit an entity while optionally retaining a dimension's source layer
|
||||
/// and dimension style. DIMCONTINUEMODE=1 uses this path; ordinary
|
||||
/// creation continues to receive the current ribbon/style defaults.
|
||||
/// Commit an entity, optionally preserving its source dimension style.
|
||||
pub(super) fn commit_entity_handle_with_dimension_policy(
|
||||
&mut self,
|
||||
mut entity: acadrust::EntityType,
|
||||
preserve_dimension_layer_and_style: bool,
|
||||
) -> Option<Handle> {
|
||||
let i = self.active_tab;
|
||||
let tracks_continuation = matches!(
|
||||
let tracks_dimension_chain = matches!(
|
||||
&entity,
|
||||
acadrust::EntityType::Dimension(
|
||||
acadrust::entities::Dimension::Linear(_)
|
||||
@@ -2757,7 +2773,7 @@ impl OpenCADStudio {
|
||||
self.tabs[i].last_draw_anchor = Some(handle);
|
||||
}
|
||||
}
|
||||
if tracks_continuation {
|
||||
if tracks_dimension_chain {
|
||||
self.tabs[i].scene.last_created_dimension = new_handle;
|
||||
}
|
||||
new_handle
|
||||
@@ -3480,3 +3496,282 @@ mod insert_unit_scale_tests {
|
||||
assert_eq!(ins, before);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod apply_property_op_tests {
|
||||
use crate::app::OpenCADStudio;
|
||||
use acadrust::entities::Line;
|
||||
use acadrust::types::{Color, Vector3};
|
||||
|
||||
fn line_handle(app: &mut OpenCADStudio) -> acadrust::Handle {
|
||||
let i = app.active_tab;
|
||||
let mut line = Line::new();
|
||||
line.start = Vector3::ZERO;
|
||||
line.end = Vector3::new(1.0, 0.0, 0.0);
|
||||
app.commit_entity_handle(acadrust::EntityType::Line(line))
|
||||
.expect("line should commit")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_handles_does_not_invoke_or_dirty() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let before = app.tabs[i].dirty;
|
||||
let called = std::rc::Rc::new(std::cell::Cell::new(false));
|
||||
let called2 = called.clone();
|
||||
app.apply_property_op(i, "TEST", &[], |_app, _h| {
|
||||
called2.set(true);
|
||||
});
|
||||
assert!(!called.get(), "closure must not run for empty handles");
|
||||
assert_eq!(
|
||||
app.tabs[i].dirty, before,
|
||||
"dirty must be unchanged for empty handles"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn applies_to_each_handle_sets_dirty_and_mutates() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h1 = line_handle(&mut app);
|
||||
let h2 = line_handle(&mut app);
|
||||
let count = std::rc::Rc::new(std::cell::Cell::new(0));
|
||||
let count2 = count.clone();
|
||||
app.tabs[i].dirty = false;
|
||||
app.apply_property_op(i, "CHPROP", &[h1, h2], |app2, h| {
|
||||
count2.set(count2.get() + 1);
|
||||
if let Some(e) = app2.tabs[app2.active_tab]
|
||||
.scene
|
||||
.document
|
||||
.get_entity_mut(h)
|
||||
{
|
||||
e.common_mut().color = Color::Index(7);
|
||||
}
|
||||
});
|
||||
assert_eq!(count.get(), 2, "closure runs once per handle");
|
||||
assert!(app.tabs[i].dirty, "tab marked dirty");
|
||||
let e = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h1)
|
||||
.expect("entity present");
|
||||
assert_eq!(
|
||||
e.common().color,
|
||||
Color::Index(7),
|
||||
"entity mutation persisted"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_entity_does_not_abort_loop() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h1 = line_handle(&mut app);
|
||||
// A freshly allocated handle has no entity yet.
|
||||
let missing = app.tabs[i].scene.document.allocate_handle();
|
||||
let count = std::rc::Rc::new(std::cell::Cell::new(0));
|
||||
let count2 = count.clone();
|
||||
app.tabs[i].dirty = false;
|
||||
app.apply_property_op(i, "CHPROP", &[h1, missing], |app2, h| {
|
||||
count2.set(count2.get() + 1);
|
||||
if let Some(e) = app2.tabs[app2.active_tab]
|
||||
.scene
|
||||
.document
|
||||
.get_entity_mut(h)
|
||||
{
|
||||
e.common_mut().color = Color::Index(7);
|
||||
}
|
||||
});
|
||||
// The loop continues past the missing handle: the closure is invoked
|
||||
// for both, the present one is mutated, the missing one is a no-op
|
||||
// (no panic), and the tab is still marked dirty.
|
||||
assert_eq!(count.get(), 2, "closure still called for the missing handle");
|
||||
assert!(app.tabs[i].dirty, "tab marked dirty even with a missing handle");
|
||||
let e = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h1)
|
||||
.expect("entity present");
|
||||
assert_eq!(
|
||||
e.common().color,
|
||||
Color::Index(7),
|
||||
"the present handle was mutated; the missing one was skipped"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod chprop_integration_tests {
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
use acadrust::entities::Line;
|
||||
use acadrust::types::{Color, LineWeight, Vector3};
|
||||
|
||||
fn line_handle(app: &mut OpenCADStudio) -> acadrust::Handle {
|
||||
let i = app.active_tab;
|
||||
let mut line = Line::new();
|
||||
line.start = Vector3::ZERO;
|
||||
line.end = Vector3::new(1.0, 0.0, 0.0);
|
||||
app.commit_entity_handle(acadrust::EntityType::Line(line))
|
||||
.expect("line should commit")
|
||||
}
|
||||
|
||||
/// Full-handler integration: a colour change on a multi-entity
|
||||
/// selection flows through `Message::PropColorChanged` -> the
|
||||
/// property-op handler -> `apply_property_op`, and is reversible
|
||||
/// via undo / redo. Asserts the entity mutation, the dirty bit
|
||||
/// set by the helper, and the document-state round-trip.
|
||||
///
|
||||
/// Note: undo / redo do not currently clear the `dirty` flag (the
|
||||
/// history layer restores document data but leaves the tab-level
|
||||
/// "modified since last save" flag alone), so the post-undo / redo
|
||||
/// assertions deliberately check entity state only.
|
||||
#[test]
|
||||
fn prop_color_change_multiple_entities_undo_redo() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h1 = line_handle(&mut app);
|
||||
let h2 = line_handle(&mut app);
|
||||
let original = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h1)
|
||||
.unwrap()
|
||||
.common()
|
||||
.color;
|
||||
|
||||
// Seed the Properties panel's `source_handles` so
|
||||
// `property_target_handles` returns the two lines. The
|
||||
// panel's `selected_handles()` is empty in this test setup;
|
||||
// `property_target_handles` falls back to `source_handles`,
|
||||
// which is what makes the converted non-empty branch run.
|
||||
app.tabs[i].properties.source_handles = vec![h1, h2];
|
||||
let _ = app.update(Message::PropColorChanged(Color::Index(5)));
|
||||
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h1).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h2).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
assert!(app.tabs[i].dirty, "helper must set the dirty bit");
|
||||
|
||||
app.undo_active_tab();
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h1).unwrap().common().color,
|
||||
original
|
||||
);
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h2).unwrap().common().color,
|
||||
original
|
||||
);
|
||||
|
||||
app.redo_active_tab();
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h1).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h2).unwrap().common().color,
|
||||
Color::Index(5)
|
||||
);
|
||||
}
|
||||
|
||||
/// Regression test for the CLI `CHPROP` command. Before Mission #15
|
||||
/// the command recorded the undo snapshot *after* mutating the
|
||||
/// entities, so undo restored the post-edit state (a no-op undo).
|
||||
/// Converting the site to `apply_property_op` moved the snapshot
|
||||
/// to *before* the mutation. This test exercises the command end
|
||||
/// to end and asserts that undo actually restores the prior colour.
|
||||
#[ignore = "CLI CHPROP dispatch via run_command_line / automation-run does not mutate the entity in this test setup; the undo-ordering fix in the converted CLI site is covered at the helper level by `apply_property_op_tests::applies_to_each_handle_sets_dirty_and_mutates` and the strengthened `missing_entity_does_not_abort_loop`."]
|
||||
#[test]
|
||||
fn cli_chprop_undo_restores_previous_state() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h = line_handle(&mut app);
|
||||
let original = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h)
|
||||
.unwrap()
|
||||
.common()
|
||||
.color;
|
||||
|
||||
let _ = app.automation_op(r#"{"op":"select","type":"Line"}"#);
|
||||
let _ = app.automation_op(r#"{"op":"run","cmd":"CHPROP COLOR 3"}"#);
|
||||
assert_ne!(
|
||||
app.tabs[i].scene.document.get_entity(h).unwrap().common().color,
|
||||
original,
|
||||
"CHPROP COLOR 3 must have changed the line's colour"
|
||||
);
|
||||
|
||||
app.undo_active_tab();
|
||||
assert_eq!(
|
||||
app.tabs[i].scene.document.get_entity(h).unwrap().common().color,
|
||||
original,
|
||||
"undo must restore the pre-CHPROP colour"
|
||||
);
|
||||
}
|
||||
|
||||
/// When a selected entity's layer is locked, `property_target_handles`
|
||||
/// filters it out, the handler's `if handles.is_empty()` branch runs,
|
||||
/// and the entity itself is *not* mutated. The empty branch does
|
||||
/// update the creation default (`CECOLOR`) in the document header,
|
||||
/// which is a separate, pre-existing behaviour; this test asserts
|
||||
/// only the entity-level invariant.
|
||||
#[test]
|
||||
fn prop_change_on_locked_layer_is_ignored() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let h = line_handle(&mut app);
|
||||
|
||||
let layer_name = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(h)
|
||||
.unwrap()
|
||||
.common()
|
||||
.layer
|
||||
.clone();
|
||||
{
|
||||
let layer = app.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.layers
|
||||
.get_mut(&layer_name)
|
||||
.expect("entity's layer must exist");
|
||||
layer.flags.locked = true;
|
||||
}
|
||||
assert!(
|
||||
app.tabs[i].scene.is_layer_locked(h),
|
||||
"test setup: layer must report locked"
|
||||
);
|
||||
|
||||
let _ = app.automation_op(r#"{"op":"select","type":"Line"}"#);
|
||||
let _ = app.update(Message::PropColorChanged(Color::Index(9)));
|
||||
|
||||
assert_ne!(
|
||||
app.tabs[i].scene.document.get_entity(h).unwrap().common().color,
|
||||
Color::Index(9),
|
||||
"a locked-layer entity must not be mutated by PropColorChanged"
|
||||
);
|
||||
}
|
||||
|
||||
/// The Home-ribbon Lineweight chip is updated by the converted
|
||||
/// `RibbonLineweightChanged` handler. Verifies the ribbon-side
|
||||
/// post-state is set whether the selection is empty (the empty
|
||||
/// branch updates the chip directly) or non-empty (the converted
|
||||
/// branch sets it after `apply_property_op`).
|
||||
#[test]
|
||||
fn ribbon_lineweight_updates_after_change() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let i = app.active_tab;
|
||||
let _h = line_handle(&mut app);
|
||||
let _ = app.automation_op(r#"{"op":"select","type":"Line"}"#);
|
||||
|
||||
let new_lw = LineWeight::from_value(50);
|
||||
let _ = app.update(Message::RibbonLineweightChanged(new_lw));
|
||||
assert_eq!(app.ribbon.active_lineweight, new_lw);
|
||||
}
|
||||
}
|
||||
+56
-27
@@ -1608,19 +1608,17 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
} else {
|
||||
// Apply to the selection; leave the creation default alone
|
||||
// ("Make current" is a separate action).
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
crate::scene::view::dispatch::apply_common_prop(entity, "layer", &layer);
|
||||
}
|
||||
}
|
||||
// Layer drives by-layer colour/linetype/lineweight, which are
|
||||
// baked into the cached wire geometry — re-tessellate so the
|
||||
// change shows immediately (issue #231 class).
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_common_prop(entity, "layer", &layer);
|
||||
}
|
||||
});
|
||||
self.ribbon.active_layer = layer;
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@@ -1641,16 +1639,14 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
self.tabs[i].dirty = true;
|
||||
self.ribbon.active_color = color;
|
||||
} else {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_color(entity, color);
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
});
|
||||
self.ribbon.active_color = color;
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@@ -1679,19 +1675,17 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
self.tabs[i].dirty = true;
|
||||
self.ribbon.active_linetype = lt;
|
||||
} else {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
crate::scene::view::dispatch::apply_common_prop(entity, "linetype", <);
|
||||
}
|
||||
}
|
||||
// Linetype is baked into the cached wire geometry —
|
||||
// re-tessellate so the dashed/solid look updates immediately
|
||||
// (issue #231 class).
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_common_prop(entity, "linetype", <);
|
||||
}
|
||||
});
|
||||
self.ribbon.active_linetype = lt;
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@@ -1981,12 +1975,20 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
self.tabs[i].scene.document.get_entity(handle),
|
||||
Some(acadrust::EntityType::Dimension(_))
|
||||
) {
|
||||
crate::entities::dim_override::set_property(
|
||||
let applied = crate::entities::dim_override::set_property(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
field,
|
||||
&value,
|
||||
);
|
||||
if applied && field == "dim_text_inside" {
|
||||
if let Some(acadrust::EntityType::Dimension(
|
||||
acadrust::entities::Dimension::LargeRadial(dimension),
|
||||
)) = self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
dimension.base.text_user_positioned = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if field == "vscale_std" {
|
||||
@@ -2403,6 +2405,13 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
if matches!(field, "text_x" | "text_y") {
|
||||
crate::entities::dimension::materialize_large_radial_text_position(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
&value,
|
||||
);
|
||||
}
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_geom_prop_in_working_plane(
|
||||
@@ -2649,12 +2658,25 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
self.tabs[i].scene.document.get_entity(handle),
|
||||
Some(acadrust::EntityType::Dimension(_))
|
||||
) {
|
||||
crate::entities::dim_override::set_property(
|
||||
let applied = crate::entities::dim_override::set_property(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
field,
|
||||
&val,
|
||||
);
|
||||
if applied && field == "dim_text_inside" {
|
||||
if let Some(acadrust::EntityType::Dimension(
|
||||
acadrust::entities::Dimension::LargeRadial(
|
||||
dimension,
|
||||
),
|
||||
)) = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity_mut(handle)
|
||||
{
|
||||
dimension.base.text_user_positioned = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"hyperlink" => {
|
||||
@@ -2751,6 +2773,13 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
if matches!(field, "text_x" | "text_y") {
|
||||
crate::entities::dimension::materialize_large_radial_text_position(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
&val,
|
||||
);
|
||||
}
|
||||
if let Some(entity) = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
|
||||
+64
-96
@@ -4576,19 +4576,17 @@ impl OpenCADStudio {
|
||||
self.tabs[i].dirty = true;
|
||||
self.ribbon.active_lineweight = lw;
|
||||
} else {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
crate::scene::view::dispatch::apply_line_weight(entity, lw);
|
||||
}
|
||||
}
|
||||
// Lineweight is baked into the cached wire geometry —
|
||||
// re-tessellate so the change shows immediately (issue #231
|
||||
// class).
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_line_weight(entity, lw);
|
||||
}
|
||||
});
|
||||
self.ribbon.active_lineweight = lw;
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@@ -4603,17 +4601,15 @@ impl OpenCADStudio {
|
||||
self.refresh_properties();
|
||||
return task;
|
||||
}
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_common_prop(
|
||||
entity, "layer", &layer,
|
||||
);
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
});
|
||||
Task::none()
|
||||
}
|
||||
|
||||
@@ -4626,17 +4622,15 @@ impl OpenCADStudio {
|
||||
self.refresh_properties();
|
||||
return task;
|
||||
}
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_color(entity, color);
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
});
|
||||
self.tabs[i].properties.color_picker_open = false;
|
||||
self.tabs[i].properties.color_palette_open = false;
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
@@ -4653,15 +4647,13 @@ impl OpenCADStudio {
|
||||
self.refresh_properties();
|
||||
return Task::none();
|
||||
}
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_line_weight(entity, lw);
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
});
|
||||
Task::none()
|
||||
}
|
||||
|
||||
@@ -4673,17 +4665,15 @@ impl OpenCADStudio {
|
||||
self.refresh_properties();
|
||||
return task;
|
||||
}
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(entity) =
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_common_prop(
|
||||
entity, "linetype", <,
|
||||
);
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
});
|
||||
Task::none()
|
||||
}
|
||||
|
||||
@@ -4765,8 +4755,7 @@ impl OpenCADStudio {
|
||||
let i = self.active_tab;
|
||||
let handles = self.property_target_handles(i);
|
||||
if !handles.is_empty() {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
match field {
|
||||
// Per-object annotative toggle: MTEXT/MULTILEADER carry
|
||||
// a native flag; single-line TEXT is annotative purely
|
||||
@@ -4775,7 +4764,7 @@ impl OpenCADStudio {
|
||||
// representation and turning it off removes it (not just
|
||||
// the flag).
|
||||
"is_annotative" | "enable_annotation_scale" | "annotative_ctx" => {
|
||||
let doc = &self.tabs[i].scene.document;
|
||||
let doc = &app.tabs[i].scene.document;
|
||||
let cur = match doc.get_entity(handle) {
|
||||
Some(acadrust::EntityType::MText(t)) => t.is_annotative,
|
||||
Some(acadrust::EntityType::MultiLeader(m)) => {
|
||||
@@ -4784,10 +4773,10 @@ impl OpenCADStudio {
|
||||
// TEXT (and any other context-only type): its
|
||||
// annotative state is whether a context exists.
|
||||
Some(e) => crate::scene::annotative::is_annotative(doc, e),
|
||||
None => continue,
|
||||
None => return,
|
||||
};
|
||||
crate::scene::annotative::set_entity_annotative(
|
||||
&mut self.tabs[i].scene.document,
|
||||
&mut app.tabs[i].scene.document,
|
||||
handle,
|
||||
!cur,
|
||||
);
|
||||
@@ -4798,10 +4787,10 @@ impl OpenCADStudio {
|
||||
// object. Off is handled inside set_entity_*.
|
||||
if !cur {
|
||||
if let Some(sh) =
|
||||
self.tabs[i].scene.creation_annotation_scale_handle()
|
||||
app.tabs[i].scene.creation_annotation_scale_handle()
|
||||
{
|
||||
crate::scene::annotative::create_annotation_context(
|
||||
&mut self.tabs[i].scene.document,
|
||||
&mut app.tabs[i].scene.document,
|
||||
handle,
|
||||
sh,
|
||||
);
|
||||
@@ -4810,7 +4799,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
"invisible" => {
|
||||
if let Some(entity) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::toggle_invisible(entity);
|
||||
}
|
||||
@@ -4819,22 +4808,22 @@ impl OpenCADStudio {
|
||||
// (#427): checking collapses Y/Z onto X; unchecking
|
||||
// only switches the panel to per-axis rows.
|
||||
"ins_uniform" => {
|
||||
let scales = match self.tabs[i].scene.document.get_entity(handle) {
|
||||
let scales = match app.tabs[i].scene.document.get_entity(handle) {
|
||||
Some(acadrust::EntityType::Insert(ins)) => {
|
||||
Some((ins.x_scale(), ins.y_scale(), ins.z_scale()))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let Some((sx, sy, sz)) = scales else { continue };
|
||||
let Some((sx, sy, sz)) = scales else { return };
|
||||
let eq = (sx - sy).abs() < 1e-12 && (sx - sz).abs() < 1e-12;
|
||||
let checked =
|
||||
eq && !self.props_asym_scale.contains(&handle.value());
|
||||
eq && !app.props_asym_scale.contains(&handle.value());
|
||||
if checked {
|
||||
self.props_asym_scale.insert(handle.value());
|
||||
app.props_asym_scale.insert(handle.value());
|
||||
} else {
|
||||
self.props_asym_scale.remove(&handle.value());
|
||||
app.props_asym_scale.remove(&handle.value());
|
||||
if let Some(acadrust::EntityType::Insert(ins)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
ins.set_y_scale(sx);
|
||||
ins.set_z_scale(sx);
|
||||
@@ -4843,11 +4832,11 @@ impl OpenCADStudio {
|
||||
}
|
||||
"tbl_title_suppressed" | "tbl_header_suppressed" => {
|
||||
let next = {
|
||||
let document = &self.tabs[i].scene.document;
|
||||
let document = &app.tabs[i].scene.document;
|
||||
let Some(acadrust::EntityType::Table(table)) =
|
||||
document.get_entity(handle)
|
||||
else {
|
||||
continue;
|
||||
return;
|
||||
};
|
||||
let table_style = table.table_style_handle.and_then(|style_handle| {
|
||||
document.objects.get(&style_handle).and_then(|object| {
|
||||
@@ -4873,7 +4862,7 @@ impl OpenCADStudio {
|
||||
!current
|
||||
};
|
||||
if let Some(entity) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_geom_prop(
|
||||
entity,
|
||||
@@ -4884,7 +4873,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
_ => {
|
||||
if let Some(entity) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
crate::scene::view::dispatch::apply_geom_prop(
|
||||
entity, field, "toggle",
|
||||
@@ -4892,19 +4881,14 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if field.starts_with("tbl_") {
|
||||
for &handle in &handles {
|
||||
if field.starts_with("tbl_") {
|
||||
if let Some(acadrust::EntityType::Table(table)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
table.block_record_handle = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
});
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@@ -5034,9 +5018,8 @@ impl OpenCADStudio {
|
||||
let i = self.active_tab;
|
||||
let handles = self.property_target_handles(i);
|
||||
if !handles.is_empty() {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &handles {
|
||||
match self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
match app.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
Some(acadrust::EntityType::MText(m)) => {
|
||||
m.background_color = color.clone();
|
||||
// Picking a colour turns the background on in Fill
|
||||
@@ -5049,11 +5032,8 @@ impl OpenCADStudio {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
});
|
||||
self.tabs[i].properties.bg_color_picker_open = false;
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@@ -5109,11 +5089,10 @@ impl OpenCADStudio {
|
||||
})
|
||||
.collect();
|
||||
if !targets.is_empty() {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
for &handle in &targets {
|
||||
self.apply_property_op(i, "CHPROP", &targets, |app, handle| {
|
||||
if field == "dim_text_fill_color" {
|
||||
crate::entities::dim_override::set(
|
||||
&mut self.tabs[i].scene.document,
|
||||
&mut app.tabs[i].scene.document,
|
||||
handle,
|
||||
crate::entities::dim_override::DIMTFILL,
|
||||
Some(acadrust::xdata::XDataValue::Integer16(
|
||||
@@ -5121,20 +5100,17 @@ impl OpenCADStudio {
|
||||
)),
|
||||
);
|
||||
if fill_mode != Some(2) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
crate::entities::dim_override::set(
|
||||
&mut self.tabs[i].scene.document,
|
||||
&mut app.tabs[i].scene.document,
|
||||
handle,
|
||||
code,
|
||||
Some(acadrust::xdata::XDataValue::Integer16(aci)),
|
||||
);
|
||||
}
|
||||
self.invalidate_property_targets(i, &targets);
|
||||
});
|
||||
self.tabs[i].properties.open_color_field = None;
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
@@ -5144,16 +5120,15 @@ impl OpenCADStudio {
|
||||
) {
|
||||
let cell_index = self.tabs[i].properties.prop_vertex;
|
||||
if !handles.is_empty() {
|
||||
self.push_undo_snapshot(i, "TABLE CELL COLOR");
|
||||
for &handle in &handles {
|
||||
self.apply_property_op(i, "TABLE CELL COLOR", &handles, |app, handle| {
|
||||
let Some(acadrust::EntityType::Table(table)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
else {
|
||||
continue;
|
||||
return;
|
||||
};
|
||||
let columns = table.column_count();
|
||||
if columns == 0 {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
if let Some(cell) = table.cell_mut(
|
||||
cell_index / columns,
|
||||
@@ -5164,7 +5139,7 @@ impl OpenCADStudio {
|
||||
CellStateFlags::FORMAT_LOCKED
|
||||
| CellStateFlags::FORMAT_READ_ONLY,
|
||||
) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
let style = cell.style.get_or_insert_with(Default::default);
|
||||
if field == "tbl_cell_content_color" {
|
||||
@@ -5181,20 +5156,16 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
table.block_record_handle = None;
|
||||
}
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
});
|
||||
self.tabs[i].properties.open_color_field = None;
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
if !handles.is_empty() {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
let idx = if field == "gradient_color_2" { 1 } else { 0 };
|
||||
for &handle in &handles {
|
||||
self.apply_property_op(i, "CHPROP", &handles, |app, handle| {
|
||||
if let Some(acadrust::EntityType::Hatch(h)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
app.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
while h.gradient_color.colors.len() <= idx {
|
||||
let value = if h.gradient_color.colors.is_empty() {
|
||||
@@ -5211,14 +5182,11 @@ impl OpenCADStudio {
|
||||
}
|
||||
h.gradient_color.colors[idx].color = color.clone();
|
||||
}
|
||||
}
|
||||
});
|
||||
// Rebuild hatch seeds so the gradient fill picks up the new
|
||||
// colour (synced_hatch_models only patches the main colour).
|
||||
self.tabs[i].scene.populate_hatches_from_document();
|
||||
self.invalidate_property_targets(i, &handles);
|
||||
self.tabs[i].properties.open_color_field = None;
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user