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 focus_handle: FocusHandle,
10 items: Vec<FocusHandle>,
11 message: SharedString,
12}
13
14impl Example {
15 fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
16 let items = vec![
17 cx.focus_handle().tab_index(1).tab_stop(true),
18 cx.focus_handle().tab_index(2).tab_stop(true),
19 cx.focus_handle().tab_index(3).tab_stop(true),
20 cx.focus_handle(),
21 cx.focus_handle().tab_index(2).tab_stop(true),
22 ];
23
24 let focus_handle = cx.focus_handle();
25 window.focus(&focus_handle, cx);
26
27 Self {
28 focus_handle,
29 items,
30 message: SharedString::from("Press `Tab`, `Shift-Tab` to switch focus."),
31 }
32 }
33
34 fn on_tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
35 window.focus_next(cx);
36 self.message = SharedString::from("You have pressed `Tab`.");
37 }
38
39 fn on_tab_prev(&mut self, _: &TabPrev, window: &mut Window, cx: &mut Context<Self>) {
40 window.focus_prev(cx);
41 self.message = SharedString::from("You have pressed `Shift-Tab`.");
42 }
43}
44
45impl Render for Example {
46 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
47 fn tab_stop_style<T: Styled>(this: T) -> T {
48 this.border_3().border_color(gpui::blue())
49 }
50
51 fn button(id: impl Into<ElementId>) -> Stateful<Div> {
52 div()
53 .id(id)
54 .h_10()
55 .flex_1()
56 .flex()
57 .justify_center()
58 .items_center()
59 .border_1()
60 .border_color(gpui::black())
61 .bg(gpui::black())
62 .text_color(gpui::white())
63 .focus(tab_stop_style)
64 .shadow_sm()
65 }
66
67 div()
68 .id("app")
69 .track_focus(&self.focus_handle)
70 .on_action(cx.listener(Self::on_tab))
71 .on_action(cx.listener(Self::on_tab_prev))
72 .size_full()
73 .flex()
74 .flex_col()
75 .p_4()
76 .gap_3()
77 .bg(gpui::white())
78 .text_color(gpui::black())
79 .child(self.message.clone())
80 .children(
81 self.items
82 .clone()
83 .into_iter()
84 .enumerate()
85 .map(|(ix, item_handle)| {
86 div()
87 .id(("item", ix))
88 .track_focus(&item_handle)
89 .h_10()
90 .w_full()
91 .flex()
92 .justify_center()
93 .items_center()
94 .border_1()
95 .border_color(gpui::black())
96 .when(
97 item_handle.tab_stop && item_handle.is_focused(window),
98 tab_stop_style,
99 )
100 .map(|this| match item_handle.tab_stop {
101 true => this
102 .hover(|this| this.bg(gpui::black().opacity(0.1)))
103 .child(format!("tab_index: {}", item_handle.tab_index)),
104 false => this.opacity(0.4).child("tab_stop: false"),
105 })
106 }),
107 )
108 .child(
109 div()
110 .flex()
111 .flex_row()
112 .gap_3()
113 .items_center()
114 .child(
115 button("el1")
116 .tab_index(4)
117 .child("Button 1")
118 .on_click(cx.listener(|this, _, _, cx| {
119 this.message = "You have clicked Button 1.".into();
120 cx.notify();
121 })),
122 )
123 .child(
124 button("el2")
125 .tab_index(5)
126 .child("Button 2")
127 .on_click(cx.listener(|this, _, _, cx| {
128 this.message = "You have clicked Button 2.".into();
129 cx.notify();
130 })),
131 ),
132 )
133 .child(
134 div()
135 .id("group-1")
136 .tab_index(6)
137 .tab_group()
138 .tab_stop(false)
139 .child(
140 button("group-1-button-1")
141 .tab_index(1)
142 .child("Tab index [6, 1]"),
143 )
144 .child(
145 button("group-1-button-2")
146 .tab_index(2)
147 .child("Tab index [6, 2]"),
148 )
149 .child(
150 button("group-1-button-3")
151 .tab_index(3)
152 .child("Tab index [6, 3]"),
153 ),
154 )
155 .child(
156 div()
157 .id("group-2")
158 .tab_index(7)
159 .tab_group()
160 .tab_stop(false)
161 .child(
162 button("group-2-button-1")
163 .tab_index(1)
164 .child("Tab index [7, 1]"),
165 )
166 .child(
167 button("group-2-button-2")
168 .tab_index(2)
169 .child("Tab index [7, 2]"),
170 )
171 .child(
172 button("group-2-button-3")
173 .tab_index(3)
174 .child("Tab index [7, 3]"),
175 ),
176 )
177 }
178}
179
180fn main() {
181 Application::new().run(|cx: &mut App| {
182 cx.bind_keys([
183 KeyBinding::new("tab", Tab, None),
184 KeyBinding::new("shift-tab", TabPrev, None),
185 ]);
186
187 let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx);
188 cx.open_window(
189 WindowOptions {
190 window_bounds: Some(WindowBounds::Windowed(bounds)),
191 ..Default::default()
192 },
193 |window, cx| cx.new(|cx| Example::new(window, cx)),
194 )
195 .unwrap();
196
197 cx.activate(true);
198 });
199}