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);
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, _: &mut Context<Self>) {
35 window.focus_next();
36 self.message = SharedString::from("You have pressed `Tab`.");
37 }
38
39 fn on_tab_prev(&mut self, _: &TabPrev, window: &mut Window, _: &mut Context<Self>) {
40 window.focus_prev();
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(button("el1").tab_index(4).child("Button 1"))
115 .child(button("el2").tab_index(5).child("Button 2")),
116 )
117 }
118}
119
120fn main() {
121 Application::new().run(|cx: &mut App| {
122 cx.bind_keys([
123 KeyBinding::new("tab", Tab, None),
124 KeyBinding::new("shift-tab", TabPrev, None),
125 ]);
126
127 let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx);
128 cx.open_window(
129 WindowOptions {
130 window_bounds: Some(WindowBounds::Windowed(bounds)),
131 ..Default::default()
132 },
133 |window, cx| cx.new(|cx| Example::new(window, cx)),
134 )
135 .unwrap();
136
137 cx.activate(true);
138 });
139}