Skip to main content

halley_core/
focus.rs

1use crate::field::{Field, NodeId};
2
3/// Current interaction target (separate from history).
4/// Focus only applies to nodes that are present and experience-visible.
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub struct Focus {
7    focused: Option<NodeId>,
8}
9
10impl Focus {
11    pub fn new() -> Self {
12        Self { focused: None }
13    }
14
15    pub fn current(&self) -> Option<NodeId> {
16        self.focused
17    }
18
19    pub fn is_focused(&self, id: NodeId) -> bool {
20        self.focused == Some(id)
21    }
22
23    /// Set focus to `id` if it exists and is experience-visible.
24    pub fn set(&mut self, field: &Field, id: NodeId) -> bool {
25        if !field.is_visible(id) {
26            return false;
27        }
28        self.focused = Some(id);
29        true
30    }
31
32    pub fn clear(&mut self) {
33        self.focused = None;
34    }
35
36    /// If the focused node is removed, clear focus.
37    pub fn on_removed(&mut self, removed: NodeId) {
38        if self.focused == Some(removed) {
39            self.focused = None;
40        }
41    }
42
43    /// If the focused node becomes hidden (collapse/hide/detach), clear focus.
44    pub fn on_hidden(&mut self, field: &Field, id: NodeId) {
45        if self.focused == Some(id) && !field.is_visible(id) {
46            self.focused = None;
47        }
48    }
49}
50
51impl Default for Focus {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::field::Vec2;
61
62    #[test]
63    fn starts_empty() {
64        let f = Focus::new();
65        assert_eq!(f.current(), None);
66    }
67
68    #[test]
69    fn can_focus_existing_node() {
70        let mut field = Field::new();
71        let id = field.spawn_surface("A", Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
72
73        let mut focus = Focus::new();
74        assert!(focus.set(&field, id));
75        assert_eq!(focus.current(), Some(id));
76        assert!(focus.is_focused(id));
77    }
78
79    #[test]
80    fn cannot_focus_missing_node() {
81        let field = Field::new();
82        let mut focus = Focus::new();
83        assert!(!focus.set(&field, NodeId::new(999)));
84        assert_eq!(focus.current(), None);
85    }
86
87    #[test]
88    fn cannot_focus_hidden_node() {
89        let mut field = Field::new();
90        let id = field.spawn_surface("A", Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
91
92        assert!(field.set_hidden(id, true));
93
94        let mut focus = Focus::new();
95        assert!(!focus.set(&field, id));
96        assert_eq!(focus.current(), None);
97    }
98
99    #[test]
100    fn clears_when_focused_node_removed() {
101        let mut field = Field::new();
102        let id = field.spawn_surface("A", Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
103
104        let mut focus = Focus::new();
105        assert!(focus.set(&field, id));
106
107        field.remove(id);
108        focus.on_removed(id);
109
110        assert_eq!(focus.current(), None);
111    }
112
113    #[test]
114    fn clears_when_focused_node_becomes_hidden() {
115        let mut field = Field::new();
116        let id = field.spawn_surface("A", Vec2 { x: 0.0, y: 0.0 }, Vec2 { x: 10.0, y: 10.0 });
117
118        let mut focus = Focus::new();
119        assert!(focus.set(&field, id));
120
121        assert!(field.set_hidden(id, true));
122        focus.on_hidden(&field, id);
123
124        assert_eq!(focus.current(), None);
125    }
126}