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