feat(styles): make style managers transactional — persist only on Apply
A style manager is now a staged transaction. When it opens, a snapshot of the style tables, style objects and current-style pointers is taken. Every New / Copy / Delete / Rename / Set Current / property edit mutates the document live so the dialog previews the result, but the tab is not marked dirty, nothing is pushed to undo, and the drawing is not rebuilt. - Apply commits: one undo entry (restoring the pre-edit state), the tab is marked dirty, the drawing rebuilds, and the baseline is reset so further edits stage afresh. - Closing the window without Apply discards: the opening snapshot is restored, so none of the changes persist. Side effects (dirty / undo / bump_geometry) were removed from every style edit handler and routed exclusively through the commit. Multiline's Apply is a structural-only commit since its editor has no editable properties. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
65a25033bc
commit
de4eb31003
6 changed files with 205 additions and 101 deletions
|
|
@ -391,6 +391,10 @@ pub(super) struct OpenCADStudio {
|
||||||
style_rename: Option<String>,
|
style_rename: Option<String>,
|
||||||
/// Edit buffer for the inline rename text input.
|
/// Edit buffer for the inline rename text input.
|
||||||
style_rename_buf: String,
|
style_rename_buf: String,
|
||||||
|
/// Active style-manager transaction. Edits mutate the document live for an
|
||||||
|
/// in-dialog preview but only persist on Apply; closing without Apply
|
||||||
|
/// restores this snapshot. `None` when no style manager is staging.
|
||||||
|
style_stage: Option<style_ops::StyleStage>,
|
||||||
|
|
||||||
// ── TextStyle Font Browser ────────────────────────────────────────────
|
// ── TextStyle Font Browser ────────────────────────────────────────────
|
||||||
textstyle_selected: String,
|
textstyle_selected: String,
|
||||||
|
|
@ -1390,6 +1394,7 @@ impl OpenCADStudio {
|
||||||
// TextStyle font browser
|
// TextStyle font browser
|
||||||
style_rename: None,
|
style_rename: None,
|
||||||
style_rename_buf: String::new(),
|
style_rename_buf: String::new(),
|
||||||
|
style_stage: None,
|
||||||
textstyle_selected: "Standard".to_string(),
|
textstyle_selected: "Standard".to_string(),
|
||||||
textstyle_font: String::new(),
|
textstyle_font: String::new(),
|
||||||
textstyle_width: "1.0".to_string(),
|
textstyle_width: "1.0".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -341,15 +341,19 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
// ── Public operations (called by the message handlers) ─────────────────
|
// ── Public operations (called by the message handlers) ─────────────────
|
||||||
|
|
||||||
|
// Note: the structural ops below mutate the document live (so the dialog
|
||||||
|
// shows a preview) but do NOT mark the tab dirty, push undo, or rebuild the
|
||||||
|
// drawing. Those side effects are deferred to `style_stage_commit` (Apply);
|
||||||
|
// closing the window without Apply calls `style_stage_discard`, which
|
||||||
|
// restores the snapshot taken when the manager opened.
|
||||||
|
|
||||||
pub(super) fn style_new(&mut self, kind: StyleKind) {
|
pub(super) fn style_new(&mut self, kind: StyleKind) {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let name = self.unique_new_name(kind);
|
let name = self.unique_new_name(kind);
|
||||||
self.push_undo_snapshot(i, "STYLE NEW");
|
|
||||||
let h = self.tabs[i].scene.document.allocate_handle();
|
let h = self.tabs[i].scene.document.allocate_handle();
|
||||||
self.insert_default_style(kind, &name, h);
|
self.insert_default_style(kind, &name, h);
|
||||||
self.set_style_selected(kind, name.clone());
|
self.set_style_selected(kind, name.clone());
|
||||||
self.load_style_bufs(kind);
|
self.load_style_bufs(kind);
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.after_style_change(kind);
|
self.after_style_change(kind);
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output(&format!("Style '{name}' created."));
|
.push_output(&format!("Style '{name}' created."));
|
||||||
|
|
@ -359,28 +363,24 @@ impl OpenCADStudio {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let src = self.style_selected(kind);
|
let src = self.style_selected(kind);
|
||||||
let name = self.unique_suffixed_name(kind, &src);
|
let name = self.unique_suffixed_name(kind, &src);
|
||||||
self.push_undo_snapshot(i, "STYLE COPY");
|
|
||||||
let h = self.tabs[i].scene.document.allocate_handle();
|
let h = self.tabs[i].scene.document.allocate_handle();
|
||||||
if !self.clone_style_as(kind, &src, &name, h) {
|
if !self.clone_style_as(kind, &src, &name, h) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.set_style_selected(kind, name.clone());
|
self.set_style_selected(kind, name.clone());
|
||||||
self.load_style_bufs(kind);
|
self.load_style_bufs(kind);
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.after_style_change(kind);
|
self.after_style_change(kind);
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output(&format!("Style '{name}' created."));
|
.push_output(&format!("Style '{name}' created."));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn style_delete(&mut self, kind: StyleKind) {
|
pub(super) fn style_delete(&mut self, kind: StyleKind) {
|
||||||
let i = self.active_tab;
|
|
||||||
let name = self.style_selected(kind);
|
let name = self.style_selected(kind);
|
||||||
if name.eq_ignore_ascii_case("Standard") {
|
if name.eq_ignore_ascii_case("Standard") {
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_error("Cannot delete the Standard style.");
|
.push_error("Cannot delete the Standard style.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "STYLE DEL");
|
|
||||||
if !self.remove_style_storage(kind, &name) {
|
if !self.remove_style_storage(kind, &name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -391,7 +391,6 @@ impl OpenCADStudio {
|
||||||
.unwrap_or_else(|| "Standard".to_string());
|
.unwrap_or_else(|| "Standard".to_string());
|
||||||
self.set_style_selected(kind, first);
|
self.set_style_selected(kind, first);
|
||||||
self.load_style_bufs(kind);
|
self.load_style_bufs(kind);
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.after_style_change(kind);
|
self.after_style_change(kind);
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output(&format!("Style '{name}' deleted."));
|
.push_output(&format!("Style '{name}' deleted."));
|
||||||
|
|
@ -408,7 +407,6 @@ impl OpenCADStudio {
|
||||||
/// Commit the inline rename. No-op (with feedback) on empty / unchanged /
|
/// Commit the inline rename. No-op (with feedback) on empty / unchanged /
|
||||||
/// colliding names, and the Standard style cannot be renamed.
|
/// colliding names, and the Standard style cannot be renamed.
|
||||||
pub(super) fn style_rename_commit(&mut self, kind: StyleKind) {
|
pub(super) fn style_rename_commit(&mut self, kind: StyleKind) {
|
||||||
let i = self.active_tab;
|
|
||||||
let Some(old) = self.style_rename.take() else {
|
let Some(old) = self.style_rename.take() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -427,13 +425,11 @@ impl OpenCADStudio {
|
||||||
.push_error(&format!("Style '{new}' already exists."));
|
.push_error(&format!("Style '{new}' already exists."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "STYLE RENAME");
|
|
||||||
self.rename_style_storage(kind, &old, &new);
|
self.rename_style_storage(kind, &old, &new);
|
||||||
if self.style_selected(kind).eq_ignore_ascii_case(&old) {
|
if self.style_selected(kind).eq_ignore_ascii_case(&old) {
|
||||||
self.set_style_selected(kind, new.clone());
|
self.set_style_selected(kind, new.clone());
|
||||||
}
|
}
|
||||||
self.load_style_bufs(kind);
|
self.load_style_bufs(kind);
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.after_style_change(kind);
|
self.after_style_change(kind);
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output(&format!("Renamed '{old}' → '{new}'."));
|
.push_output(&format!("Renamed '{old}' → '{new}'."));
|
||||||
|
|
@ -443,6 +439,139 @@ impl OpenCADStudio {
|
||||||
self.style_rename = None;
|
self.style_rename = None;
|
||||||
self.style_rename_buf.clear();
|
self.style_rename_buf.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Staging: nothing persists until Apply ──────────────────────────────
|
||||||
|
//
|
||||||
|
// A style manager is a transaction. When it opens we snapshot the style
|
||||||
|
// tables / objects / current pointers; every New / Copy / Delete / Rename /
|
||||||
|
// Set Current / property edit mutates the live document for an in-dialog
|
||||||
|
// preview, but the tab stays clean and the drawing is not rebuilt. Apply
|
||||||
|
// commits (marks dirty, pushes one undo entry, rebuilds); closing the
|
||||||
|
// window without Apply discards by restoring the snapshot.
|
||||||
|
|
||||||
|
/// Snapshot the style-related document state into a transferable record.
|
||||||
|
fn capture_style_state(&self) -> StyleStateSnapshot {
|
||||||
|
let i = self.active_tab;
|
||||||
|
let doc = &self.tabs[i].scene.document;
|
||||||
|
let style_objects = doc
|
||||||
|
.objects
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, o)| {
|
||||||
|
matches!(
|
||||||
|
o,
|
||||||
|
ObjectType::TableStyle(_)
|
||||||
|
| ObjectType::MLineStyle(_)
|
||||||
|
| ObjectType::MultiLeaderStyle(_)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|(&h, o)| (h, o.clone()))
|
||||||
|
.collect();
|
||||||
|
StyleStateSnapshot {
|
||||||
|
text_styles: doc.text_styles.clone(),
|
||||||
|
dim_styles: doc.dim_styles.clone(),
|
||||||
|
style_objects,
|
||||||
|
current_text: doc.header.current_text_style_name.clone(),
|
||||||
|
current_dim: doc.header.current_dimstyle_name.clone(),
|
||||||
|
multiline_style: doc.header.multiline_style.clone(),
|
||||||
|
active_table: self.ribbon.active_table_style.clone(),
|
||||||
|
active_mleader: self.ribbon.active_mleader_style.clone(),
|
||||||
|
tab_active_mleader: self.tabs[i].active_mleader_style.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overwrite the live style state with a snapshot (used by commit's undo
|
||||||
|
/// dance and by discard).
|
||||||
|
fn restore_style_state(&mut self, snap: &StyleStateSnapshot) {
|
||||||
|
let i = self.active_tab;
|
||||||
|
let doc = &mut self.tabs[i].scene.document;
|
||||||
|
doc.text_styles = snap.text_styles.clone();
|
||||||
|
doc.dim_styles = snap.dim_styles.clone();
|
||||||
|
doc.objects.retain(|_, o| {
|
||||||
|
!matches!(
|
||||||
|
o,
|
||||||
|
ObjectType::TableStyle(_)
|
||||||
|
| ObjectType::MLineStyle(_)
|
||||||
|
| ObjectType::MultiLeaderStyle(_)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
for (h, o) in &snap.style_objects {
|
||||||
|
doc.objects.insert(*h, o.clone());
|
||||||
|
}
|
||||||
|
doc.header.current_text_style_name = snap.current_text.clone();
|
||||||
|
doc.header.current_dimstyle_name = snap.current_dim.clone();
|
||||||
|
doc.header.multiline_style = snap.multiline_style.clone();
|
||||||
|
self.ribbon.active_table_style = snap.active_table.clone();
|
||||||
|
self.ribbon.active_mleader_style = snap.active_mleader.clone();
|
||||||
|
self.tabs[i].active_mleader_style = snap.tab_active_mleader.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Begin a staging transaction for a freshly-opened style manager.
|
||||||
|
pub(super) fn style_stage_begin(&mut self) {
|
||||||
|
let dirty_at_open = self.tabs[self.active_tab].dirty;
|
||||||
|
let baseline = self.capture_style_state();
|
||||||
|
self.style_stage = Some(StyleStage {
|
||||||
|
dirty_at_open,
|
||||||
|
baseline,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit the staged changes (Apply): make them permanent with a single
|
||||||
|
/// undo entry, mark the tab dirty, and rebuild the drawing.
|
||||||
|
pub(super) fn style_stage_commit(&mut self) {
|
||||||
|
let i = self.active_tab;
|
||||||
|
let Some(stage) = self.style_stage.take() else {
|
||||||
|
// No active transaction — still refresh the drawing so a bare Apply
|
||||||
|
// is harmless.
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.tabs[i].scene.bump_geometry();
|
||||||
|
self.sync_ribbon_styles();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// Capture the edited state, rewind to the baseline so the undo entry
|
||||||
|
// restores the pre-edit document, then re-apply the edits on top.
|
||||||
|
let edited = self.capture_style_state();
|
||||||
|
self.restore_style_state(&stage.baseline);
|
||||||
|
self.push_undo_snapshot(i, "STYLE");
|
||||||
|
self.restore_style_state(&edited);
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.tabs[i].scene.bump_geometry();
|
||||||
|
self.sync_ribbon_styles();
|
||||||
|
// Re-baseline so further edits in the still-open window stage afresh.
|
||||||
|
self.style_stage = Some(StyleStage {
|
||||||
|
dirty_at_open: true,
|
||||||
|
baseline: edited,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Discard staged changes (window closed without Apply): restore the
|
||||||
|
/// snapshot taken when the manager opened.
|
||||||
|
pub(super) fn style_stage_discard(&mut self) {
|
||||||
|
let Some(stage) = self.style_stage.take() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.restore_style_state(&stage.baseline);
|
||||||
|
self.tabs[self.active_tab].dirty = stage.dirty_at_open;
|
||||||
|
self.sync_ribbon_styles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Snapshot of every document field a style manager can touch.
|
||||||
|
pub(super) struct StyleStateSnapshot {
|
||||||
|
text_styles: acadrust::tables::Table<TextStyle>,
|
||||||
|
dim_styles: acadrust::tables::Table<DimStyle>,
|
||||||
|
style_objects: Vec<(Handle, ObjectType)>,
|
||||||
|
current_text: String,
|
||||||
|
current_dim: String,
|
||||||
|
multiline_style: String,
|
||||||
|
active_table: String,
|
||||||
|
active_mleader: String,
|
||||||
|
tab_active_mleader: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An in-progress style-manager transaction.
|
||||||
|
pub(super) struct StyleStage {
|
||||||
|
dirty_at_open: bool,
|
||||||
|
baseline: StyleStateSnapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Object-store helpers ───────────────────────────────────────────────────
|
// ── Object-store helpers ───────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -1519,6 +1519,17 @@ impl OpenCADStudio {
|
||||||
self.save_dialog_window = None;
|
self.save_dialog_window = None;
|
||||||
return Task::none();
|
return Task::none();
|
||||||
}
|
}
|
||||||
|
// A style manager closed via OS ✕ counts as Cancel: discard any
|
||||||
|
// staged (not-yet-applied) changes by restoring the snapshot
|
||||||
|
// taken when it opened.
|
||||||
|
if self.textstyle_window == Some(id)
|
||||||
|
|| self.dimstyle_window == Some(id)
|
||||||
|
|| self.tablestyle_window == Some(id)
|
||||||
|
|| self.mleaderstyle_window == Some(id)
|
||||||
|
|| self.mlstyle_window == Some(id)
|
||||||
|
{
|
||||||
|
self.style_stage_discard();
|
||||||
|
}
|
||||||
// Each popup window that's launched from a ribbon tool
|
// Each popup window that's launched from a ribbon tool
|
||||||
// also turns that tool blue (`activate_tool`); when the
|
// also turns that tool blue (`activate_tool`); when the
|
||||||
// window closes, the matching tool needs to be cleared
|
// window closes, the matching tool needs to be cleared
|
||||||
|
|
@ -6127,6 +6138,7 @@ impl OpenCADStudio {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.textstyle_window = Some(id);
|
self.textstyle_window = Some(id);
|
||||||
|
self.style_stage_begin();
|
||||||
task.map(|_| Message::Noop)
|
task.map(|_| Message::Noop)
|
||||||
}
|
}
|
||||||
Message::TextStyleDialogClose => {
|
Message::TextStyleDialogClose => {
|
||||||
|
|
@ -6143,12 +6155,11 @@ impl OpenCADStudio {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::TextStyleDialogSetCurrent => {
|
Message::TextStyleDialogSetCurrent => {
|
||||||
|
// Staged: persists on Apply.
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let name = self.textstyle_selected.clone();
|
let name = self.textstyle_selected.clone();
|
||||||
if self.tabs[i].scene.document.text_styles.get(&name).is_some() {
|
if self.tabs[i].scene.document.text_styles.get(&name).is_some() {
|
||||||
self.push_undo_snapshot(i, "STYLE SET");
|
|
||||||
self.tabs[i].scene.document.header.current_text_style_name = name.clone();
|
self.tabs[i].scene.document.header.current_text_style_name = name.clone();
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.sync_ribbon_styles();
|
self.sync_ribbon_styles();
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output(&format!("Current text style: {}", name));
|
.push_output(&format!("Current text style: {}", name));
|
||||||
|
|
@ -6199,63 +6210,52 @@ impl OpenCADStudio {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::TextStyleToggle(field) => {
|
Message::TextStyleToggle(field) => {
|
||||||
|
// Staged: mutate live for preview, persist on Apply.
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let name = self.textstyle_selected.clone();
|
let name = self.textstyle_selected.clone();
|
||||||
if self.tabs[i].scene.document.text_styles.get(&name).is_some() {
|
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
|
||||||
self.push_undo_snapshot(i, "STYLE EDIT");
|
match field {
|
||||||
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
|
"backward" => s.flags.backward = !s.flags.backward,
|
||||||
match field {
|
"upside_down" => s.flags.upside_down = !s.flags.upside_down,
|
||||||
"backward" => s.flags.backward = !s.flags.backward,
|
"annotative" => s.annotative = !s.annotative,
|
||||||
"upside_down" => s.flags.upside_down = !s.flags.upside_down,
|
_ => {}
|
||||||
"annotative" => s.annotative = !s.annotative,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::TextStyleApply => {
|
Message::TextStyleApply => {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let name = self.textstyle_selected.clone();
|
let name = self.textstyle_selected.clone();
|
||||||
if self.tabs[i].scene.document.text_styles.get(&name).is_some() {
|
let font = self.textstyle_font.clone();
|
||||||
self.push_undo_snapshot(i, "STYLE EDIT");
|
let width_str = self.textstyle_width.clone();
|
||||||
let font = self.textstyle_font.clone();
|
let oblique_str = self.textstyle_oblique.clone();
|
||||||
let width_str = self.textstyle_width.clone();
|
let height_str = self.textstyle_height.clone();
|
||||||
let oblique_str = self.textstyle_oblique.clone();
|
let bigfont = self.textstyle_bigfont.clone();
|
||||||
let height_str = self.textstyle_height.clone();
|
let ttf = self.textstyle_ttf.clone();
|
||||||
let bigfont = self.textstyle_bigfont.clone();
|
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
|
||||||
let ttf = self.textstyle_ttf.clone();
|
s.font_file = font;
|
||||||
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
|
s.big_font_file = bigfont;
|
||||||
s.font_file = font;
|
s.true_type_font = ttf;
|
||||||
s.big_font_file = bigfont;
|
if let Ok(w) = width_str.trim().parse::<f64>() {
|
||||||
s.true_type_font = ttf;
|
s.width_factor = w;
|
||||||
if let Ok(w) = width_str.trim().parse::<f64>() {
|
}
|
||||||
s.width_factor = w;
|
if let Ok(a) = oblique_str.trim().parse::<f64>() {
|
||||||
}
|
s.oblique_angle = a.to_radians();
|
||||||
if let Ok(a) = oblique_str.trim().parse::<f64>() {
|
}
|
||||||
s.oblique_angle = a.to_radians();
|
if let Ok(h) = height_str.trim().parse::<f64>() {
|
||||||
}
|
s.height = h.max(0.0);
|
||||||
if let Ok(h) = height_str.trim().parse::<f64>() {
|
|
||||||
s.height = h.max(0.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
}
|
}
|
||||||
|
self.style_stage_commit();
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::TextStyleFontPick(font_file) => {
|
Message::TextStyleFontPick(font_file) => {
|
||||||
|
// Staged: update the buffer + live style; persist on Apply.
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.textstyle_font = font_file.clone();
|
self.textstyle_font = font_file.clone();
|
||||||
let name = self.textstyle_selected.clone();
|
let name = self.textstyle_selected.clone();
|
||||||
if self.tabs[i].scene.document.text_styles.get(&name).is_some() {
|
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
|
||||||
self.push_undo_snapshot(i, "STYLE FONT");
|
s.font_file = font_file;
|
||||||
if let Some(s) = self.tabs[i].scene.document.text_styles.get_mut(&name) {
|
|
||||||
s.font_file = font_file;
|
|
||||||
}
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6288,6 +6288,7 @@ impl OpenCADStudio {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.tablestyle_window = Some(id);
|
self.tablestyle_window = Some(id);
|
||||||
|
self.style_stage_begin();
|
||||||
task.map(|_| Message::Noop)
|
task.map(|_| Message::Noop)
|
||||||
}
|
}
|
||||||
Message::TableStyleDialogClose => {
|
Message::TableStyleDialogClose => {
|
||||||
|
|
@ -6321,7 +6322,6 @@ impl OpenCADStudio {
|
||||||
let h: Option<f64> = self.ts_hmargin.trim().parse().ok();
|
let h: Option<f64> = self.ts_hmargin.trim().parse().ok();
|
||||||
let v: Option<f64> = self.ts_vmargin.trim().parse().ok();
|
let v: Option<f64> = self.ts_vmargin.trim().parse().ok();
|
||||||
let desc = self.ts_description.clone();
|
let desc = self.ts_description.clone();
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
for obj in self.tabs[i].scene.document.objects.values_mut() {
|
for obj in self.tabs[i].scene.document.objects.values_mut() {
|
||||||
if let ObjectType::TableStyle(s) = obj {
|
if let ObjectType::TableStyle(s) = obj {
|
||||||
if s.name == name {
|
if s.name == name {
|
||||||
|
|
@ -6335,7 +6335,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.tabs[i].dirty = true;
|
self.style_stage_commit();
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6347,8 +6347,6 @@ impl OpenCADStudio {
|
||||||
"Up" => TableFlowDirection::Up,
|
"Up" => TableFlowDirection::Up,
|
||||||
_ => TableFlowDirection::Down,
|
_ => TableFlowDirection::Down,
|
||||||
};
|
};
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6404,9 +6402,6 @@ impl OpenCADStudio {
|
||||||
_ => TableBorderType::Single,
|
_ => TableBorderType::Single,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6419,9 +6414,6 @@ impl OpenCADStudio {
|
||||||
{
|
{
|
||||||
bd.is_invisible = !bd.is_invisible;
|
bd.is_invisible = !bd.is_invisible;
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6432,9 +6424,6 @@ impl OpenCADStudio {
|
||||||
if let Some(c) = Self::ts_cell_of(s, row) {
|
if let Some(c) = Self::ts_cell_of(s, row) {
|
||||||
c.fill_enabled = !c.fill_enabled;
|
c.fill_enabled = !c.fill_enabled;
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6456,9 +6445,6 @@ impl OpenCADStudio {
|
||||||
_ => CellAlignment::MiddleCenter,
|
_ => CellAlignment::MiddleCenter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6521,9 +6507,6 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6532,7 +6515,6 @@ impl OpenCADStudio {
|
||||||
use acadrust::objects::ObjectType;
|
use acadrust::objects::ObjectType;
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let name = self.tablestyle_selected.clone();
|
let name = self.tablestyle_selected.clone();
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
for obj in self.tabs[i].scene.document.objects.values_mut() {
|
for obj in self.tabs[i].scene.document.objects.values_mut() {
|
||||||
if let ObjectType::TableStyle(s) = obj {
|
if let ObjectType::TableStyle(s) = obj {
|
||||||
if s.name == name {
|
if s.name == name {
|
||||||
|
|
@ -6544,7 +6526,6 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6552,7 +6533,6 @@ impl OpenCADStudio {
|
||||||
use acadrust::objects::ObjectType;
|
use acadrust::objects::ObjectType;
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let name = self.tablestyle_selected.clone();
|
let name = self.tablestyle_selected.clone();
|
||||||
self.push_undo_snapshot(i, "TABLESTYLE EDIT");
|
|
||||||
for obj in self.tabs[i].scene.document.objects.values_mut() {
|
for obj in self.tabs[i].scene.document.objects.values_mut() {
|
||||||
if let ObjectType::TableStyle(s) = obj {
|
if let ObjectType::TableStyle(s) = obj {
|
||||||
if s.name == name {
|
if s.name == name {
|
||||||
|
|
@ -6560,7 +6540,6 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6627,6 +6606,7 @@ impl OpenCADStudio {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.mlstyle_window = Some(id);
|
self.mlstyle_window = Some(id);
|
||||||
|
self.style_stage_begin();
|
||||||
task.map(|_| Message::Noop)
|
task.map(|_| Message::Noop)
|
||||||
}
|
}
|
||||||
Message::MlStyleDialogClose => {
|
Message::MlStyleDialogClose => {
|
||||||
|
|
@ -6651,9 +6631,8 @@ impl OpenCADStudio {
|
||||||
.values()
|
.values()
|
||||||
.any(|o| matches!(o, ObjectType::MLineStyle(s) if s.name == name));
|
.any(|o| matches!(o, ObjectType::MLineStyle(s) if s.name == name));
|
||||||
if exists {
|
if exists {
|
||||||
self.push_undo_snapshot(i, "MLSTYLE SET");
|
// Staged: persists on Apply.
|
||||||
self.tabs[i].scene.document.header.multiline_style = name.clone();
|
self.tabs[i].scene.document.header.multiline_style = name.clone();
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output(&format!("Current multiline style: {}", name));
|
.push_output(&format!("Current multiline style: {}", name));
|
||||||
}
|
}
|
||||||
|
|
@ -6663,7 +6642,12 @@ impl OpenCADStudio {
|
||||||
// Apply pair as every other style manager. The editor is currently
|
// Apply pair as every other style manager. The editor is currently
|
||||||
// read-only, so there is nothing to apply yet — wire this up when
|
// read-only, so there is nothing to apply yet — wire this up when
|
||||||
// editable MLineStyle properties land.
|
// editable MLineStyle properties land.
|
||||||
Message::MlStyleApply => Task::none(),
|
Message::MlStyleApply => {
|
||||||
|
// Multiline styles have no editable properties yet; Apply still
|
||||||
|
// commits any staged structural / current-style changes.
|
||||||
|
self.style_stage_commit();
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
Message::MlStyleDialogNew => {
|
Message::MlStyleDialogNew => {
|
||||||
self.style_new(crate::app::StyleKind::MLine);
|
self.style_new(crate::app::StyleKind::MLine);
|
||||||
Task::none()
|
Task::none()
|
||||||
|
|
@ -6716,6 +6700,7 @@ impl OpenCADStudio {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.mleaderstyle_window = Some(id);
|
self.mleaderstyle_window = Some(id);
|
||||||
|
self.style_stage_begin();
|
||||||
task.map(|_| Message::Noop)
|
task.map(|_| Message::Noop)
|
||||||
}
|
}
|
||||||
Message::MLeaderStyleDialogClose => {
|
Message::MLeaderStyleDialogClose => {
|
||||||
|
|
@ -6802,9 +6787,6 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "MLEADERSTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6900,9 +6882,6 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "MLEADERSTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -6939,9 +6918,6 @@ impl OpenCADStudio {
|
||||||
"block_content_handle" => s.block_content_handle = handle,
|
"block_content_handle" => s.block_content_handle = handle,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "MLEADERSTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -7026,10 +7002,8 @@ impl OpenCADStudio {
|
||||||
if let Some(v) = bsz {
|
if let Some(v) = bsz {
|
||||||
s.block_content_scale_z = v;
|
s.block_content_scale_z = v;
|
||||||
}
|
}
|
||||||
self.push_undo_snapshot(i, "MLEADERSTYLE EDIT");
|
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
}
|
}
|
||||||
|
self.style_stage_commit();
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7067,6 +7041,7 @@ impl OpenCADStudio {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
self.dimstyle_window = Some(id);
|
self.dimstyle_window = Some(id);
|
||||||
|
self.style_stage_begin();
|
||||||
task.map(|_| Message::Noop)
|
task.map(|_| Message::Noop)
|
||||||
}
|
}
|
||||||
Message::DimStyleDialogClose => {
|
Message::DimStyleDialogClose => {
|
||||||
|
|
@ -7079,6 +7054,7 @@ impl OpenCADStudio {
|
||||||
Message::DimStyleDialogApply => {
|
Message::DimStyleDialogApply => {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.apply_dimstyle_bufs(i);
|
self.apply_dimstyle_bufs(i);
|
||||||
|
self.style_stage_commit();
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::DimStyleDialogSelect(name) => {
|
Message::DimStyleDialogSelect(name) => {
|
||||||
|
|
@ -7100,11 +7076,10 @@ impl OpenCADStudio {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
Message::DimStyleDialogSetCurrent => {
|
Message::DimStyleDialogSetCurrent => {
|
||||||
|
// Staged: persists on Apply.
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
self.push_undo_snapshot(i, "DIMSTYLE SETCURRENT");
|
|
||||||
self.tabs[i].scene.document.header.current_dimstyle_name =
|
self.tabs[i].scene.document.header.current_dimstyle_name =
|
||||||
self.dimstyle_selected.clone();
|
self.dimstyle_selected.clone();
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.sync_ribbon_styles();
|
self.sync_ribbon_styles();
|
||||||
self.command_line.push_output(&format!(
|
self.command_line.push_output(&format!(
|
||||||
"Current dim style set to '{}'.",
|
"Current dim style set to '{}'.",
|
||||||
|
|
@ -7147,7 +7122,7 @@ impl OpenCADStudio {
|
||||||
.map(|b| b.handle)
|
.map(|b| b.handle)
|
||||||
.unwrap_or(acadrust::types::Handle::NULL)
|
.unwrap_or(acadrust::types::Handle::NULL)
|
||||||
};
|
};
|
||||||
self.push_undo_snapshot(i, "DIMSTYLE EDIT");
|
// Staged: persists on Apply.
|
||||||
if let Some(ds) = self.tabs[i].scene.document.dim_styles.get_mut(&name) {
|
if let Some(ds) = self.tabs[i].scene.document.dim_styles.get_mut(&name) {
|
||||||
match field {
|
match field {
|
||||||
"dimblk" => ds.dimblk = handle,
|
"dimblk" => ds.dimblk = handle,
|
||||||
|
|
@ -7160,8 +7135,6 @@ impl OpenCADStudio {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.tabs[i].dirty = true;
|
|
||||||
self.tabs[i].scene.bump_geometry();
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7888,7 +7861,6 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
/// Write edit buffers back into the selected dim style document entry.
|
/// Write edit buffers back into the selected dim style document entry.
|
||||||
fn apply_dimstyle_bufs(&mut self, tab: usize) {
|
fn apply_dimstyle_bufs(&mut self, tab: usize) {
|
||||||
self.push_undo_snapshot(tab, "DIMSTYLE EDIT");
|
|
||||||
let doc = &mut self.tabs[tab].scene.document;
|
let doc = &mut self.tabs[tab].scene.document;
|
||||||
let Some(ds) = doc.dim_styles.get_mut(&self.dimstyle_selected) else {
|
let Some(ds) = doc.dim_styles.get_mut(&self.dimstyle_selected) else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -7979,7 +7951,6 @@ impl OpenCADStudio {
|
||||||
ds.dimtofl = self.ds_dimtofl;
|
ds.dimtofl = self.ds_dimtofl;
|
||||||
ds.dimalt = self.ds_dimalt;
|
ds.dimalt = self.ds_dimalt;
|
||||||
ds.dimapost = self.ds_dimapost.clone();
|
ds.dimapost = self.ds_dimapost.clone();
|
||||||
self.tabs[tab].dirty = true;
|
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output(&format!("DimStyle '{}' updated.", self.dimstyle_selected));
|
.push_output(&format!("DimStyle '{}' updated.", self.dimstyle_selected));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space,
|
button, checkbox, column, container, pick_list, row, scrollable, text, text_input,
|
||||||
};
|
};
|
||||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
//! Multiline Style Manager window — fills the entire OS window.
|
//! Multiline Style Manager window — fills the entire OS window.
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use iced::widget::{column, container, row, scrollable, text, Space};
|
use iced::widget::{column, container, row, scrollable, text};
|
||||||
use iced::{Background, Color, Element, Fill, Theme};
|
use iced::{Color, Element, Fill};
|
||||||
|
|
||||||
const DIM: Color = Color {
|
const DIM: Color = Color {
|
||||||
r: 0.55,
|
r: 0.55,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Column,
|
button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Column,
|
||||||
Space,
|
|
||||||
};
|
};
|
||||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue