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