From 734f69ad725376d82b7ba436d8ee110180d09d3e Mon Sep 17 00:00:00 2001 From: DuBearte <130190089+DuBearte@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:20:21 -0400 Subject: [PATCH] Parameters: arrow keys move the cursor while renaming (#1248) Fixes #1165. --- crates/ui-egui/src/params_dialog.rs | 13 +++++++------ crates/ui-egui/tests/parameter_enter.rs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/crates/ui-egui/src/params_dialog.rs b/crates/ui-egui/src/params_dialog.rs index a8abf159..2f2f030e 100644 --- a/crates/ui-egui/src/params_dialog.rs +++ b/crates/ui-egui/src/params_dialog.rs @@ -240,17 +240,18 @@ fn table(app: &mut SolveApp, ui: &mut egui::Ui, d: &mut ParamsDialog, rows: &[&R d.error = app.run("parameters.rename", json!({"name": old, "new_name": new})).err(); } d.renaming = None; - } else { + } else if !resp.has_focus() { + // Only take focus once: requesting it every frame resets the text box's + // focus lock, so arrow keys would move focus away and end the rename. resp.request_focus(); } } _ => { let name = if r.source == "user" { RichText::new(&r.name) } else { RichText::new(&r.name).color(t.text_dim) }; - if ui - .add(egui::Label::new(name).sense(egui::Sense::click())) - .on_hover_text(crate::i18n::tr_label("Double-click to rename")) - .double_clicked() - { + let resp = + ui.add(egui::Label::new(name).sense(egui::Sense::click())).on_hover_text(crate::i18n::tr_label("Double-click to rename")); + crate::scenario::publish_handle(&format!("param:name:{}", r.name), resp.rect.center()); + if resp.double_clicked() { d.renaming = Some((r.name.clone(), r.name.clone())); } } diff --git a/crates/ui-egui/tests/parameter_enter.rs b/crates/ui-egui/tests/parameter_enter.rs index 39dbf8de..92fe227f 100644 --- a/crates/ui-egui/tests/parameter_enter.rs +++ b/crates/ui-egui/tests/parameter_enter.rs @@ -106,3 +106,21 @@ fn parameter_enter_comment_uses_selected_unit_and_unrelated_focus_does_not_submi assert!(result["parameters"].as_array().unwrap().iter().any(|p| p["name"] == "length" && p["unit"] == "cm" && p["comment"] == "Overall length")); assert_eq!(h.app.session.undo.len(), undo + 1); } +#[test] +fn parameter_rename_arrow_keys_move_cursor_and_keep_editing() { + let mut h = open(); + h.app.session.execute("parameters.add", &json!({"name":"width","expression":"10 mm"})).unwrap(); + // Let the click that opened the dialog age, or egui counts the double-click as a triple. + h.frames(30); + h.run(&json!([{"click":{"handle":"param:name:width"},"double":true}])).unwrap(); + h.frames(2); + let renaming = |h: &Harness| h.app.params.as_ref().unwrap().renaming.clone(); + assert_eq!(renaming(&h), Some(("width".to_string(), "width".to_string()))); + // Move the cursor to the start and type there; every arrow key keeps the rename open. + h.run(&json!([{"key":"ArrowLeft"}, {"key":"ArrowUp"}, {"key":"ArrowDown"}, {"key":"ArrowRight"}, {"key":"Home"}, {"text":"plate_"}])).unwrap(); + assert_eq!(renaming(&h), Some(("width".to_string(), "plate_width".to_string()))); + enter(&mut h, false); + assert_eq!(renaming(&h), None); + let result = h.app.session.execute("parameters.list", &json!({})).unwrap(); + assert!(result["parameters"].as_array().unwrap().iter().any(|p| p["name"] == "plate_width")); +}