diff --git a/crates/keymap/src/ui/capture.rs b/crates/keymap/src/ui/capture.rs index a5fef16f1..612458fc4 100644 --- a/crates/keymap/src/ui/capture.rs +++ b/crates/keymap/src/ui/capture.rs @@ -15,6 +15,13 @@ pub(crate) fn lock_focus(ui: &Ui, id: Id) { ui.memory_mut(|m| m.set_focus_lock_filter(id, FOCUS_LOCK)); } +/// Gives Tab, arrows and Escape back to egui's focus navigation. egui keeps a focused widget's +/// filter until focus moves, so a widget that recorded calls this each frame it has focus and +/// does not record. +pub(crate) fn unlock_focus(ui: &Ui, id: Id) { + ui.memory_mut(|m| m.set_focus_lock_filter(id, EventFilter::default())); +} + /// Removes every key, text, IME and clipboard event from `input` and returns the chords pressed, /// in order. Repeats, releases and modifier-only presses give no chord. Copy, Cut and Paste /// count as the presses that cause them (Cmd+C, Cmd+X, Cmd+V, or Shift+Delete and Shift+Insert), diff --git a/crates/keymap/src/ui/view.rs b/crates/keymap/src/ui/view.rs index 8e5799cb3..7bebed9f3 100644 --- a/crates/keymap/src/ui/view.rs +++ b/crates/keymap/src/ui/view.rs @@ -486,6 +486,8 @@ impl Draw<'_, '_> { } else if r.has_focus() || r.clicked() { capture::lock_focus(ui, r.id); } + } else if r.has_focus() { + capture::unlock_focus(ui, r.id); } self.keyboard_toggle(ui); } @@ -1140,6 +1142,8 @@ impl Draw<'_, '_> { } else { capture::lock_focus(ui, field_id); } + } else if resp.has_focus() { + capture::unlock_focus(ui, field_id); } if ui.link(self.vis(&self.ui("Type…"))).clicked() { self.state.stop_recording(); diff --git a/crates/keymap/tests/ui_dialog.rs b/crates/keymap/tests/ui_dialog.rs index 4ff8bf91f..8df427734 100644 --- a/crates/keymap/tests/ui_dialog.rs +++ b/crates/keymap/tests/ui_dialog.rs @@ -210,6 +210,41 @@ fn the_new_shortcut_field_waits_for_assign() { assert_eq!(h.state().bindings("file.save"), ["Cmd+S", "Cmd+E"]); } +/// Presses Tab until the control labeled `label` has focus; false after `max` presses. +fn tab_to(h: &mut Harness<'_, Fx>, label: &str, max: usize) -> bool { + for _ in 0..max { + press(h, Modifiers::NONE, EguiKey::Tab); + if h.get_by_label(label).is_focused() { + return true; + } + } + false +} + +#[test] +fn tab_leaves_the_new_shortcut_field_after_recording() { + let mut h = mac(); + h.get_by_label("Save").click(); + h.run(); + click(&mut h, "New shortcut: None"); + press(&mut h, Modifiers::NONE, EguiKey::F7); + assert!(!h.state().state.is_recording()); + assert!(h.get_by_label("New shortcut: F7").is_focused()); + assert!(tab_to(&mut h, "Assign", 6), "Tab moves on to Assign"); + assert_eq!(h.state().bindings("file.save"), ["Cmd+S"], "Tab is not recorded"); +} + +#[test] +fn tab_leaves_search_by_key_after_recording() { + let mut h = mac(); + click(&mut h, "Search by key"); + press(&mut h, Modifiers::COMMAND, EguiKey::S); + assert!(!h.state().state.is_recording()); + assert!(h.get_by_label("Search by key").is_focused()); + press(&mut h, Modifiers::NONE, EguiKey::Tab); + assert!(table_focused(&h), "Tab moves on to the table"); +} + // ---- Conflicts ---- #[test]