1use crate::{FocusHandle, FocusId};
2
3/// Represents a collection of tab handles.
4///
5/// Used to manage the `Tab` event to switch between focus handles.
6#[derive(Default)]
7pub(crate) struct TabHandles {
8 pub(crate) handles: Vec<FocusHandle>,
9}
10
11impl TabHandles {
12 pub(crate) fn insert(&mut self, focus_handle: &FocusHandle) {
13 if !focus_handle.tab_stop {
14 return;
15 }
16
17 let focus_handle = focus_handle.clone();
18
19 // Insert handle with same tab_index last
20 if let Some(ix) = self
21 .handles
22 .iter()
23 .position(|tab| tab.tab_index > focus_handle.tab_index)
24 {
25 self.handles.insert(ix, focus_handle);
26 } else {
27 self.handles.push(focus_handle);
28 }
29 }
30
31 pub(crate) fn clear(&mut self) {
32 self.handles.clear();
33 }
34
35 fn current_index(&self, focused_id: Option<&FocusId>) -> Option<usize> {
36 self.handles.iter().position(|h| Some(&h.id) == focused_id)
37 }
38
39 pub(crate) fn next(&self, focused_id: Option<&FocusId>) -> Option<FocusHandle> {
40 let next_ix = self
41 .current_index(focused_id)
42 .and_then(|ix| {
43 let next_ix = ix + 1;
44 (next_ix < self.handles.len()).then_some(next_ix)
45 })
46 .unwrap_or_default();
47
48 if let Some(next_handle) = self.handles.get(next_ix) {
49 Some(next_handle.clone())
50 } else {
51 None
52 }
53 }
54
55 pub(crate) fn prev(&self, focused_id: Option<&FocusId>) -> Option<FocusHandle> {
56 let ix = self.current_index(focused_id).unwrap_or_default();
57 let prev_ix;
58 if ix == 0 {
59 prev_ix = self.handles.len().saturating_sub(1);
60 } else {
61 prev_ix = ix.saturating_sub(1);
62 }
63
64 if let Some(prev_handle) = self.handles.get(prev_ix) {
65 Some(prev_handle.clone())
66 } else {
67 None
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use crate::{FocusHandle, FocusMap, TabHandles};
75 use std::sync::Arc;
76
77 #[test]
78 fn test_tab_handles() {
79 let focus_map = Arc::new(FocusMap::default());
80 let mut tab = TabHandles::default();
81
82 let focus_handles = vec![
83 FocusHandle::new(&focus_map).tab_stop(true).tab_index(0),
84 FocusHandle::new(&focus_map).tab_stop(true).tab_index(1),
85 FocusHandle::new(&focus_map).tab_stop(true).tab_index(1),
86 FocusHandle::new(&focus_map),
87 FocusHandle::new(&focus_map).tab_index(2),
88 FocusHandle::new(&focus_map).tab_stop(true).tab_index(0),
89 FocusHandle::new(&focus_map).tab_stop(true).tab_index(2),
90 ];
91
92 for handle in focus_handles.iter() {
93 tab.insert(handle);
94 }
95 assert_eq!(
96 tab.handles
97 .iter()
98 .map(|handle| handle.id)
99 .collect::<Vec<_>>(),
100 vec![
101 focus_handles[0].id,
102 focus_handles[5].id,
103 focus_handles[1].id,
104 focus_handles[2].id,
105 focus_handles[6].id,
106 ]
107 );
108
109 // Select first tab index if no handle is currently focused.
110 assert_eq!(tab.next(None), Some(tab.handles[0].clone()));
111 // Select last tab index if no handle is currently focused.
112 assert_eq!(
113 tab.prev(None),
114 Some(tab.handles[tab.handles.len() - 1].clone())
115 );
116
117 assert_eq!(
118 tab.next(Some(&tab.handles[0].id)),
119 Some(tab.handles[1].clone())
120 );
121 assert_eq!(
122 tab.next(Some(&tab.handles[1].id)),
123 Some(tab.handles[2].clone())
124 );
125 assert_eq!(
126 tab.next(Some(&tab.handles[2].id)),
127 Some(tab.handles[3].clone())
128 );
129 assert_eq!(
130 tab.next(Some(&tab.handles[3].id)),
131 Some(tab.handles[4].clone())
132 );
133 assert_eq!(
134 tab.next(Some(&tab.handles[4].id)),
135 Some(tab.handles[0].clone())
136 );
137
138 // prev
139 assert_eq!(tab.prev(None), Some(tab.handles[4].clone()));
140 assert_eq!(
141 tab.prev(Some(&tab.handles[0].id)),
142 Some(tab.handles[4].clone())
143 );
144 assert_eq!(
145 tab.prev(Some(&tab.handles[1].id)),
146 Some(tab.handles[0].clone())
147 );
148 assert_eq!(
149 tab.prev(Some(&tab.handles[2].id)),
150 Some(tab.handles[1].clone())
151 );
152 assert_eq!(
153 tab.prev(Some(&tab.handles[3].id)),
154 Some(tab.handles[2].clone())
155 );
156 assert_eq!(
157 tab.prev(Some(&tab.handles[4].id)),
158 Some(tab.handles[3].clone())
159 );
160 }
161}