1use gpui_kit::{App, Div, ElementId, KeyBinding, Stateful, Window, actions, div, prelude::*};
3
4actions!(macos_focus, [Next, Previous]);
5
6pub(crate) fn init(cx: &mut App) {
7 cx.bind_keys([
8 KeyBinding::new(
9 if cfg!(target_os = "macos") {
10 "cmd-c"
11 } else {
12 "ctrl-c"
13 },
14 gpui_kit::base::input::Copy,
15 Some("macOSFocusScope"),
16 ),
17 KeyBinding::new("tab", Next, Some("macOSFocusScope")),
18 KeyBinding::new("shift-tab", Previous, Some("macOSFocusScope")),
19 ]);
20}
21
22pub fn focus_scope(id: impl Into<ElementId>) -> Stateful<Div> {
26 div()
27 .id(id)
28 .tab_group()
29 .key_context("macOSFocusScope")
30 .on_action(|_: &gpui_kit::base::input::Copy, window: &mut Window, cx| {
31 let selected = gpui_kit::base::TextSelection::selected_text(window, cx);
32 if selected.is_empty() {
33 cx.propagate();
34 } else {
35 cx.write_to_clipboard(gpui_kit::ClipboardItem::new_string(selected));
36 }
37 })
38 .on_action(|_: &Next, window: &mut Window, cx| traverse(window, cx, false))
39 .on_action(|_: &Previous, window: &mut Window, cx| traverse(window, cx, true))
40 .on_action(
41 |_: &gpui_kit::base::input::IndentInline, window: &mut Window, cx| {
42 traverse(window, cx, false)
43 },
44 )
45 .on_action(
46 |_: &gpui_kit::base::input::OutdentInline, window: &mut Window, cx| {
47 traverse(window, cx, true)
48 },
49 )
50}
51
52fn traverse(window: &mut Window, cx: &mut App, backwards: bool) {
55 let trap = gpui_kit::base::active_focus_trap(window, cx);
56 let mut visited = Vec::new();
57 loop {
58 if backwards {
59 window.focus_prev(cx);
60 } else {
61 window.focus_next(cx);
62 }
63 let Some(trap) = &trap else {
64 return;
65 };
66 if trap.contains_focused(window, cx) {
67 return;
68 }
69 let focused = window.focused(cx);
70 if focused.is_none() || visited.contains(&focused) {
73 trap.focus(window, cx);
74 return;
75 }
76 visited.push(focused);
77 }
78}