From f11df30d234f9e20f4d16e51c34ad9384a97413a Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 4 Nov 2026 03:14:57 -0800 Subject: [PATCH] Type: Kerning menu lists Auto and Optical for mixed kerning modes A selection that mixed kerning modes left the Kerning dropdown without its Auto and Optical entries, so one mode could not be set on all of it. The menu now lists both entries with neither checked. --- crates/ui-egui/src/panels/character.rs | 11 +++++++++-- crates/ui-egui/src/panels/character/tests.rs | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) 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