Fix #1457: a focused field follows its value when a slider or preview changes it

This commit is contained in:
bflatastic 2026-10-13 02:41:21 +07:00
parent 3acc29f8e0
commit 3672e0ae84
2 changed files with 44 additions and 1 deletions

View File

@ -9,6 +9,42 @@ use vectorcraft_doc::Unit;
use crate::widgets;
/// #1457: a focused field follows its value when that changes from outside (the slider beside
/// it), so the arrow keys step from the value shown and losing focus doesn't put the old one back.
#[test]
fn a_focused_field_follows_a_value_changed_from_outside() {
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut time = 0.0;
let mut frame = |value: f64, events: Vec<Event>| {
time += 0.05;
let screen_rect = Some(Rect::from_min_size(Pos2::ZERO, vec2(400.0, 200.0)));
let mut got = (None, Rect::NOTHING);
let mut out = ctx.run_ui(egui::RawInput { events, time: Some(time), screen_rect, focused: true, ..Default::default() }, |ui| {
got.0 = widgets::range_field(ui, "f", value, 0.0..=1000.0, "%", 2, 80.0);
got.1 = ui.min_rect();
});
out.textures_delta.clear();
got
};
let key = |key| Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE };
let button = |pos, pressed| Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE };
// Click into the field showing 100 %.
let at = frame(100.0, vec![]).1.center();
frame(100.0, vec![Event::PointerMoved(at), button(at, true)]);
frame(100.0, vec![button(at, false)]);
assert_eq!(frame(100.0, vec![]).0, None);
// The value changes under the focused field (a slider dragged to 142): ↓ steps from 142, not
// from the 100 the field held when it took the focus.
frame(142.0, vec![]);
assert_eq!(frame(142.0, vec![key(Key::ArrowDown)]).0, Some(141.0));
// And changed again, losing focus leaves the value alone.
frame(50.0, vec![]);
let away = at + vec2(0.0, 150.0);
frame(50.0, vec![Event::PointerMoved(away), button(away, true)]);
assert_eq!(frame(50.0, vec![button(away, false)]).0, None);
}
/// Headless frames of one field: its rect and id as last drawn.
struct Field {
ctx: egui::Context,

View File

@ -348,7 +348,14 @@ fn select_all_on_focus(ui: &Ui, resp: &Response, text: &str) {
fn recessed_text(ui: &mut Ui, id: egui::Id, shown: &str, width: f32, rows: usize) -> (String, Response, Rect) {
let t = Tokens::get(ui.ctx());
let editing = ui.memory(|m| m.has_focus(id));
let mut buf: String = if editing { ui.data_mut(|d| d.get_temp::<String>(id)).unwrap_or_else(|| shown.to_string()) } else { shown.to_string() };
// When the value changes under a focused field (the slider beside it, a preview, another
// field's edit), its text follows: the text typed for the old value would otherwise go back in
// on the next Enter, Tab or arrow key (#1457).
let shown_id = id.with("shown");
let followed = ui.data_mut(|d| d.get_temp::<String>(shown_id)).is_none_or(|before| before == shown);
ui.data_mut(|d| d.insert_temp(shown_id, shown.to_string()));
let typed = if editing && followed { ui.data_mut(|d| d.get_temp::<String>(id)) } else { None };
let mut buf: String = typed.unwrap_or_else(|| shown.to_string());
// The box is exactly as tall as the other controls of a row (26 points a line), its text
// centred in it: a frame around the text came out a point taller and stood out next to
// spinners and dropdowns (#900).