gpui-focus-handle by longbridge/gpui-component
npx skills add https://github.com/longbridge/gpui-component --skill gpui-focus-handleGPUI 的焦点系统支持键盘导航和焦点管理。
核心概念:
struct FocusableComponent {
focus_handle: FocusHandle,
}
impl FocusableComponent {
fn new(cx: &mut Context<Self>) -> Self {
Self {
focus_handle: cx.focus_handle(),
}
}
}
impl Render for FocusableComponent {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.on_action(cx.listener(Self::on_enter))
.child("Focusable content")
}
fn on_enter(&mut self, _: &Enter, cx: &mut Context<Self>) {
// 当获得焦点时处理 Enter 键
cx.notify();
}
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
impl MyComponent {
fn focus(&mut self, cx: &mut Context<Self>) {
self.focus_handle.focus(cx);
}
fn is_focused(&self, cx: &App) -> bool {
self.focus_handle.is_focused(cx)
}
fn blur(&mut self, cx: &mut Context<Self>) {
cx.blur();
}
}
impl Render for MyInput {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let is_focused = self.focus_handle.is_focused(cx);
div()
.track_focus(&self.focus_handle)
.on_focus(cx.listener(|this, _event, cx| {
this.on_focus(cx);
}))
.on_blur(cx.listener(|this, _event, cx| {
this.on_blur(cx);
}))
.when(is_focused, |el| {
el.bg(cx.theme().focused_background)
})
.child(self.render_content())
}
}
impl MyInput {
fn on_focus(&mut self, cx: &mut Context<Self>) {
// 处理获得焦点
cx.notify();
}
fn on_blur(&mut self, cx: &mut Context<Self>) {
// 处理失去焦点
cx.notify();
}
}
使用 track_focus() 的元素会自动参与 Tab 键导航。
div()
.child(
input1.track_focus(&focus1) // Tab 顺序: 1
)
.child(
input2.track_focus(&focus2) // Tab 顺序: 2
)
.child(
input3.track_focus(&focus3) // Tab 顺序: 3
)
impl Container {
fn focus_first(&mut self, cx: &mut Context<Self>) {
if let Some(first) = self.children.first() {
first.update(cx, |child, cx| {
child.focus_handle.focus(cx);
});
}
}
fn focus_next(&mut self, cx: &mut Context<Self>) {
// 自定义焦点导航逻辑
}
}
impl MyDialog {
fn new(cx: &mut Context<Self>) -> Self {
let focus_handle = cx.focus_handle();
// 创建时聚焦
focus_handle.focus(cx);
Self { focus_handle }
}
}
impl Modal {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.on_key_down(cx.listener(|this, event: &KeyDownEvent, cx| {
if event.key == Key::Tab {
// 将焦点保持在模态框内
this.focus_next_in_modal(cx);
cx.stop_propagation();
}
}))
.child(self.render_content())
}
}
impl Searchable {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.when(self.search_active, |el| {
el.on_mount(cx.listener(|this, _, cx| {
this.focus_handle.focus(cx);
}))
})
.child(self.search_input())
}
}
// ✅ 良好:为键盘交互追踪焦点
input()
.track_focus(&self.focus_handle)
.on_action(cx.listener(Self::on_enter))
let is_focused = self.focus_handle.is_focused(cx);
div()
.when(is_focused, |el| {
el.border_color(cx.theme().focused_border)
})
// ❌ 不好:没有 track_focus,键盘导航将无法工作
div()
.on_action(cx.listener(Self::on_enter))
每周安装量
135
仓库
GitHub 星标数
10.7K
首次出现
2026年1月21日
安全审计
安装于
opencode123
codex117
gemini-cli116
github-copilot108
cursor106
claude-code102
GPUI's focus system enables keyboard navigation and focus management.
Key Concepts:
struct FocusableComponent {
focus_handle: FocusHandle,
}
impl FocusableComponent {
fn new(cx: &mut Context<Self>) -> Self {
Self {
focus_handle: cx.focus_handle(),
}
}
}
impl Render for FocusableComponent {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.on_action(cx.listener(Self::on_enter))
.child("Focusable content")
}
fn on_enter(&mut self, _: &Enter, cx: &mut Context<Self>) {
// Handle Enter key when focused
cx.notify();
}
}
impl MyComponent {
fn focus(&mut self, cx: &mut Context<Self>) {
self.focus_handle.focus(cx);
}
fn is_focused(&self, cx: &App) -> bool {
self.focus_handle.is_focused(cx)
}
fn blur(&mut self, cx: &mut Context<Self>) {
cx.blur();
}
}
impl Render for MyInput {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let is_focused = self.focus_handle.is_focused(cx);
div()
.track_focus(&self.focus_handle)
.on_focus(cx.listener(|this, _event, cx| {
this.on_focus(cx);
}))
.on_blur(cx.listener(|this, _event, cx| {
this.on_blur(cx);
}))
.when(is_focused, |el| {
el.bg(cx.theme().focused_background)
})
.child(self.render_content())
}
}
impl MyInput {
fn on_focus(&mut self, cx: &mut Context<Self>) {
// Handle focus gained
cx.notify();
}
fn on_blur(&mut self, cx: &mut Context<Self>) {
// Handle focus lost
cx.notify();
}
}
Elements with track_focus() automatically participate in Tab navigation.
div()
.child(
input1.track_focus(&focus1) // Tab order: 1
)
.child(
input2.track_focus(&focus2) // Tab order: 2
)
.child(
input3.track_focus(&focus3) // Tab order: 3
)
impl Container {
fn focus_first(&mut self, cx: &mut Context<Self>) {
if let Some(first) = self.children.first() {
first.update(cx, |child, cx| {
child.focus_handle.focus(cx);
});
}
}
fn focus_next(&mut self, cx: &mut Context<Self>) {
// Custom focus navigation logic
}
}
impl MyDialog {
fn new(cx: &mut Context<Self>) -> Self {
let focus_handle = cx.focus_handle();
// Focus when created
focus_handle.focus(cx);
Self { focus_handle }
}
}
impl Modal {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.on_key_down(cx.listener(|this, event: &KeyDownEvent, cx| {
if event.key == Key::Tab {
// Keep focus within modal
this.focus_next_in_modal(cx);
cx.stop_propagation();
}
}))
.child(self.render_content())
}
}
impl Searchable {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.when(self.search_active, |el| {
el.on_mount(cx.listener(|this, _, cx| {
this.focus_handle.focus(cx);
}))
})
.child(self.search_input())
}
}
// ✅ Good: Track focus for keyboard interaction
input()
.track_focus(&self.focus_handle)
.on_action(cx.listener(Self::on_enter))
let is_focused = self.focus_handle.is_focused(cx);
div()
.when(is_focused, |el| {
el.border_color(cx.theme().focused_border)
})
// ❌ Bad: No track_focus, keyboard navigation won't work
div()
.on_action(cx.listener(Self::on_enter))
Weekly Installs
135
Repository
GitHub Stars
10.7K
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode123
codex117
gemini-cli116
github-copilot108
cursor106
claude-code102
agentation - AI智能体可视化UI反馈工具,连接人眼与代码的桥梁
5,400 周安装