#881: Tab in the Project panel's rename field commits and keeps the keyboard

Problem: Tab in a Project panel rename (or comment) field committed the
edit and egui moved keyboard focus to the next widget, the row's label
swatch. While it held focus, Enter no longer renamed (it "clicked" the
swatch), the arrow keys moved between widgets and app shortcuts paused.

Fix: the field keeps Tab through its event filter (arrows as before);
Tab surrenders focus and commits like Enter, so nothing else takes the
keyboard and the next Enter renames again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
vangel76 2026-10-11 18:41:17 +02:00
parent 67434e9038
commit 73404a2ad2
2 changed files with 33 additions and 2 deletions

View File

@ -447,9 +447,16 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
let cell_edit = |app: &mut EffectcraftApp, ui: &mut egui::Ui, actions: &mut Vec<(String, serde_json::Value)>, field: &str, cell: Rect| -> bool {
let Some((eid, ef, mut buf)) = editing.clone().filter(|(e, f, _)| *e == id.0 && f == field) else { return false };
let mut child = ui.new_child(egui::UiBuilder::new().max_rect(cell));
let resp = child.add(egui::TextEdit::singleline(&mut buf).desired_width(cell.width()).font(Tokens::ui(12.0)));
// The field keeps Tab (egui would move the keyboard to the next widget, where Enter no
// longer renames, #881): Tab commits like Enter and gives the keyboard back to the panel.
let filter = egui::EventFilter { tab: true, horizontal_arrows: true, vertical_arrows: true, ..Default::default() };
let resp = child.add(egui::TextEdit::singleline(&mut buf).desired_width(cell.width()).font(Tokens::ui(12.0)).event_filter(filter));
app.auto.add(&format!("project.item.{}.{field}Edit", id.0), cell, field);
if resp.lost_focus() {
let tab = resp.has_focus() && ctx.input(|i| i.key_pressed(egui::Key::Tab));
if tab {
resp.surrender_focus();
}
if resp.lost_focus() || tab {
ctx.data_mut(|d| d.remove::<Editing>(edit_id()));
if !ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
let (cmd, params) = if ef == "name" {

View File

@ -72,6 +72,30 @@ fn project_rename_replaces_the_name() {
assert!(!h.ctx.egui_wants_keyboard_input(), "Enter releases the keyboard");
}
/// #881: Tab in the Project panel's rename field commits the name like Enter and gives the keyboard
/// back to the panel; it doesn't leave keyboard focus on another widget (the row's label swatch),
/// where Enter would no longer rename and the arrow keys would move between widgets.
#[test]
fn tab_in_the_project_rename_field_commits_and_keeps_enter_renaming() {
let (mut h, comp) = harness();
let name = |h: &Harness<'_, EffectcraftApp>| h.state().session.project.items.values().find(|i| i.id.0 == comp).unwrap().name.clone();
let edit = format!("project.item.{comp}.nameEdit");
let editing = |h: &Harness<'_, EffectcraftApp>| h.state().auto.elements.iter().any(|e| e.id == edit);
let p = center(&h, &format!("project.item.{comp}.name"));
click(&mut h, p, 1);
key(&mut h, Key::Enter);
type_text(&mut h, "Hero");
key(&mut h, Key::Tab);
assert_eq!(name(&h), "Hero", "Tab commits the name");
assert!(!editing(&h), "Tab closes the rename field");
assert_eq!(h.ctx.memory(|m| m.focused()), None, "no other widget takes the keyboard");
key(&mut h, Key::Enter);
assert!(editing(&h), "Enter renames again");
type_text(&mut h, "Main");
key(&mut h, Key::Enter);
assert_eq!(name(&h), "Main");
}
#[test]
fn timecode_field_replaces_commits_and_cancels() {
let (mut h, _) = harness();