forked from 1b-agent/opencadstudio-upstream
ATTDEF dialog error band and multi-line editor; ATTDISP sets ATTMODE
Validation messages show in the dialogs, the Default value of a multi-line definition can be edited in the multi-line text editor, "Specify on-screen" is kept for the session, ATTDISP sets ATTMODE with the reference prompt instead of changing the definitions, and Insert > Block gets the Edit Attribute (Single / Multiple) and attribute display lists. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c992b73cdf
commit
7f55043210
29 files changed
+449
-70
No files matched your search
@@ -46,6 +46,19 @@ impl OpenCADStudio {
|
||||
self.push_ucs_to_cmd(i);
|
||||
Some(Task::none())
|
||||
}
|
||||
"ATTMODE0" => self.dispatch_draw("ATTDISP OFF", i),
|
||||
"ATTMODE1" => self.dispatch_draw("ATTDISP NORMAL", i),
|
||||
"ATTMODE2" => self.dispatch_draw("ATTDISP ON", i),
|
||||
"_ATTDEF_VALUE" => {
|
||||
let value = attdef::session().editor_value.take();
|
||||
if let Some(state) = self.attdef_dialog.as_mut() {
|
||||
if let Some(value) = value {
|
||||
state.default = value;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDef);
|
||||
}
|
||||
Some(Task::none())
|
||||
}
|
||||
cmd if cmd == "_ATTDEF_PICKED" || cmd.starts_with("_ATTDEF_PICKED ") => {
|
||||
let mut parts = cmd.split_whitespace().skip(1);
|
||||
if let (Some(kind), Some(value), Some(state)) =
|
||||
@@ -116,10 +129,11 @@ impl OpenCADStudio {
|
||||
height: short(defaults.height),
|
||||
rotation: "0".into(),
|
||||
width: "0".into(),
|
||||
on_screen: true,
|
||||
on_screen: session.on_screen,
|
||||
coords: ["0".into(), "0".into(), "0".into()],
|
||||
align_below: false,
|
||||
can_align_below: session.last.is_some(),
|
||||
error: None,
|
||||
});
|
||||
drop(session);
|
||||
self.tabs[i].active_cmd = None;
|
||||
@@ -139,6 +153,7 @@ impl OpenCADStudio {
|
||||
prompt: a.prompt.clone(),
|
||||
default: a.default_value.clone(),
|
||||
constant: a.flags.constant,
|
||||
error: None,
|
||||
});
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDefEdit);
|
||||
}
|
||||
@@ -155,6 +170,7 @@ impl OpenCADStudio {
|
||||
.as_ref(),
|
||||
),
|
||||
AttdefDialogMsg::EditOk => return self.attdef_edit_ok(i),
|
||||
AttdefDialogMsg::DismissError => edit.error = None,
|
||||
_ => {}
|
||||
}
|
||||
return Task::none();
|
||||
@@ -201,30 +217,50 @@ impl OpenCADStudio {
|
||||
.as_ref(),
|
||||
),
|
||||
AttdefDialogMsg::Ok => return self.attdef_dialog_ok(i),
|
||||
AttdefDialogMsg::DismissError => state.error = None,
|
||||
AttdefDialogMsg::EditValue => {
|
||||
// The dialog waits while the multi-line editor collects the value.
|
||||
let initial = state.default.clone();
|
||||
let height = state.height.trim().parse::<f64>().ok().filter(|h| *h > 0.0).unwrap_or(2.5);
|
||||
self.active_modal = None;
|
||||
attdef::session().editor_value = None;
|
||||
self.tabs[i].active_cmd = Some(Box::new(attdef::AttdefValueCommand));
|
||||
let pos = self.tabs[i].scene.camera.borrow().target;
|
||||
return self.apply_cmd_result(crate::command::CmdResult::SuspendForMTextInput {
|
||||
pos,
|
||||
initial,
|
||||
height,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// Show a validation message in the dialog; it stays open.
|
||||
fn attdef_dialog_error(&mut self, error: &str) -> Task<Message> {
|
||||
if let Some(state) = self.attdef_dialog.as_mut() {
|
||||
state.error = Some(crate::t!(error).into_owned());
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn attdef_dialog_ok(&mut self, i: usize) -> Task<Message> {
|
||||
let Some(state) = self.attdef_dialog.clone() else {
|
||||
return Task::none();
|
||||
};
|
||||
if let Some(error) = tag_error(&state.tag) {
|
||||
self.command_line.push_error(error);
|
||||
return Task::none();
|
||||
return self.attdef_dialog_error(error);
|
||||
}
|
||||
let number = |s: &str| s.trim().parse::<f64>().ok().filter(|v| v.is_finite());
|
||||
let height = match number(&state.height) {
|
||||
Some(v) if v > 0.0 => v,
|
||||
_ => {
|
||||
self.command_line.push_error("Value must be positive and nonzero.");
|
||||
return Task::none();
|
||||
return self.attdef_dialog_error("Value must be positive and nonzero.");
|
||||
}
|
||||
};
|
||||
let Some(rotation) = number(&state.rotation) else {
|
||||
self.command_line.push_error("Requires valid numeric angle or second point.");
|
||||
return Task::none();
|
||||
return self.attdef_dialog_error("Requires valid numeric angle or second point.");
|
||||
};
|
||||
let width = number(&state.width).unwrap_or(0.0).max(0.0);
|
||||
let defaults =
|
||||
@@ -250,6 +286,7 @@ impl OpenCADStudio {
|
||||
let mut session = attdef::session();
|
||||
session.aflags = spec.aflags;
|
||||
session.annotative = spec.annotative;
|
||||
session.on_screen = state.on_screen;
|
||||
}
|
||||
self.reset_command_start_state(i);
|
||||
if state.align_below {
|
||||
@@ -288,7 +325,9 @@ impl OpenCADStudio {
|
||||
return Task::none();
|
||||
};
|
||||
if let Some(error) = tag_error(&state.tag) {
|
||||
self.command_line.push_error(error);
|
||||
if let Some(edit) = self.attdef_edit.as_mut() {
|
||||
edit.error = Some(crate::t!(error).into_owned());
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
self.attdef_edit = None;
|
||||
|
||||
+19
-55
@@ -183,68 +183,32 @@ impl OpenCADStudio {
|
||||
}
|
||||
}
|
||||
|
||||
// ATTDISP — control attribute display visibility.
|
||||
// ATTDISP ON — make all AttributeDefinitions visible
|
||||
// ATTDISP OFF — make all AttributeDefinitions invisible
|
||||
// ATTDISP NORMAL — restore: show only those without the invisible flag
|
||||
// ATTDISP sets ATTMODE: 0 hides every attribute, 1 shows the
|
||||
// visible ones, 2 shows them all. The definitions are not touched.
|
||||
"ATTDISP" => {
|
||||
use crate::command::KeywordCommand;
|
||||
let c = KeywordCommand::new(
|
||||
"ATTDISP",
|
||||
"ATTDISP attribute display [On / Off / Normal]:",
|
||||
vec![
|
||||
("On", "ON", None),
|
||||
("Off", "OFF", None),
|
||||
("Normal", "NORMAL", None),
|
||||
],
|
||||
let command = crate::modules::draw::draw::attdef::AttdispCommand::new(
|
||||
self.tabs[i].scene.document.header.attribute_visibility,
|
||||
);
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
cmd if cmd.starts_with("ATTDISP ") => {
|
||||
let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
||||
match sub.as_str() {
|
||||
"ON" | "OFF" | "NORMAL" => {
|
||||
let handles: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|entity| {
|
||||
matches!(entity, codec::EntityType::AttributeDefinition(_))
|
||||
.then_some(entity.common().handle)
|
||||
})
|
||||
.filter(|handle| !self.tabs[i].scene.is_layer_locked(*handle))
|
||||
.collect();
|
||||
self.push_undo_snapshot(i, "ATTDISP");
|
||||
let mut count = 0usize;
|
||||
for handle in handles {
|
||||
if let Some(codec::EntityType::AttributeDefinition(ad)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(handle)
|
||||
{
|
||||
match sub.as_str() {
|
||||
"ON" => {
|
||||
ad.flags.invisible = false;
|
||||
count += 1;
|
||||
}
|
||||
"OFF" => {
|
||||
ad.flags.invisible = true;
|
||||
count += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(
|
||||
crate::tf!("ATTDISP {sub}: {count} attribute definition(s) updated.")
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
let mode = match cmd[8..].trim().to_ascii_uppercase().as_str() {
|
||||
"OFF" => 0,
|
||||
"ON" => 2,
|
||||
"NORMAL" | "N" => 1,
|
||||
_ => {
|
||||
self.command_line
|
||||
.push_info(crate::t!("Usage: ATTDISP ON | OFF | NORMAL").as_ref());
|
||||
self.command_line.push_error("Invalid option keyword.");
|
||||
return Some(Task::none());
|
||||
}
|
||||
};
|
||||
if self.tabs[i].scene.document.header.attribute_visibility != mode {
|
||||
self.push_undo_snapshot(i, "ATTDISP");
|
||||
self.tabs[i].scene.document.header.attribute_visibility = mode;
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
}
|
||||
self.command_line.push_output("Regenerating model.");
|
||||
}
|
||||
|
||||
"DONUT" => {
|
||||
|
||||
@@ -666,6 +666,9 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"POINTCLOUDPOINTMAXLEGACY",
|
||||
"XCLIPFRAME",
|
||||
"WIPEOUTFRAME",
|
||||
"ATTMODE0",
|
||||
"ATTMODE1",
|
||||
"ATTMODE2",
|
||||
"FRAMES0",
|
||||
"FRAMES1",
|
||||
"FRAMES3",
|
||||
|
||||
@@ -344,6 +344,12 @@ impl OpenCADStudio {
|
||||
_ => "FRAMES3",
|
||||
};
|
||||
self.ribbon.set_dropdown_current("FRAMES_DROPDOWN", current);
|
||||
let attmode = match tab.scene.document.header.attribute_visibility {
|
||||
0 => "ATTMODE0",
|
||||
2 => "ATTMODE2",
|
||||
_ => "ATTMODE1",
|
||||
};
|
||||
self.ribbon.set_dropdown_current("ATTMODE_DROPDOWN", attmode);
|
||||
}
|
||||
|
||||
fn update_message(&mut self, msg: Message) -> Task<Message> {
|
||||
|
||||
Reference in new issue
Block a user