Fix #322: the typed-value field takes the keyboard on the frames after the click

The click's own release took the focus straight back from a field created
in that frame, so typing went nowhere.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8
This commit is contained in:
Claude 2026-10-08 18:19:31 +00:00
parent 92c7237c50
commit 2b66eb4800
No known key found for this signature in database
1 changed files with 13 additions and 8 deletions

View File

@ -183,7 +183,9 @@ pub fn slider(ui: &mut Ui, spec: &ControlSpec, value: f64, enabled: bool, label_
register(ui.ctx(), format!("sliderValue:{}", spec.id), value_rect);
let typing_id = id.with("typing");
let field_id = id.with("typingField");
let mut typing: Option<String> = ui.data(|m| m.get_temp(typing_id));
// the text being typed and how many frames the field has been up: it takes the keyboard on its
// first frames (not on the click's own, whose release would take the focus straight back)
let mut typing: Option<(String, u8)> = ui.data(|m| m.get_temp(typing_id));
if enabled && typing.is_none() && value_resp.clicked() {
let text = shown_value(spec, value).trim_start_matches('+').to_string();
// the old value starts selected, so typing replaces it
@ -191,15 +193,14 @@ pub fn slider(ui: &mut Ui, spec: &ControlSpec, value: f64, enabled: bool, label_
let all = egui::text::CCursorRange::two(egui::text::CCursor::new(0), egui::text::CCursor::new(text.chars().count()));
state.cursor.set_char_range(Some(all));
state.store(ui.ctx(), field_id);
typing = Some(text);
ui.memory_mut(|m| m.request_focus(field_id));
typing = Some((text, 0));
}
let mut out = SliderOut::default();
let span = (spec.max - spec.min).max(1e-9);
let to_x = |v: f64| track_rect.left() + ((v - spec.min) / span).clamp(0.0, 1.0) as f32 * track_rect.width();
let from_x = |x: f32| spec.min + ((x - track_rect.left()) / track_rect.width()).clamp(0.0, 1.0) as f64 * span;
let mut v = value;
if let Some(mut text) = typing.take() {
if let Some((mut text, frames)) = typing.take() {
let field = egui::TextEdit::singleline(&mut text)
.id(field_id)
.font(t.font(12.5))
@ -207,7 +208,11 @@ pub fn slider(ui: &mut Ui, spec: &ControlSpec, value: f64, enabled: bool, label_
.desired_width(value_rect.width())
.margin(egui::Margin::ZERO);
let te = ui.put(value_rect, field);
if te.lost_focus() || !enabled {
if frames < 2 {
te.request_focus();
typing = Some((text.clone(), frames + 1));
ui.data_mut(|m| m.insert_temp(typing_id, (text, frames + 1)));
} else if !te.has_focus() || !enabled {
// Esc (or the slider turning off) keeps the old value; Return or clicking away applies
let cancelled = !enabled || ui.input(|i| i.key_pressed(egui::Key::Escape));
if let Some(nv) = typed_value(spec, &text).filter(|_| !cancelled) {
@ -218,10 +223,10 @@ pub fn slider(ui: &mut Ui, spec: &ControlSpec, value: f64, enabled: bool, label_
v = nv;
}
}
ui.data_mut(|m| m.remove::<String>(typing_id));
ui.data_mut(|m| m.remove::<(String, u8)>(typing_id));
} else {
typing = Some(text.clone());
ui.data_mut(|m| m.insert_temp(typing_id, text));
typing = Some((text.clone(), frames));
ui.data_mut(|m| m.insert_temp(typing_id, (text, frames)));
}
} else if resp.double_clicked() || label_resp.double_clicked() {
out.reset = true;