From c69db898623c0efebf920a30c33c9f4e98707cb6 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 11 Nov 2026 20:06:28 -0800 Subject: [PATCH] Justification: Auto Leading stores the shown percent, and the current choice is no edit The Auto Leading field shows whole percents, and it now stores the whole percent it shows, so a typed 500.4% (shown as 500%) is accepted as 500% instead of being refused. Picking the Single Word Justification the paragraphs already have is no longer an edit, so OK adds no undo step for it. --- crates/ui-egui/src/dialogs.rs | 16 ++++++++++++++-- crates/ui-egui/src/dialogs/tests.rs | 24 ++++++++++++++++++++---- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 6664fc349..1ad599bbd 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -7716,6 +7716,12 @@ const JUSTIFY_ROWS: [(&str, &str, (f64, f64)); 3] = [ /// The allowed Auto Leading, in percent of the type size. const AUTO_LEADING_RANGE: (f64, f64) = (0.0, 500.0); +/// The Auto Leading stored for a percent typed into its field: the whole percent the field shows. +fn auto_leading_typed(pct: f64) -> f64 { + // Adding 0.0 turns a rounded -0 into 0. + (pct.round() + 0.0) / 100.0 +} + /// The Single Word Justification choices: (attribute value, label). const SINGLE_WORD_CHOICES: [(&str, &str); 4] = [("left", "Align Left"), ("center", "Align Center"), ("right", "Align Right"), ("fullyJustified", "Full Justify")]; @@ -7941,7 +7947,7 @@ fn justification(lang: &str, ui: &mut egui::Ui, d: &mut Dialog, prefix: &str, cu crate::rtl::label(ui, crate::i18n::tr(lang, "Auto Leading:")); let auto = cur(d, "autoLeading").as_f64().map(|x| x * 100.0); if let Some(n) = ui.scope(|ui| crate::widgets::number(ui, &format!("ps{prefix}autoLeading"), auto, "%", 60.0, 0)).inner { - d.fields.insert(format!("{prefix}autoLeading"), json!(n / 100.0)); + d.fields.insert(format!("{prefix}autoLeading"), json!(auto_leading_typed(n))); } ui.end_row(); let single_label = crate::rtl::label(ui, crate::i18n::tr(lang, "Single Word Justification:")); @@ -7954,7 +7960,13 @@ fn justification(lang: &str, ui: &mut egui::Ui, d: &mut Dialog, prefix: &str, cu .show_ui(ui, |ui| { for (v, label) in SINGLE_WORD_CHOICES { if ui.selectable_label(single.as_str() == Some(v), crate::rtl::widget(ui, crate::i18n::tr(lang, label))).clicked() { - d.fields.insert(format!("{prefix}singleWordJustify"), json!(v)); + // The paragraphs' own choice is no edit, so OK adds no undo step for it. + let key = format!("{prefix}singleWordJustify"); + if current["singleWordJustify"].as_str() == Some(v) { + d.fields.remove(&key); + } else { + d.fields.insert(key, json!(v)); + } } } }) diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 1343b30d7..2e3cd162a 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -1156,8 +1156,15 @@ fn justification_dialog_sets_auto_leading_and_single_word_justification() { h.run_steps(3); assert!(h.query_by_label("Auto Leading:").is_some()); assert!(h.query_by_label("Single Word Justification:").is_some()); - // The dropdown shows the paragraph's Full Justify; pick Align Center. - h.get_by(|n| n.role() == egui::accesskit::Role::ComboBox && n.value().as_deref() == Some("Full Justify")).click(); + use egui::accesskit::Role; + // The dropdown shows the paragraph's Full Justify. Picking Full Justify again is no edit. + h.get_by(|n| n.role() == Role::ComboBox && n.value().as_deref() == Some("Full Justify")).click(); + h.run_steps(3); + h.get_by(|n| n.role() != Role::ComboBox && n.label().as_deref() == Some("Full Justify")).click(); + h.run_steps(3); + assert!(!h.state().ui.dialog.as_ref().unwrap().fields.contains_key("singleWordJustify"), "the paragraph's own choice is no edit"); + // Pick Align Center. + h.get_by(|n| n.role() == Role::ComboBox && n.value().as_deref() == Some("Full Justify")).click(); h.run_steps(3); h.get_by_label("Align Center").click(); h.run_steps(3); @@ -1170,8 +1177,8 @@ fn justification_dialog_sets_auto_leading_and_single_word_justification() { let d = app.ui.dialog.clone().unwrap(); let before = undo_steps(&app); let bad = [ - ("autoLeading", json!(5.004)), - ("autoLeading", json!(-0.004)), + ("autoLeading", json!(5.01)), + ("autoLeading", json!(-0.01)), ("autoLeading", json!(f64::NAN)), ("singleWordJustify", json!("leftJustified")), ]; @@ -1183,6 +1190,15 @@ fn justification_dialog_sets_auto_leading_and_single_word_justification() { } assert_eq!(undo_steps(&app), before); + // A typed percent is stored as the whole percent the field shows, so 500.4% (shown as 500%) + // and -0.4% (shown as 0%) are accepted. + for (typed, stored) in [(500.4, 5.0), (-0.4, 0.0), (149.6, 1.5)] { + assert_eq!(auto_leading_typed(typed), stored, "{typed}%"); + let mut probe = d.clone(); + probe.fields.insert("autoLeading".into(), json!(auto_leading_typed(typed))); + assert_eq!(check_justification(&probe, "", ¤t), Ok(()), "{typed}%"); + } + app.ui.dialog = Some(d); app.ui.dialog.as_mut().unwrap().fields.insert("autoLeading".into(), json!(1.5)); draw(&mut app, &ctx);