tab_stop.rs

  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}