forked from 1b-agent/opencadstudio-upstream
Fields: stored values on redraw, evaluation events, per-field shading
MTEXT draws its stored text instead of re-evaluating fields on every redraw. Fields update on their evaluation events as the reference does: open (1), save (2, not autosave), plot (4) and REGEN/REGENALL (16), each masked with the drawing's FIELDEVAL (variable dictionary, 0-31, with the reference's prompt and message), and the new UPDATEFIELD command (selection, attributes of selected block references, "N field(s) found." / "N field(s) updated."). A Date field (on demand only) keeps its value until UPDATEFIELD. The FIELDDISPLAY gray box now covers each field's own glyphs, one box per line it spans, instead of the whole text. Codec moved to the update_fields / field_spans revision. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
71cebbb3fe
commit
1c242eb489
13 files changed
+229
-19
No files matched your search
@@ -848,6 +848,7 @@ impl OpenCADStudio {
|
||||
".{}.tmp.dwg",
|
||||
path.file_name().unwrap().to_string_lossy()
|
||||
));
|
||||
self.tabs[i].scene.update_fields(2, None);
|
||||
let document = self.tabs[i].scene.document_for_save();
|
||||
if let Err(e) = crate::io::save(&document, &scratch) {
|
||||
return err(format!("save: {e}"));
|
||||
|
||||
@@ -504,6 +504,8 @@ impl OpenCADStudio {
|
||||
}
|
||||
// Rebuild tessellation caches without modifying the document.
|
||||
"REGEN" | "REGENALL" => {
|
||||
// A regen is a field evaluation event (FIELDEVAL bit 16).
|
||||
self.tabs[i].scene.update_fields(16, None);
|
||||
self.tabs[i].scene.populate_meshes_from_document();
|
||||
self.command_line.push_output(crate::t!("REGEN: regenerated model.").as_ref());
|
||||
return Some(Task::none());
|
||||
|
||||
@@ -17,6 +17,68 @@ impl OpenCADStudio {
|
||||
self.open_field_dialog(FieldTarget::NewText);
|
||||
Some(Task::none())
|
||||
}
|
||||
// UPDATEFIELD — re-evaluate the fields of the selected objects
|
||||
// (a block reference's attributes included), whatever FIELDEVAL.
|
||||
"UPDATEFIELD" => {
|
||||
let selected: Vec<codec::Handle> =
|
||||
self.tabs[i].scene.selected_entities().into_iter().map(|(h, _)| h).collect();
|
||||
if selected.is_empty() {
|
||||
use crate::modules::draw::select::SelectObjectsCommand;
|
||||
let cmd = SelectObjectsCommand::new("UPDATEFIELD");
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
return Some(Task::none());
|
||||
}
|
||||
let document = &self.tabs[i].scene.document;
|
||||
let mut hosts = selected.clone();
|
||||
for handle in &selected {
|
||||
if let Some(codec::EntityType::Insert(insert)) = document.get_entity(*handle) {
|
||||
hosts.extend(insert.attributes.iter().map(|a| a.common.handle));
|
||||
}
|
||||
}
|
||||
self.push_undo_snapshot(i, "UPDATEFIELD");
|
||||
let found = self.tabs[i].scene.update_fields(32, Some(&hosts));
|
||||
self.tabs[i].scene.deselect_all();
|
||||
self.refresh_properties();
|
||||
if found > 0 {
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
self.command_line.push_output(crate::tf!("{} field(s) found.", found).as_ref());
|
||||
self.command_line.push_output(crate::tf!("{} field(s) updated.", found).as_ref());
|
||||
Some(Task::none())
|
||||
}
|
||||
// FIELDEVAL — the events that update fields, kept in the drawing.
|
||||
cmd if cmd == "FIELDEVAL" || cmd.starts_with("FIELDEVAL ") || cmd.starts_with("SETVAR FIELDEVAL") => {
|
||||
let value = cmd
|
||||
.trim_start_matches("SETVAR ")
|
||||
.trim_start_matches("FIELDEVAL")
|
||||
.trim();
|
||||
let current = crate::entities::field::fieldeval(&self.tabs[i].scene.document);
|
||||
let ask = |app: &mut Self| {
|
||||
app.command_line
|
||||
.push_output(&format!("Enter new value for FIELDEVAL <{current}>:"));
|
||||
app.pending_setvar = Some("FIELDEVAL".into());
|
||||
};
|
||||
match value.parse::<i32>() {
|
||||
_ if value.is_empty() => ask(self),
|
||||
Ok(n) if (0..=31).contains(&n) => {
|
||||
if n != current {
|
||||
self.push_undo_snapshot(i, "FIELDEVAL");
|
||||
crate::io::set_drawing_variable(
|
||||
&mut self.tabs[i].scene.document,
|
||||
"FIELDEVAL",
|
||||
&n.to_string(),
|
||||
);
|
||||
self.tabs[i].dirty = true;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.command_line.push_error("Requires an integer between 0 and 31.");
|
||||
ask(self);
|
||||
}
|
||||
}
|
||||
Some(Task::none())
|
||||
}
|
||||
cmd if cmd == "_FIELD_OBJECT" || cmd.starts_with("_FIELD_OBJECT ") => {
|
||||
let handle = cmd
|
||||
.split_whitespace()
|
||||
|
||||
@@ -697,6 +697,8 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"PDFOSNAP",
|
||||
"UOSNAP",
|
||||
"FIELDDISPLAY",
|
||||
"FIELDEVAL",
|
||||
"UPDATEFIELD",
|
||||
"PDFIMPORTMODE",
|
||||
"PDFIMPORTFILTER",
|
||||
"PDFIMPORTLAYERS",
|
||||
|
||||
@@ -2144,6 +2144,14 @@ impl OpenCADStudio {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(in crate::app) fn prepare_native_save(&mut self, i: usize) {
|
||||
// A save is a field evaluation event (FIELDEVAL bit 2).
|
||||
self.tabs[i].scene.update_fields(2, None);
|
||||
self.prepare_snapshot_save(i);
|
||||
}
|
||||
|
||||
/// Bring the drawing state the file stores up to date, without the save
|
||||
/// event an autosave does not raise.
|
||||
pub(in crate::app) fn prepare_snapshot_save(&mut self, i: usize) {
|
||||
self.sync_view_state_for_save(i);
|
||||
sync_annotation_scale_header(&mut self.tabs[i].scene);
|
||||
self.stamp_header_sysvars(i);
|
||||
@@ -3275,7 +3283,7 @@ impl OpenCADStudio {
|
||||
if !self.tabs[i].dirty || self.active_save_jobs.contains_key(&self.tabs[i].id) {
|
||||
continue;
|
||||
}
|
||||
self.prepare_native_save(i);
|
||||
self.prepare_snapshot_save(i);
|
||||
let version = self.tabs[i].scene.document.version;
|
||||
let target = self.autosave_target(i);
|
||||
tasks.push(self.queue_native_save(
|
||||
|
||||
Reference in new issue
Block a user