Kit · K02: Tab binds under FocusScope's ElyFocus context, so a deeper context's Tab wins

This commit is contained in:
ZacharyZhang-NY committed 2026-10-07 12:39:20 -04:00
1 parent c61a5b22b0
commit a9b30c7eab
7 files changed
+58 -11

No files matched your search

+3 -3
View File
@@ -50,7 +50,7 @@ pub use assets::Assets;
use anyhow::Context as _;
use gpui::{App, KeyBinding};
use crate::primitives::{FocusNext, FocusPrev, IconName};
use crate::primitives::{FOCUS_CONTEXT, FocusNext, FocusPrev, IconName};
/// Loads fonts and the theme, binds Tab and text keys. Call once, first.
pub fn init(cx: &mut App) -> anyhow::Result<()> {
@@ -76,8 +76,8 @@ pub fn init_for_tests(cx: &mut App) {
fn setup(cx: &mut App) {
theme::Theme::init(cx);
cx.bind_keys([
KeyBinding::new("tab", FocusNext, None),
KeyBinding::new("shift-tab", FocusPrev, None),
KeyBinding::new("tab", FocusNext, Some(FOCUS_CONTEXT)),
KeyBinding::new("shift-tab", FocusPrev, Some(FOCUS_CONTEXT)),
]);
forms::bind_keys(cx);
editor::bind_keys(cx);
+4 -1
View File
@@ -8,6 +8,9 @@ use crate::theme::ActiveTheme;
actions!(ely, [FocusNext, FocusPrev]);
/// The key context of every `FocusScope`; Tab and Shift-Tab bind under it.
pub(crate) const CONTEXT: &str = "ElyFocus";
/// Focus-colored border while focused. Give the element a 1px border.
pub trait FocusRing: InteractiveElement + Sized {
fn focus_ring(self, cx: &App) -> Self {
@@ -30,7 +33,7 @@ pub struct FocusScope {
impl FocusScope {
pub fn new(handle: &FocusHandle) -> Self {
Self {
base: div().track_focus(handle),
base: div().key_context(CONTEXT).track_focus(handle),
handle: handle.clone(),
trap: false,
root: false,
+3 -1
View File
@@ -20,8 +20,10 @@ pub use disclosure::Disclosure;
pub use divider::Divider;
pub use files::IconTheme;
pub(crate) use files::{file_icon, icon_theme};
pub(crate) use focus::{
CONTEXT as FOCUS_CONTEXT, Takeover, give_back, hand_back, hold_focus, tab_stop, take_focus,
};
pub use focus::{FocusNext, FocusPrev, FocusRing, FocusScope};
pub(crate) use focus::{Takeover, give_back, hand_back, hold_focus, tab_stop, take_focus};
pub use ghost::DragGhost;
pub use icon::{Icon, IconName};
pub use image::Image;
+44 -3
View File
@@ -1,11 +1,11 @@
use std::{cell::RefCell, rc::Rc};
use gpui::{
App, Context, Entity, FocusHandle, IntoElement, Modifiers, ParentElement, Render, RenderOnce,
Styled, TestAppContext, Window, anchored, div, point, prelude::*, px,
App, Context, Entity, FocusHandle, IntoElement, KeyBinding, Modifiers, ParentElement, Render,
RenderOnce, Styled, TestAppContext, Window, actions, anchored, div, point, prelude::*, px,
};
use super::raise;
use super::{FocusScope, raise};
use crate::theme::Theme;
/// A raised square holding a raised button, under a cover drawn after it in the same layer.
@@ -130,3 +130,44 @@ fn a_raise_keeps_its_state_while_another_comes_before_it(cx: &mut TestAppContext
"the box kept focus"
);
}
actions!(host, [Indent]);
/// A root scope around a field whose own context binds Tab.
struct Field {
root: FocusHandle,
field: FocusHandle,
indents: usize,
}
impl Render for Field {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> 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, |field, _| field.indents += 1)),
)
}
}
#[gpui::test]
fn a_deeper_context_keeps_its_own_tab_whatever_init_ran_last(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| Field {
root: cx.focus_handle(),
field: cx.focus_handle(),
indents: 0,
});
cx.update(|window, cx| {
let field = view.read(cx).field.clone();
window.focus(&field, cx);
});
cx.simulate_keystrokes("tab");
assert_eq!(view.read_with(cx, |field, _| field.indents), 1);
}