TUT4.2: reject malformed previews and cancel replaced dialogs

This commit is contained in:
moppeeer
2026-10-15 20:21:02 +02:00
parent e298455174
commit 7d4ddc3679
3 changed files with 54 additions and 1 deletions
+27
View File
@@ -339,6 +339,9 @@ fn handle_request(app: &mut DesignApp, ctx: &egui::Context, req: &ControlRequest
},
"ui.dialog.open" => {
let Some(id) = s("id") else { return err("missing `id`") };
// Replacing a modal cancels its transaction just like its Cancel button.
// In particular, an uncommitted style preview must not leak into the new dialog.
crate::dialogs::cancel(app);
if id == "findChange" || app.ui.dialog.as_ref().is_some_and(|d| d.id == "findChange") {
app.session.reset_find_scope();
}
@@ -440,6 +443,30 @@ pub(crate) fn page_index(p: &Value, default: usize, count: usize) -> Result<usiz
#[cfg(test)]
mod automation_contract_tests {
use super::*;
#[test]
fn replacing_a_style_preview_dialog_restores_the_document_and_undo_recording() {
let mut app = app();
let ctx = egui::Context::default();
let before = app.session.doc().unwrap().doc.clone();
let history = app.session.doc().unwrap().history.undo.len();
app.session.begin_interaction("Style Options").unwrap();
app.session.preview("style.paragraph.edit", json!({"name":designcraft_doc::BASIC_PARAGRAPH,"chars":{"size":24}})).unwrap();
app.ui.dialog = Some(crate::dialogs::Dialog::new(
"paragraphStyleOptions",
json!({
"name":designcraft_doc::BASIC_PARAGRAPH,"preview":true,"_previewed":{"chars":{"size":24}}
}),
));
assert_ne!(app.session.doc().unwrap().doc, before);
let response = call(&mut app, &ctx, "ui.dialog.open", json!({"id":"characterStyleOptions","fields":{"new":true}}));
assert_eq!(response["ok"], true);
assert_eq!(app.session.doc().unwrap().doc, before);
assert!(app.session.doc().unwrap().interaction.is_none());
assert_eq!(app.session.doc().unwrap().history.undo.len(), history);
app.run("style.paragraph.edit", json!({"name":designcraft_doc::BASIC_PARAGRAPH,"chars":{"size":18}})).unwrap();
assert_eq!(app.session.doc().unwrap().history.undo.len(), history + 1);
}
fn app() -> DesignApp {
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.session.execute("file.new", &json!({"pages":2})).unwrap();
+23 -1
View File
@@ -2834,7 +2834,9 @@ fn style_options_preview(app: &mut DesignApp, d: &mut Dialog) {
if params["para"][attr].is_null() {
params["para"][attr] = json!({});
}
params["para"][attr][field] = value.clone();
if let Some(fields) = params["para"][attr].as_object_mut() {
fields.insert(field.into(), value.clone());
}
} else {
params["para"][attr] = value.clone();
}
@@ -4954,6 +4956,26 @@ mod tests {
assert!(app.session.doc().unwrap().interaction.is_none());
}
#[test]
fn style_preview_rejects_conflicting_scalar_and_nested_rule_fields_without_panicking() {
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({})).unwrap();
let before = app.session.doc().unwrap().doc.clone();
let mut dialog = Dialog::new(
"paragraphStyleOptions",
json!({
"name": designcraft_doc::BASIC_PARAGRAPH, "preview": true,
"p.ruleAbove": false, "p.ruleAbove.weight": 2
}),
);
style_options_preview(&mut app, &mut dialog);
assert!(dialog.fields.contains_key("status"));
assert_eq!(app.session.doc().unwrap().doc, before);
app.ui.dialog = Some(dialog);
cancel(&mut app);
assert!(app.session.doc().unwrap().interaction.is_none());
}
#[test]
fn step_and_repeat_dialog_previews_and_makes_the_copies_as_one_undo_step() {
let mut app = DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());
+4
View File
@@ -61,6 +61,10 @@ context menus, typing at a caret) are things a layout artist does every minute,
## Remaining effort
Style Preview rejects conflicting scalar/nested automation fields without a panic. Replacing a
previewing dialog through the control channel cancels its transaction, restoring the original
document and normal undo recording before the replacement opens.
Paragraph and Character Style Options now preview edits to existing styles. Preview updates
every use, remains outside undo history, and restores the starting document when disabled or
cancelled. OK applies the final edit once. The color-preview step at about 4:00 in