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(
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 }
134}
135
136fn main() {
137 Application::new().run(|cx: &mut App| {
138 cx.bind_keys([
139 KeyBinding::new("tab", Tab, None),
140 KeyBinding::new("shift-tab", TabPrev, None),
141 ]);
142
143 let bounds = Bounds::centered(None, size(px(800.), px(600.0)), cx);
144 cx.open_window(
145 WindowOptions {
146 window_bounds: Some(WindowBounds::Windowed(bounds)),
147 ..Default::default()
148 },
149 |window, cx| cx.new(|cx| Example::new(window, cx)),
150 )
151 .unwrap();
152
153 cx.activate(true);
154 });
155}