feat(annotative): annotation-scale manager window

Replace the scale popup's inline delete/add affordances with a single
"Manage scales..." row that opens a proper manager, matching the style
managers: it reuses the shared style-manager palette and chrome (toolbar +
list + editor frame), so it looks and behaves like the Text / Dim / MLeader
managers.

The manager lists the drawing's annotation scales (name + ratio, the current
one checked) and edits them through a Name / Paper units / Drawing units
editor: New adds a scale from the fields, Apply renames / re-ratios the
selected one, Delete removes it (never the current scale), and Set Current
makes the selected scale the model-space annotation scale. Backed by
Scene::add_scale / remove_scale / edit_scale on ACAD_SCALELIST.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-13 10:39:14 +03:00
commit 1071570f40
9 changed files with 427 additions and 67 deletions

View file

@ -1945,6 +1945,57 @@ impl Scene {
true
}
/// Rename `old_name` and/or change its paper:drawing ratio. Returns `false`
/// if the scale is missing or the new name collides with another scale.
pub fn edit_scale(&mut self, old_name: &str, new_name: &str, paper: f64, drawing: f64) -> bool {
use acadrust::objects::ObjectType;
let root_h = self.document.header.named_objects_dict_handle;
let Some(scalelist_h) = crate::scene::annotative::as_dict(&self.document, root_h)
.and_then(|d| d.get("ACAD_SCALELIST"))
else {
return false;
};
let Some(sh) = crate::scene::annotative::as_dict(&self.document, scalelist_h).and_then(
|d| {
d.entries
.iter()
.find(|(n, _)| n.eq_ignore_ascii_case(old_name))
.map(|(_, h)| *h)
},
) else {
return false;
};
// A new name that collides with a *different* scale is rejected.
if !new_name.eq_ignore_ascii_case(old_name)
&& crate::scene::annotative::as_dict(&self.document, scalelist_h)
.is_some_and(|d| d.entries.iter().any(|(n, _)| n.eq_ignore_ascii_case(new_name)))
{
return false;
}
if let Some(ObjectType::Scale(s)) = self.document.objects.get_mut(&sh) {
s.name = new_name.to_string();
s.paper_units = paper;
s.drawing_units = drawing;
s.is_unit_scale = (paper - drawing).abs() < 1e-10;
}
if let Some(ObjectType::Dictionary(sl)) = self.document.objects.get_mut(&scalelist_h) {
if let Some(e) = sl.entries.iter_mut().find(|(n, _)| n.eq_ignore_ascii_case(old_name)) {
e.0 = new_name.to_string();
}
}
true
}
/// The (paper_units, drawing_units) of a named scale, for the editor.
pub fn scale_paper_drawing(&self, name: &str) -> Option<(f64, f64)> {
self.document.objects.values().find_map(|o| match o {
ObjectType::Scale(s) if !s.is_temporary && s.name.eq_ignore_ascii_case(name) => {
Some((s.paper_units, s.drawing_units))
}
_ => None,
})
}
/// List of user viewports in the current layout: (handle, label, frozen_layer_handles).
pub fn viewport_list(&self) -> Vec<(acadrust::Handle, String, Vec<acadrust::Handle>)> {
if self.current_layout == "Model" {