fix(ui): refresh font-size text when selection becomes mixed

Include the mixed-size flag in the font-size draft cache key. Selecting a mixed block can preserve the previous representative number, so a value-only key left the field showing 24 instead of Mixed. Unchanged formatting still preserves partially typed input across frames.

The headless regression covers uniform-to-mixed and mixed-to-uniform transitions with the same number, draft preservation and a single committed edit. Linux scoped clippy, tests, wasm, formatting, layering and assets pass; reverting the cache fix reproduces the stale display.

Co-Authored-By: Codex GPT-6.1 <noreply@openai.com>
This commit is contained in:
FredApps
2026-11-01 16:18:26 +01:00
co-authored by Codex GPT-6.1
parent e7762568cc
commit b7d04009a2
+49 -3
View File
@@ -465,8 +465,10 @@ fn font_size_input(ui: &mut egui::Ui, size: &mut f64, mixed: bool) -> bool {
let initial_text = || {
if mixed || *size <= 0.0 { tl!("Mixed").to_string() } else { size.to_string() }
};
let (previous, mut text) = ui.data(|d| d.get_temp::<(u64, String)>(id)).unwrap_or_else(|| (size.to_bits(), initial_text()));
if previous != size.to_bits() {
// A selection can become mixed without changing its representative numeric size.
let (previous, previous_mixed, mut text) =
ui.data(|d| d.get_temp::<(u64, bool, String)>(id)).unwrap_or_else(|| (size.to_bits(), mixed, initial_text()));
if previous != size.to_bits() || previous_mixed != mixed {
text = initial_text();
}
let field = egui::TextEdit::singleline(&mut text).id(id).desired_width(40.0);
@@ -502,7 +504,7 @@ fn font_size_input(ui: &mut egui::Ui, size: &mut f64, mixed: bool) -> bool {
if *size != before {
text = size.to_string();
}
ui.data_mut(|d| d.insert_temp(id, (size.to_bits(), text)));
ui.data_mut(|d| d.insert_temp(id, (size.to_bits(), mixed, text)));
});
committed
}
@@ -539,6 +541,50 @@ mod typed_size_tests {
}
}
#[test]
fn mixed_size_changes_refresh_the_field_without_discarding_a_draft() {
let mut h = egui_kittest::Harness::new_ui_state(
|ui, state: &mut (f64, bool, usize)| {
if font_size_input(ui, &mut state.0, state.1) {
state.1 = false;
state.2 += 1;
}
},
(12.0, false, 0),
);
h.run();
let field =
|h: &egui_kittest::Harness<'_, (f64, bool, usize)>| h.get_by_role_and_label(egui::accesskit::Role::TextInput, "Font size:").value();
assert_eq!(field(&h).as_deref(), Some("12"));
h.state_mut().0 = 24.0;
h.run_steps(2);
assert_eq!(field(&h).as_deref(), Some("24"), "the heading selection updates the size");
h.state_mut().1 = true;
h.run_steps(2);
assert_eq!(field(&h).as_deref(), Some("Mixed"), "collapsing to a mixed block must refresh even when its representative size stays 24");
if let Ok(path) = std::env::var("PDFCRAFT_MIXED_SIZE_SHOT") {
h.render().unwrap().save(path).unwrap();
}
h.state_mut().1 = false;
h.run_steps(2);
assert_eq!(field(&h).as_deref(), Some("24"), "selecting the heading again clears Mixed without a numeric change");
h.state_mut().1 = true;
h.run_steps(2);
h.get_by_role_and_label(egui::accesskit::Role::TextInput, "Font size:").click();
h.run();
h.event(egui::Event::Key { key: egui::Key::A, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::COMMAND });
h.event(egui::Event::Text("1".into()));
h.run_steps(3);
assert_eq!(field(&h).as_deref(), Some("1"), "an unchanged mixed style must preserve the draft");
h.event(egui::Event::Text("8".into()));
h.run_steps(3);
assert_eq!(field(&h).as_deref(), Some("18"));
assert_eq!(*h.state(), (24.0, true, 0), "typing does not apply a formatting change");
h.event(egui::Event::Key { key: egui::Key::Enter, physical_key: None, pressed: true, repeat: false, modifiers: egui::Modifiers::NONE });
h.run_steps(2);
assert_eq!(*h.state(), (18.0, false, 1));
}
#[test]
fn typed_size_commits_once_and_invalid_input_preserves_the_style() {
let mut h = egui_kittest::Harness::new_ui_state(