From 713d81d64b97f6d682387c0c77d1d23d19d21d42 Mon Sep 17 00:00:00 2001 From: Marco Arnulfo Date: Tue, 13 Oct 2026 00:51:05 +0200 Subject: [PATCH] KEY-SLIDER-TYPE: a typed slider value keeps editing after the click is released (fixes #1362) egui gives the opening release's click to the readout, so the field under it surrenders the focus. Only the field's first two frames took it back: a click held any longer ended the edit, and the value could only be typed while holding the button. --- crates/ui-egui/src/widgets.rs | 12 ++++++++---- .../src/widgets/tests_numeric_entry.rs | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index 0872b2ec4..a1b881ef0 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -222,7 +222,10 @@ pub fn slider(ui: &mut Ui, spec: &ControlSpec, value: f64, enabled: bool, label_ let mut v = value; if let Some((mut text, frames, pressed)) = typing.take() { // Keep the owned opening release focused without restarting its draft or selection. - if pressed && value_released { + // egui gives that release's click to the readout, so the field under it gives up the focus + // whatever frame it lands on (issue #1362): the field takes it back after drawing too. + let opening_release = pressed && value_released; + if opening_release { ui.memory_mut(|m| m.request_focus(crate::text_field::id(&format!("sliderField:{}", spec.id)))); } let pressed = pressed && !value_released; @@ -237,10 +240,11 @@ pub fn slider(ui: &mut Ui, spec: &ControlSpec, value: f64, enabled: bool, label_ .select_on_focus(true) .begin_edit(frames == 0) .show(&mut child); - if frames < 2 && !matches!(field.ending, Some(crate::text_field::Ending::Return | crate::text_field::Ending::Escape)) { + if (frames < 2 || opening_release) && !matches!(field.ending, Some(crate::text_field::Ending::Return | crate::text_field::Ending::Escape)) { field.response.request_focus(); - typing = Some((text.clone(), frames + 1, pressed)); - ui.data_mut(|m| m.insert_temp(typing_id, (text, frames + 1, pressed))); + let frames = frames.saturating_add(1); + typing = Some((text.clone(), frames, pressed)); + ui.data_mut(|m| m.insert_temp(typing_id, (text, frames, pressed))); } else if field.ending.is_some() || !field.editing || !enabled { // Esc (or the slider turning off) keeps the old value; Return or clicking away applies let cancelled = !enabled || field.cancelled(); diff --git a/crates/ui-egui/src/widgets/tests_numeric_entry.rs b/crates/ui-egui/src/widgets/tests_numeric_entry.rs index 1935cde95..5e54ff064 100644 --- a/crates/ui-egui/src/widgets/tests_numeric_entry.rs +++ b/crates/ui-egui/src/widgets/tests_numeric_entry.rs @@ -75,3 +75,22 @@ fn dragging_away_from_readout_does_not_capture_keyboard() { } assert!(!h.view.ctx.egui_wants_keyboard_input(), "a cancelled readout gesture must not retain keyboard focus"); } + +#[test] +fn typing_after_a_released_click_still_edits() { + // Issue #1362: a normal click (press, a few frames held, release) must leave the field + // editing, so the value can be typed without holding the button down. + let mut h = Harness::new(); + let pos = h.rect.center(); + let button = |pressed| Event::PointerButton { pos, button: PointerButton::Primary, pressed, modifiers: Modifiers::NONE }; + h.frame(vec![Event::PointerMoved(pos)]); + h.frame(vec![button(true)]); + for _ in 0..4 { + h.frame(vec![]); + } + assert_eq!(h.frame(vec![button(false)]).value, None); + h.frame(vec![]); + assert!(h.view.ctx.egui_wants_keyboard_input(), "the field keeps the keyboard after the click"); + h.frame(vec![Event::Text("2".into())]); + assert_eq!(h.key(Key::Enter).value, Some(2.0)); +}