From 029632b1f4a564a49a6d2f2b45da06277c0447dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 8 Oct 2026 14:30:43 -0400 Subject: [PATCH] F6 and Shift-F6 move focus on from a field that keeps Tab Documents and web pages take Tab for themselves; without another key a keyboard user could not leave them. --- src/lib.rs | 3 +++ src/primitives/tests.rs | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 33a5d09..8f78165 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,6 +79,9 @@ fn setup(cx: &mut App) { cx.bind_keys([ KeyBinding::new("tab", FocusNext, Some(FOCUS_CONTEXT)), KeyBinding::new("shift-tab", FocusPrev, Some(FOCUS_CONTEXT)), + // Out of a field that keeps Tab, such as a document. + KeyBinding::new("f6", FocusNext, Some(FOCUS_CONTEXT)), + KeyBinding::new("shift-f6", FocusPrev, Some(FOCUS_CONTEXT)), ]); forms::bind_keys(cx); editor::bind_keys(cx); diff --git a/src/primitives/tests.rs b/src/primitives/tests.rs index 47d7566..5a79ad9 100644 --- a/src/primitives/tests.rs +++ b/src/primitives/tests.rs @@ -171,3 +171,54 @@ fn a_deeper_context_keeps_its_own_tab_whatever_init_ran_last(cx: &mut TestAppCon cx.simulate_keystrokes("tab"); assert_eq!(view.read_with(cx, |field, _| field.indents), 1); } + +/// A field that keeps Tab, and a stop after it. +struct Kept { + root: FocusHandle, + field: FocusHandle, + after: FocusHandle, + indents: usize, +} + +impl Render for Kept { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let view = cx.entity(); + FocusScope::new(&self.root) + .root() + .size_full() + .child( + div() + .key_context("HostField") + .track_focus(&self.field) + .size(px(100.0)) + .on_action(move |_: &Indent, _, cx| view.update(cx, |kept, _| kept.indents += 1)), + ) + .child(div().track_focus(&self.after).size(px(100.0))) + } +} + +#[gpui::test] +fn f6_leaves_a_field_that_keeps_tab(cx: &mut TestAppContext) { + cx.update(|cx| { + cx.bind_keys([KeyBinding::new("tab", Indent, Some("HostField"))]); + crate::init_for_tests(cx); + }); + let (view, cx) = cx.add_window_view(|_, cx| Kept { + root: cx.focus_handle(), + field: cx.focus_handle().tab_stop(true), + after: cx.focus_handle().tab_stop(true), + indents: 0, + }); + cx.update(|window, cx| { + let field = view.read(cx).field.clone(); + window.focus(&field, cx); + }); + cx.simulate_keystrokes("tab"); + let after = view.read_with(cx, |kept, _| kept.after.clone()); + cx.simulate_keystrokes("f6"); + assert_eq!(view.read_with(cx, |kept, _| kept.indents), 1, "Tab stayed in the field"); + cx.update(|window, cx| assert!(after.is_focused(window), "F6 moved on")); + cx.simulate_keystrokes("shift-f6"); + let field = view.read_with(cx, |kept, _| kept.field.clone()); + cx.update(|window, _| assert!(field.is_focused(window), "Shift-F6 came back")); +}