Skip to main content

oo_ide/widgets/
focus.rs

1use super::focusable::WidgetId;
2
3/// Manages a linear focus ring of widget IDs.
4///
5/// Views use this to track which widget has focus and to cycle through
6/// the focus order with Tab / Shift+Tab.
7#[derive(Debug, Clone)]
8pub struct FocusRing {
9    order: Vec<WidgetId>,
10    current: usize,
11}
12
13impl FocusRing {
14    pub fn new(order: Vec<WidgetId>) -> Self {
15        assert!(!order.is_empty(), "FocusRing must have at least one entry");
16        Self { order, current: 0 }
17    }
18
19    /// The currently focused widget's ID.
20    pub fn current(&self) -> WidgetId {
21        self.order[self.current]
22    }
23
24    /// Advance focus to the next widget (wraps around).
25    pub fn focus_next(&mut self) {
26        self.current = (self.current + 1) % self.order.len();
27    }
28
29    /// Move focus to the previous widget (wraps around).
30    pub fn focus_prev(&mut self) {
31        self.current = if self.current == 0 {
32            self.order.len() - 1
33        } else {
34            self.current - 1
35        };
36    }
37
38    /// Set focus to the widget with the given ID.
39    /// No-op if the ID is not in the ring.
40    pub fn set_focus(&mut self, id: WidgetId) {
41        if let Some(pos) = self.order.iter().position(|&w| w == id) {
42            self.current = pos;
43        }
44    }
45
46    /// Returns `true` if the given widget currently has focus.
47    pub fn is_focused(&self, id: WidgetId) -> bool {
48        self.order[self.current] == id
49    }
50
51    /// Returns `true` if the ring contains no widgets.
52    pub fn is_empty(&self) -> bool {
53        self.order.is_empty()
54    }
55
56    /// The number of widgets in the ring.
57    pub fn len(&self) -> usize {
58        self.order.len()
59    }
60
61    /// The ordered list of widget IDs.
62    pub fn order(&self) -> &[WidgetId] {
63        &self.order
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_new_starts_at_first() {
73        let ring = FocusRing::new(vec!["a", "b", "c"]);
74        assert_eq!(ring.current(), "a");
75    }
76
77    #[test]
78    fn test_focus_next_cycles() {
79        let mut ring = FocusRing::new(vec!["a", "b", "c"]);
80        ring.focus_next();
81        assert_eq!(ring.current(), "b");
82        ring.focus_next();
83        assert_eq!(ring.current(), "c");
84        ring.focus_next();
85        assert_eq!(ring.current(), "a");
86    }
87
88    #[test]
89    fn test_focus_prev_cycles() {
90        let mut ring = FocusRing::new(vec!["a", "b", "c"]);
91        ring.focus_prev();
92        assert_eq!(ring.current(), "c");
93        ring.focus_prev();
94        assert_eq!(ring.current(), "b");
95        ring.focus_prev();
96        assert_eq!(ring.current(), "a");
97    }
98
99    #[test]
100    fn test_set_focus() {
101        let mut ring = FocusRing::new(vec!["a", "b", "c"]);
102        ring.set_focus("c");
103        assert_eq!(ring.current(), "c");
104    }
105
106    #[test]
107    fn test_set_focus_unknown_id_is_noop() {
108        let mut ring = FocusRing::new(vec!["a", "b", "c"]);
109        ring.set_focus("z");
110        assert_eq!(ring.current(), "a");
111    }
112
113    #[test]
114    fn test_is_focused() {
115        let ring = FocusRing::new(vec!["a", "b", "c"]);
116        assert!(ring.is_focused("a"));
117        assert!(!ring.is_focused("b"));
118    }
119
120    #[test]
121    fn test_single_element_ring() {
122        let mut ring = FocusRing::new(vec!["only"]);
123        assert_eq!(ring.current(), "only");
124        ring.focus_next();
125        assert_eq!(ring.current(), "only");
126        ring.focus_prev();
127        assert_eq!(ring.current(), "only");
128    }
129
130    #[test]
131    fn test_len() {
132        let ring = FocusRing::new(vec!["a", "b"]);
133        assert_eq!(ring.len(), 2);
134    }
135
136    #[test]
137    fn test_order() {
138        let ring = FocusRing::new(vec!["x", "y", "z"]);
139        assert_eq!(ring.order(), &["x", "y", "z"]);
140    }
141
142    #[test]
143    fn test_next_then_prev_returns_to_start() {
144        let mut ring = FocusRing::new(vec!["a", "b", "c"]);
145        ring.focus_next();
146        ring.focus_next();
147        ring.focus_prev();
148        ring.focus_prev();
149        assert_eq!(ring.current(), "a");
150    }
151
152    #[test]
153    #[should_panic(expected = "FocusRing must have at least one entry")]
154    fn test_empty_ring_panics() {
155        FocusRing::new(vec![]);
156    }
157}
158