1#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
5pub struct WidgetId(u64);
6
7impl WidgetId {
8 #[must_use]
10 pub const fn new(raw: u64) -> Self {
11 Self(raw)
12 }
13
14 #[must_use]
16 pub const fn raw(self) -> u64 {
17 self.0
18 }
19}
20
21#[derive(Copy, Clone, Debug, PartialEq, Eq)]
23pub enum FocusDirection {
24 Forward,
26 Backward,
28}
29
30#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
32pub struct FocusState {
33 focused: Option<WidgetId>,
34}
35
36impl FocusState {
37 #[must_use]
39 pub const fn new() -> Self {
40 Self { focused: None }
41 }
42
43 #[must_use]
45 pub const fn focused(self) -> Option<WidgetId> {
46 self.focused
47 }
48
49 pub fn set(&mut self, focused: Option<WidgetId>) {
51 self.focused = focused;
52 }
53
54 pub fn clear(&mut self) {
56 self.focused = None;
57 }
58
59 pub fn reconcile(&mut self, order: &[WidgetId]) {
61 if let Some(id) = self.focused
62 && !order.contains(&id)
63 {
64 self.focused = None;
65 }
66 }
67
68 pub fn advance(&mut self, order: &[WidgetId], direction: FocusDirection) {
70 if order.is_empty() {
71 self.focused = None;
72 return;
73 }
74
75 let current = self
76 .focused
77 .and_then(|id| order.iter().position(|candidate| *candidate == id));
78
79 let next = match (direction, current) {
80 (FocusDirection::Forward, Some(idx)) => (idx + 1) % order.len(),
81 (FocusDirection::Backward, Some(0)) => order.len() - 1,
82 (FocusDirection::Backward, Some(idx)) => idx - 1,
83 (FocusDirection::Forward | FocusDirection::Backward, None) => 0,
84 };
85
86 self.focused = Some(order[next]);
87 }
88}