1use gpui::{
2 App, Application, Bounds, Context, Div, ElementId, FocusHandle, KeyBinding, SharedString,
3 Stateful, Window, WindowBounds, WindowOptions, actions, div, prelude::*, px, size,
4};
5
6actions!(example, [Tab, TabPrev]);
7
8struct Example {
9 items: Vec<FocusHandle>,
10 message: SharedString,
11}
12
13impl Example {
14 fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
15 let items = vec![
16 cx.focus_handle().tab_index(1).tab_stop(true),
17 cx.focus_handle().tab_index(2).tab_stop(true),
18 cx.focus_handle().tab_index(3).tab_stop(true),
19 cx.focus_handle(),
20 cx.focus_handle().tab_index(2).tab_stop(true),
21 ];
22
23 window.focus(items.first().unwrap());
24 Self {
25 items,
26 message: SharedString::from("Press `Tab`, `Shift-Tab` to switch focus."),
27 }
28 }
29
30 fn on_tab(&mut self, _: &Tab, window: &mut Window, _: &mut Context<Self>) {
31 window.focus_next();
32 self.message = SharedString::from("You have pressed `Tab`.");
33 }
34
35 fn on_tab_prev(&mut self, _: &TabPrev, window: &mut Window, _: &mut Context<Self>) {
36 window.focus_prev();
37 self.message = SharedString::from("You have pressed `Shift-Tab`.");
38 }
39}
40
41impl Render for Example {
42 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
43 fn button(id: impl Into<ElementId>) -> Stateful<Div> {
44 div()
45 .id(id)
46 .h_10()
47 .flex_1()
48 .flex()
49 .justify_center()
50 .items_center()
51 .border_1()
52 .border_color(gpui::black())
53 .bg(gpui::black())
54 .text_color(gpui::white())
55 .focus(|this| this.border_color(gpui::blue()))
56 .shadow_sm()
57 }
58
59 div()
60 .id("app")
61 .on_action(cx.listener(Self::on_tab))
62 .on_action(cx.listener(Self::on_tab_prev))
63 .size_full()
64 .flex()
65 .flex_col()
66 .p_4()
67 .gap_3()
68 .bg(gpui::white())
69 .text_color(gpui::black())
70 .child(self.message.clone())
71 .children(
72 self.items
73 .clone()
74 .into_iter()
75 .enumerate()
76 .map(|(ix, item_handle)| {
77 div()
78 .id(("item", ix))
79 .track_focus(&item_handle)
80 .h_10()
81 .w_full()
82 .flex()
83 .justify_center()
84 .items_center()
85 .border_1()
86 .border_color(gpui::black())
87 .when(
88 item_handle.tab_stop && item_handle.is_focused(window),
89 |this| this.border_color(gpui::blue()),
90 )
91 .map(|this| match item_handle.tab_stop {
92 true => this
93 .hover(|this| this.bg(gpui::black().opacity(0.1)))
94 .child(format!("tab_index: {}", item_handle.tab_index)),
95 false => this.opacity(0.4).child("tab_stop: false"),
96 })
97 }),
98 )
99 .child(
100 div()
101 .flex()
102 .flex_row()
103 .gap_3()
104 .items_center()
105 .child(button("el1").tab_index(4).child("Button 1"))
106 .child(button("el2").tab_index(5).child("Button 2")),
107 )
108 }
109}
110
111fn main() {
112 Application::new().run(|cx: &mut App| {
113 cx.bind_keys([
114 KeyBinding::new("tab", Tab, None),
115 KeyBinding::new("shift-tab", TabPrev, None),
116 ]);
117
118 let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx);
119 cx.open_window(
120 WindowOptions {
121 window_bounds: Some(WindowBounds::Windowed(bounds)),
122 ..Default::default()
123 },
124 |window, cx| cx.new(|cx| Example::new(window, cx)),
125 )
126 .unwrap();
127
128 cx.activate(true);
129 });
130}