feat(annotative): add/remove drawing annotation scales

The drawing's annotation-scale list (ACAD_SCALELIST) is now editable, so the
set of scales offered by the status-bar scale pill can be grown or trimmed:

- Scene::add_scale / remove_scale create or drop a Scale object and its
  SCALELIST dictionary entry (creating the SCALELIST dictionary, and
  registering it in the root, for drawings that carry none).
- SCALELISTEDIT gains ADD <ratio> / DELETE <name> alongside the existing
  listing (e.g. `SCALELISTEDIT ADD 1:50`).
- The scale popup (model space) grows a delete affordance on each non-active
  scale and a "+ Add scale..." row that primes the command line. The current
  annotation scale is protected from deletion.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-13 10:18:05 +03:00
1 parent fc5e93dcc1
commit b1eba180eb
5 files changed
+233 -20

No files matched your search

+72 -12
View File
@@ -704,19 +704,79 @@ impl OpenCADStudio {
}
}
// SCALELISTEDIT — list the drawing's annotation scales.
// SCALELISTEDIT — list / add / delete the drawing's annotation scales.
// SCALELISTEDIT list
// SCALELISTEDIT ADD 1:50 add (name is a paper:drawing ratio)
// SCALELISTEDIT DELETE 1:50 remove (not the current scale)
cmd if cmd == "SCALELISTEDIT" || cmd.starts_with("SCALELISTEDIT ") => {
let names: Vec<String> = self.tabs[i]
.scene
.scale_list()
.into_iter()
.map(|(n, _, _)| n)
.collect();
if names.is_empty() {
self.command_line.push_info("No annotation scales defined.");
} else {
self.command_line
.push_output(&format!("Annotation scales: {}", names.join(", ")));
let rest = cmd.trim_start_matches("SCALELISTEDIT").trim();
let mut parts = rest.splitn(2, char::is_whitespace);
let sub = parts.next().unwrap_or("").to_uppercase();
let arg = parts.next().unwrap_or("").trim();
match sub.as_str() {
"ADD" => match arg.split_once(':') {
Some((p, d)) => match (p.trim().parse::<f64>(), d.trim().parse::<f64>()) {
(Ok(paper), Ok(drawing)) if paper > 0.0 && drawing > 0.0 => {
self.push_undo_snapshot(i, "SCALELISTEDIT");
if self.tabs[i].scene.add_scale(arg, paper, drawing) {
self.tabs[i].dirty = true;
self.command_line
.push_output(&format!("Added annotation scale {arg}."));
} else {
self.command_line
.push_info(&format!("Scale {arg} already exists."));
}
}
_ => self
.command_line
.push_error("SCALELISTEDIT ADD: use a ratio like 1:50."),
},
None => self
.command_line
.push_error("SCALELISTEDIT ADD: use a ratio like 1:50."),
},
"DELETE" | "REMOVE" => {
let current = self.tabs[i]
.scene
.document
.header
.current_annotation_scale
.clone();
if arg.is_empty() {
self.command_line.push_info("Usage: SCALELISTEDIT DELETE <name>");
} else if arg.eq_ignore_ascii_case(&current) {
self.command_line.push_error(&format!(
"Cannot delete the current annotation scale ({arg})."
));
} else {
self.push_undo_snapshot(i, "SCALELISTEDIT");
if self.tabs[i].scene.remove_scale(arg) {
self.tabs[i].dirty = true;
self.command_line
.push_output(&format!("Removed annotation scale {arg}."));
} else {
self.command_line
.push_info(&format!("No annotation scale named {arg}."));
}
}
}
"" => {
let names: Vec<String> = self.tabs[i]
.scene
.scale_list()
.into_iter()
.map(|(n, _, _)| n)
.collect();
if names.is_empty() {
self.command_line.push_info("No annotation scales defined.");
} else {
self.command_line
.push_output(&format!("Annotation scales: {}", names.join(", ")));
}
}
_ => self
.command_line
.push_info("Usage: SCALELISTEDIT [ADD 1:50 | DELETE 1:50]"),
}
}
+4
View File
@@ -1455,6 +1455,10 @@ pub enum Message {
ToggleScalePopup,
/// Close the scale picker popup.
CloseScalePopup,
/// Delete a named annotation scale from the drawing's ACAD_SCALELIST.
ScaleListDelete(String),
/// Prime the command line to add a new annotation scale.
ScaleListAddPrompt,
/// Toggle the leftmost hamburger's Model/layout list dropdown.
ToggleLayoutList,
/// Close the Model/layout list dropdown.
+25
View File
@@ -1647,6 +1647,31 @@ impl OpenCADStudio {
self.scale_popup_open = false;
Task::none()
}
Message::ScaleListDelete(name) => {
let i = self.active_tab;
let current = self.tabs[i]
.scene
.document
.header
.current_annotation_scale
.clone();
// Keep the popup open so several can be removed in a row; never
// remove the current annotation scale out from under the drawing.
if !name.eq_ignore_ascii_case(&current) {
self.push_undo_snapshot(i, "SCALELISTEDIT");
if self.tabs[i].scene.remove_scale(&name) {
self.tabs[i].dirty = true;
}
}
Task::none()
}
Message::ScaleListAddPrompt => {
// Close the popup and prime the command line; the user types the
// ratio (e.g. 1:50) and commits it via SCALELISTEDIT ADD.
self.scale_popup_open = false;
self.command_line.input = "SCALELISTEDIT ADD ".to_string();
Task::none()
}
Message::ToggleLayoutList => {
self.layout_list_open ^= true;
Task::none()