tab_stop.rs

  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}