UI: numeric forms open with their first number focused and selected

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
pravdomil 2026-10-11 23:36:15 +02:00
parent 8e1d742a9d
commit ffc481a4e4
1 changed files with 38 additions and 2 deletions

View File

@ -775,11 +775,16 @@ pub fn show_form(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens) {
let r = match &mut fl.kind {
FieldKind::Number { value, speed } => {
let dec = if *speed < 0.01 { 5 } else { 3 };
ui.add(crate::widgets::drag_value(egui::DragValue::new(value).speed(*speed).max_decimals(dec))).rect
let r = ui.add(crate::widgets::drag_value(egui::DragValue::new(value).speed(*speed).max_decimals(dec)));
// The first field takes the keyboard when the form opens. (A number edits
// as text once focused; the selection is clamped to what it shows.)
if !std::mem::replace(&mut focused, true) {
crate::widgets::focus_on_open(ui.ctx(), &r, &" ".repeat(64));
}
r.rect
}
FieldKind::Text(s) => {
let r = ui.add(egui::TextEdit::singleline(s).desired_width(220.0));
// The first text field takes the keyboard when the form opens.
if !std::mem::replace(&mut focused, true) {
crate::widgets::focus_on_open(ui.ctx(), &r, s);
}
@ -956,6 +961,37 @@ mod tests {
assert_eq!(f.params(), json!({"prop": "position", "value": [10.0, 20.0], "mode": "b"}));
}
/// A numeric form (Layer ▸ Transform ▸ Position…) opens with its first number being edited
/// and selected: typing replaces it, and one Enter confirms.
#[test]
fn a_numeric_form_opens_with_its_first_number_selected() {
let mut app = EffectcraftApp::new(effectcraft_engine::Session::default());
let ctx = egui::Context::default();
crate::theme::install(&ctx, &app.tokens, crate::i18n::language(&app));
app.dialog_state.form = Form {
title: "Position".into(),
command: "layer.setTransform".into(),
base: json!({"prop": "position"}),
fields: vec![Field::num("value[0]", "X", 960.0), Field::num("value[1]", "Y", 540.0)],
};
app.dialog = Some(Dialog::Form);
let frame = |app: &mut EffectcraftApp, events: Vec<egui::Event>| {
let raw = egui::RawInput { events, ..Default::default() };
ctx.run_ui(raw, |ui| {
app.auto.begin_frame();
crate::panels::dialogs::show(app, ui.ctx());
})
.textures_delta
.clear();
};
frame(&mut app, vec![]);
frame(&mut app, vec![]);
frame(&mut app, vec![egui::Event::Text("42".into())]);
assert_eq!(app.dialog_state.form.params()["value"], json!([42.0, 540.0]));
frame(&mut app, vec![egui::Event::Key { key: egui::Key::Enter, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() }]);
assert!(app.dialog.is_none(), "one Enter confirms");
}
/// File ▸ Export ▸ Adobe Premiere Pro Project… opens a form that says it writes Final Cut Pro
/// XML; its fields register automation ids and OK runs `file.exportTimeline`.
#[test]