fix(blocks): enforce constant attributes in ATTEDIT and restore ATTDEFs on explode

Enforce constant attribute flag in ATTEDIT dialog by presenting constant attributes as disabled and ignoring modifications. When exploding an insert, restore block-definition attribute definitions as transformed ATTDEF entities while dropping instance attribute values.
This commit is contained in:
Karim Jerbi committed 2026-10-13 11:10:11 +01:00
1 parent df22824fbe
commit 59bb6faabd
5 files changed
+393 -33

No files matched your search

+40 -26
View File
@@ -4265,11 +4265,11 @@ impl OpenCADStudio {
Some(codec::EntityType::Insert(ins)) if !ins.attributes.is_empty() => {
// The prompt text lives on the block's ATTDEFs, not on the
// attribute instances — map tag → prompt from the definition.
let prompts = block_attr_prompts(doc, &ins.block_name);
let meta = block_attr_meta(doc, &ins.block_name);
let rows = ins
.attributes
.iter()
.map(|a| attr_row_from_entity(a, &prompts))
.map(|a| attr_row_from_entity(a, &meta))
.collect::<Vec<_>>();
Ok((ins.block_name.clone(), rows))
}
@@ -4363,16 +4363,47 @@ impl OpenCADStudio {
}
}
pub(crate) struct BlockAttrMeta {
pub(crate) prompt: String,
pub(crate) constant: bool,
}
/// Map each attribute tag to the metadata (prompt text, constant flag) declared on
/// the block's matching ATTDEF. Tags with no definition are absent.
pub(crate) fn block_attr_meta(
doc: &codec::CadDocument,
block_name: &str,
) -> rustc_hash::FxHashMap<String, BlockAttrMeta> {
let mut map = rustc_hash::FxHashMap::default();
if let Some(br) = doc.block_records.get(block_name) {
for &eh in &br.entity_handles {
if let Some(codec::EntityType::AttributeDefinition(ad)) = doc.get_entity(eh) {
map.insert(
ad.tag.clone(),
BlockAttrMeta {
prompt: ad.prompt.clone(),
constant: ad.flags.constant,
},
);
}
}
}
map
}
/// Build the editor's working copy of one attribute. Angles are shown in
/// degrees; numeric fields become strings so the user can type freely.
fn attr_row_from_entity(
pub(crate) fn attr_row_from_entity(
a: &codec::entities::AttributeEntity,
prompts: &rustc_hash::FxHashMap<String, String>,
meta: &rustc_hash::FxHashMap<String, BlockAttrMeta>,
) -> AttrRow {
let fmt = |v: f64| format!("{v}");
let ad_meta = meta.get(&a.tag);
let prompt = ad_meta.map(|m| m.prompt.clone()).unwrap_or_default();
let is_constant = a.flags.constant || ad_meta.map_or(false, |m| m.constant);
AttrRow {
tag: a.tag.clone(),
prompt: prompts.get(&a.tag).cloned().unwrap_or_default(),
prompt,
value: a.get_value().to_string(),
text_style: a.text_style.clone(),
height: fmt(a.height),
@@ -4387,15 +4418,16 @@ fn attr_row_from_entity(
color: a.common.color,
linetype: a.common.linetype.clone(),
line_weight: a.common.line_weight,
is_constant,
}
}
/// Write one edited row back onto its attribute; returns whether anything
/// changed. Numeric fields are parsed (angles from degrees); an unparseable
/// field is left as-is.
fn apply_attr_row(a: &mut codec::entities::AttributeEntity, row: &AttrRow) -> bool {
/// field is left as-is. Constant attributes ignore edits to their value.
pub(crate) fn apply_attr_row(a: &mut codec::entities::AttributeEntity, row: &AttrRow) -> bool {
let mut ch = false;
if a.get_value() != row.value {
if !row.is_constant && a.get_value() != row.value {
a.set_value(row.value.clone());
ch = true;
}
@@ -4466,24 +4498,6 @@ fn apply_attr_row(a: &mut codec::entities::AttributeEntity, row: &AttrRow) -> bo
ch
}
/// Map each attribute tag to the prompt text declared on the block's matching
/// ATTDEF. Attribute instances (ATTRIB) carry only tag + value; the prompt is
/// defined once on the block definition. Tags with no definition are absent.
fn block_attr_prompts(
doc: &codec::CadDocument,
block_name: &str,
) -> rustc_hash::FxHashMap<String, String> {
let mut map = rustc_hash::FxHashMap::default();
if let Some(br) = doc.block_records.get(block_name) {
for &eh in &br.entity_handles {
if let Some(codec::EntityType::AttributeDefinition(ad)) = doc.get_entity(eh) {
map.insert(ad.tag.clone(), ad.prompt.clone());
}
}
}
map
}
#[cfg(test)]
mod layer_rename_tests {
use crate::app::OpenCADStudio;