name: gpui-focus-handle description: GPUI中的焦点管理和键盘导航。用于处理焦点、焦点句柄或键盘导航。启用键盘驱动的接口,具有正确的焦点跟踪和可聚焦元素之间的导航。
概述
GPUI的焦点系统启用键盘导航和焦点管理。
关键概念:
- FocusHandle:可聚焦元素的引用
- 焦点跟踪:当前聚焦的元素
- 键盘导航:使用Tab/Shift-Tab在元素之间导航
- 焦点事件:on_focus, on_blur
快速入门
创建焦点句柄
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("可聚焦内容")
}
fn on_enter(&mut self, _: &Enter, cx: &mut Context<Self>) {
// 当聚焦时处理Enter键
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>) {
// 处理获得焦点
cx.notify();
}
fn on_blur(&mut self, cx: &mut Context<Self>) {
// 处理失去焦点
cx.notify();
}
}
键盘导航
Tab顺序
使用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>) {
// 自定义焦点导航逻辑
}
}
常见模式
1. 挂载时自动聚焦
impl MyDialog {
fn new(cx: &mut Context<Self>) -> Self {
let focus_handle = cx.focus_handle();
// 创建时聚焦
focus_handle.focus(cx);
Self { focus_handle }
}
}
2. 焦点陷阱(模态框)
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())
}
}
3. 条件性焦点
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))
参考文档
- API参考:参见api-reference.md
- FocusHandle API,焦点管理
- 事件,键盘导航
- 最佳实践