feat(plot): page setup manager — named setups, layouts, <none>/<previous>
Restructure the plot dialog into a style-manager-style master/detail: a left list of page setups plus a right settings editor. The list shows <none> (defaults + PDF) and <previous> (the settings the dialog opened with), then the drawing's layouts (*Model*, *Layout1*, …) and its standalone named page setups. - Named page setups live in the ACAD_PLOTSETTINGS dictionary (src/scene/page_setup.rs: names/get/save/delete/rename, creating the dictionary if absent); DWG round-trip verified. - Toolbar New / Copy / Delete | Set current / Preview / Print. Selecting a row loads it into the editor; Set current writes to the active layout. Copy duplicates a layout or a setup into a new named setup; Delete/Rename apply only to named setups. - Scene::plot_settings_for(name) generalizes effective_plot_settings so any layout's settings can be loaded; on open the settings are snapshotted for <previous>. - Default selection: model space → <none>; a layout → its own setup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
d37aacd895
commit
a1ee4d9269
8 changed files with 649 additions and 25 deletions
|
|
@ -21,6 +21,7 @@ mod camera_ops;
|
|||
mod layout;
|
||||
mod modify;
|
||||
mod mspace;
|
||||
mod page_setup;
|
||||
mod paper;
|
||||
mod preview;
|
||||
mod project;
|
||||
|
|
@ -1677,14 +1678,20 @@ impl Scene {
|
|||
/// AutoCAD files keep their settings embedded, so without this fallback the
|
||||
/// plot/PDF path would ignore the file's rotation, origin and scale.
|
||||
pub fn effective_plot_settings(&self) -> Option<acadrust::objects::PlotSettings> {
|
||||
self.plot_settings_for(&self.current_layout)
|
||||
}
|
||||
|
||||
/// Plot settings for a specific layout by name: its standalone
|
||||
/// `PlotSettings` object if one exists, else synthesized from the `Layout`
|
||||
/// object's embedded fields.
|
||||
pub fn plot_settings_for(&self, name: &str) -> Option<acadrust::objects::PlotSettings> {
|
||||
use acadrust::objects::{
|
||||
ObjectType, PaperMargin, PlotPaperUnits, PlotRotation, PlotSettings, PlotType,
|
||||
PlotWindow, ScaledType,
|
||||
};
|
||||
let name = &self.current_layout;
|
||||
if let Some(ps) = self.document.objects.values().find_map(|o| {
|
||||
if let ObjectType::PlotSettings(ps) = o {
|
||||
if &ps.page_name == name {
|
||||
if ps.page_name.as_str() == name {
|
||||
return Some(ps.clone());
|
||||
}
|
||||
}
|
||||
|
|
@ -1694,7 +1701,7 @@ impl Scene {
|
|||
}
|
||||
self.document.objects.values().find_map(|o| {
|
||||
let ObjectType::Layout(l) = o else { return None };
|
||||
if &l.name != name {
|
||||
if l.name.as_str() != name {
|
||||
return None;
|
||||
}
|
||||
let mut ps = PlotSettings::new(l.name.clone());
|
||||
|
|
|
|||
126
src/scene/page_setup.rs
Normal file
126
src/scene/page_setup.rs
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
//! Named page setups — the reusable plot configurations a drawing stores in
|
||||
//! its `ACAD_PLOTSETTINGS` dictionary (each a standalone `PlotSettings` object
|
||||
//! keyed by name). These are distinct from a layout's own embedded plot
|
||||
//! settings: a named setup can be applied to any layout. CRUD here keeps the
|
||||
//! dictionary + objects consistent so the result round-trips through DWG/DXF.
|
||||
|
||||
use super::Scene;
|
||||
use acadrust::objects::{Dictionary, ObjectType, PlotSettings};
|
||||
use acadrust::Handle;
|
||||
|
||||
impl Scene {
|
||||
/// Names of the document's named page setups, in dictionary order.
|
||||
pub fn page_setup_names(&self) -> Vec<String> {
|
||||
let dh = self.document.header.acad_plotsettings_dict_handle;
|
||||
match self.document.objects.get(&dh) {
|
||||
Some(ObjectType::Dictionary(d)) => d.entries.iter().map(|(k, _)| k.clone()).collect(),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the named page setup's `PlotSettings`, or `None` if absent.
|
||||
pub fn page_setup_get(&self, name: &str) -> Option<PlotSettings> {
|
||||
let dh = self.document.header.acad_plotsettings_dict_handle;
|
||||
let ObjectType::Dictionary(d) = self.document.objects.get(&dh)? else {
|
||||
return None;
|
||||
};
|
||||
let h = d.entries.iter().find(|(k, _)| k == name).map(|(_, h)| *h)?;
|
||||
match self.document.objects.get(&h)? {
|
||||
ObjectType::PlotSettings(ps) => Some(ps.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle of the `ACAD_PLOTSETTINGS` dictionary, creating it (and its entry
|
||||
/// in the root named-objects dictionary) if the drawing has none yet.
|
||||
fn ensure_plotsettings_dict(&mut self) -> Handle {
|
||||
let dh = self.document.header.acad_plotsettings_dict_handle;
|
||||
if matches!(self.document.objects.get(&dh), Some(ObjectType::Dictionary(_))) {
|
||||
return dh;
|
||||
}
|
||||
let root = self.document.header.named_objects_dict_handle;
|
||||
let mut dict = Dictionary::new();
|
||||
dict.handle = self.document.allocate_handle();
|
||||
dict.owner = root;
|
||||
let new_handle = dict.handle;
|
||||
self.document
|
||||
.objects
|
||||
.insert(new_handle, ObjectType::Dictionary(dict));
|
||||
self.document.header.acad_plotsettings_dict_handle = new_handle;
|
||||
if let Some(ObjectType::Dictionary(rd)) = self.document.objects.get_mut(&root) {
|
||||
rd.entries.retain(|(k, _)| k != "ACAD_PLOTSETTINGS");
|
||||
rd.entries.push(("ACAD_PLOTSETTINGS".to_string(), new_handle));
|
||||
}
|
||||
new_handle
|
||||
}
|
||||
|
||||
/// Handle of the named page setup, if it exists.
|
||||
fn page_setup_handle(&self, name: &str) -> Option<Handle> {
|
||||
let dh = self.document.header.acad_plotsettings_dict_handle;
|
||||
let ObjectType::Dictionary(d) = self.document.objects.get(&dh)? else {
|
||||
return None;
|
||||
};
|
||||
d.entries.iter().find(|(k, _)| k == name).map(|(_, h)| *h)
|
||||
}
|
||||
|
||||
/// Create or update the named page setup from `ps` (its `page_name` and
|
||||
/// `owner` are set here). Existing entries are updated in place; new ones
|
||||
/// are inserted and registered in the dictionary.
|
||||
pub fn page_setup_save(&mut self, name: &str, mut ps: PlotSettings) {
|
||||
let dict_handle = self.ensure_plotsettings_dict();
|
||||
ps.page_name = name.to_string();
|
||||
ps.owner = dict_handle;
|
||||
if let Some(h) = self.page_setup_handle(name) {
|
||||
ps.handle = h;
|
||||
self.document.objects.insert(h, ObjectType::PlotSettings(ps));
|
||||
} else {
|
||||
ps.handle = self.document.allocate_handle();
|
||||
let h = ps.handle;
|
||||
self.document.objects.insert(h, ObjectType::PlotSettings(ps));
|
||||
if let Some(ObjectType::Dictionary(d)) = self.document.objects.get_mut(&dict_handle) {
|
||||
d.entries.push((name.to_string(), h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the named page setup (object + dictionary entry). No-op if absent.
|
||||
pub fn page_setup_delete(&mut self, name: &str) {
|
||||
let dict_handle = self.document.header.acad_plotsettings_dict_handle;
|
||||
let handle = if let Some(ObjectType::Dictionary(d)) =
|
||||
self.document.objects.get_mut(&dict_handle)
|
||||
{
|
||||
let h = d.entries.iter().find(|(k, _)| k == name).map(|(_, h)| *h);
|
||||
d.entries.retain(|(k, _)| k != name);
|
||||
h
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(h) = handle {
|
||||
self.document.objects.remove(&h);
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename a named page setup (dictionary key + the object's `page_name`).
|
||||
/// No-op if `old` is absent or `new` already exists.
|
||||
pub fn page_setup_rename(&mut self, old: &str, new: &str) {
|
||||
if old == new || new.trim().is_empty() {
|
||||
return;
|
||||
}
|
||||
if self.page_setup_handle(new).is_some() {
|
||||
return; // name collision
|
||||
}
|
||||
let dict_handle = self.document.header.acad_plotsettings_dict_handle;
|
||||
let mut renamed = None;
|
||||
if let Some(ObjectType::Dictionary(d)) = self.document.objects.get_mut(&dict_handle) {
|
||||
if let Some(e) = d.entries.iter_mut().find(|(k, _)| k == old) {
|
||||
e.0 = new.to_string();
|
||||
renamed = Some(e.1);
|
||||
}
|
||||
}
|
||||
if let Some(h) = renamed {
|
||||
if let Some(ObjectType::PlotSettings(ps)) = self.document.objects.get_mut(&h) {
|
||||
ps.page_name = new.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue