diff --git a/crates/ui-egui/src/panels/character.rs b/crates/ui-egui/src/panels/character.rs index da5c6d410..98c3be6bc 100644 --- a/crates/ui-egui/src/panels/character.rs +++ b/crates/ui-egui/src/panels/character.rs @@ -316,6 +316,14 @@ fn caret_kerning(app: &VectorcraftApp, ui: &Ui) -> Option<(f64, bool)> { Some((value, true)) } +/// The check marks of the Kerning menu's Auto and Optical entries for the selection's kerning +/// `mode` (Optical on, manual value). A selection that mixes modes checks neither, and the menu +/// still lists both. +fn kerning_methods(mode: Option<(bool, Option)>) -> (Option, Option) { + let (automatic, optical) = mode.map_or((false, false), |(optical, manual)| (!optical && manual.is_none(), optical)); + (Some(automatic), Some(optical)) +} + /// The faces Type › Bold and Type › Italic switch to. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Face { @@ -756,8 +764,7 @@ pub(crate) fn metrics_grid(app: &mut VectorcraftApp, ui: &mut Ui, id: &str, s: & let caret = text_editing(app).is_some_and(|(_, a, b)| a == b); let at_caret = if caret { caret_kerning(app, ui) } else { None }; let kerning = at_caret.map(|(v, _)| v).or_else(|| mode.map(|(_, manual)| manual.unwrap_or(0.0))); - let automatic = mode.map(|(optical, manual)| !optical && manual.is_none()); - let optical = mode.map(|(optical, _)| optical); + let (automatic, optical) = kerning_methods(mode); match widgets::spin_kerning( ui, "ch-kern", diff --git a/crates/ui-egui/src/panels/character/tests.rs b/crates/ui-egui/src/panels/character/tests.rs index 7716c3bb4..7cdfddad3 100644 --- a/crates/ui-egui/src/panels/character/tests.rs +++ b/crates/ui-egui/src/panels/character/tests.rs @@ -258,6 +258,26 @@ fn character_kerning_setter_and_automatic_pair_value() { assert_eq!(shared(&app, |s| (s.optical_kerning, s.kerning)), None, "a mixed range shows a blank Kerning field"); } +/// The Kerning menu lists Auto and Optical for a selection that mixes kerning modes, with +/// neither checked, so one mode can be set on all of it. +#[test] +fn kerning_menu_lists_auto_and_optical_for_mixed_modes() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 300, "height": 200})).unwrap(); + let a = app.run("text.create", json!({"x": 20, "y": 50, "text": "AV", "size": 12})).unwrap()["id"].clone(); + let b = app.run("text.create", json!({"x": 20, "y": 90, "text": "AV", "size": 12})).unwrap()["id"].clone(); + app.run("select.set", json!({"ids": [b.clone()]})).unwrap(); + app.run("text.setFormat", json!({"kerning": "optical"})).unwrap(); + app.run("select.set", json!({"ids": [a.clone()]})).unwrap(); + assert_eq!(kerning_methods(shared(&app, |s| (s.optical_kerning, s.kerning))), (Some(true), Some(false)), "Auto"); + app.run("select.set", json!({"ids": [b.clone()]})).unwrap(); + assert_eq!(kerning_methods(shared(&app, |s| (s.optical_kerning, s.kerning))), (Some(false), Some(true)), "Optical"); + app.run("select.set", json!({"ids": [a, b]})).unwrap(); + let mode = shared(&app, |s| (s.optical_kerning, s.kerning)); + assert_eq!(mode, None, "the selection mixes Auto and Optical"); + assert_eq!(kerning_methods(mode), (Some(false), Some(false)), "both entries are listed, neither checked"); +} + /// Highlight Kerning and Highlight Tracking show the Character panel and give its field the /// keyboard focus with the value selected: what is typed replaces it, and Enter applies it. /// Highlight Font Name opens the font family menu with its search field focused. Without