gpui_kit/overlay/
focus.rs1use gpui::{App, FocusHandle, Window};
8
9#[derive(Debug, Clone, Default)]
14pub struct FocusTrap {
15 stops: Vec<FocusHandle>,
16 restore: Option<FocusHandle>,
17 engaged: bool,
18}
19
20impl FocusTrap {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn is_engaged(&self) -> bool {
26 self.engaged
27 }
28
29 pub fn stops(&self) -> &[FocusHandle] {
30 &self.stops
31 }
32
33 pub fn engage(&mut self, window: &Window, cx: &App) {
36 if self.engaged {
37 return;
38 }
39 self.restore = window.focused(cx);
40 self.engaged = true;
41 }
42
43 pub fn release(&mut self, window: &mut Window, cx: &mut App) {
46 if let Some(handle) = self.restore.take() {
47 handle.focus(window, cx);
48 }
49 self.stops.clear();
50 self.engaged = false;
51 }
52
53 pub fn begin_frame(&mut self) {
55 self.stops.clear();
56 }
57
58 pub fn register(&mut self, handle: FocusHandle) {
59 if !self.stops.iter().any(|stop| stop == &handle) {
60 self.stops.push(handle);
61 }
62 }
63
64 pub fn focus_first(&self, window: &mut Window, cx: &mut App) {
66 if let Some(handle) = self.stops.first() {
67 handle.focus(window, cx);
68 }
69 }
70
71 pub fn focus_next(&self, window: &mut Window, cx: &mut App) {
72 self.step(1, window, cx);
73 }
74
75 pub fn focus_prev(&self, window: &mut Window, cx: &mut App) {
76 self.step(-1, window, cx);
77 }
78
79 pub fn contains_focus(&self, window: &Window, cx: &App) -> bool {
81 window
82 .focused(cx)
83 .is_some_and(|focused| self.stops.contains(&focused))
84 }
85
86 fn step(&self, delta: isize, window: &mut Window, cx: &mut App) {
87 if self.stops.is_empty() {
88 return;
89 }
90 let current = window
91 .focused(cx)
92 .and_then(|focused| self.stops.iter().position(|stop| stop == &focused));
93 let next = match current {
94 None if delta >= 0 => 0,
97 None => self.stops.len() - 1,
98 Some(index) => (index as isize + delta).rem_euclid(self.stops.len() as isize) as usize,
99 };
100 self.stops[next].focus(window, cx);
101 }
102}