1use heapless::Vec;
2
3use crate::{GuiContext, GuiError, input::InputEvent};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub struct ScreenId(pub u16);
7
8impl ScreenId {
9 pub const fn new(raw: u16) -> Self {
10 Self(raw)
11 }
12
13 pub const fn raw(self) -> u16 {
14 self.0
15 }
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum ScreenCommand {
20 None,
21 Push(ScreenId),
22 Pop,
23 Replace(ScreenId),
24 ClearTo(ScreenId),
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum ScreenStackError {
29 Full,
30 Empty,
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum ScreenLifecycleEvent {
35 Mount(ScreenId),
36 Unmount(ScreenId),
37 Pause(ScreenId),
38 Resume(ScreenId),
39}
40
41#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42pub struct ScreenTransition {
43 pub from: Option<ScreenId>,
44 pub to: Option<ScreenId>,
45 pub command: ScreenCommand,
46}
47
48pub trait Screen<'a, const NODES: usize, const EVENTS: usize, const DIRTY: usize> {
49 fn id(&self) -> ScreenId;
50
51 fn on_mount(
52 &mut self,
53 _gui: &mut GuiContext<'a, NODES, EVENTS, DIRTY>,
54 ) -> Result<(), GuiError> {
55 Ok(())
56 }
57
58 fn on_unmount(
59 &mut self,
60 _gui: &mut GuiContext<'a, NODES, EVENTS, DIRTY>,
61 ) -> Result<(), GuiError> {
62 Ok(())
63 }
64
65 fn on_pause(
66 &mut self,
67 _gui: &mut GuiContext<'a, NODES, EVENTS, DIRTY>,
68 ) -> Result<(), GuiError> {
69 Ok(())
70 }
71
72 fn on_resume(
73 &mut self,
74 _gui: &mut GuiContext<'a, NODES, EVENTS, DIRTY>,
75 ) -> Result<(), GuiError> {
76 Ok(())
77 }
78
79 fn handle_input(
80 &mut self,
81 _event: InputEvent,
82 _gui: &mut GuiContext<'a, NODES, EVENTS, DIRTY>,
83 ) -> Result<ScreenCommand, GuiError> {
84 Ok(ScreenCommand::None)
85 }
86
87 fn tick(
88 &mut self,
89 _dt_ms: u32,
90 _gui: &mut GuiContext<'a, NODES, EVENTS, DIRTY>,
91 ) -> Result<ScreenCommand, GuiError> {
92 Ok(ScreenCommand::None)
93 }
94}
95
96pub struct ScreenStack<const N: usize> {
97 stack: Vec<ScreenId, N>,
98}
99
100impl<const N: usize> ScreenStack<N> {
101 pub const fn new() -> Self {
102 Self { stack: Vec::new() }
103 }
104
105 pub fn with_root(root: ScreenId) -> Result<Self, ScreenStackError> {
106 let mut stack = Self::new();
107 stack.push(root)?;
108 Ok(stack)
109 }
110
111 pub fn with_root_lifecycle<const M: usize>(
112 root: ScreenId,
113 events: &mut Vec<ScreenLifecycleEvent, M>,
114 ) -> Result<Self, ScreenStackError> {
115 let stack = Self::with_root(root)?;
116 push_lifecycle(events, ScreenLifecycleEvent::Mount(root))?;
117 Ok(stack)
118 }
119
120 pub fn push(&mut self, id: ScreenId) -> Result<(), ScreenStackError> {
121 self.stack.push(id).map_err(|_| ScreenStackError::Full)
122 }
123
124 pub fn pop(&mut self) -> Result<ScreenId, ScreenStackError> {
125 self.stack.pop().ok_or(ScreenStackError::Empty)
126 }
127
128 pub fn replace(&mut self, id: ScreenId) -> Result<(), ScreenStackError> {
129 if self.stack.pop().is_none() {
130 return Err(ScreenStackError::Empty);
131 }
132 self.push(id)
133 }
134
135 pub fn clear_to(&mut self, id: ScreenId) -> Result<(), ScreenStackError> {
136 self.stack.clear();
137 self.push(id)
138 }
139
140 pub fn apply(&mut self, command: ScreenCommand) -> Result<(), ScreenStackError> {
141 match command {
142 ScreenCommand::None => Ok(()),
143 ScreenCommand::Push(id) => self.push(id),
144 ScreenCommand::Pop => self.pop().map(|_| ()),
145 ScreenCommand::Replace(id) => self.replace(id),
146 ScreenCommand::ClearTo(id) => self.clear_to(id),
147 }
148 }
149
150 pub fn apply_lifecycle<const M: usize>(
151 &mut self,
152 command: ScreenCommand,
153 events: &mut Vec<ScreenLifecycleEvent, M>,
154 ) -> Result<ScreenTransition, ScreenStackError> {
155 let from = self.current();
156 match command {
157 ScreenCommand::None => Ok(ScreenTransition {
158 from,
159 to: from,
160 command,
161 }),
162 ScreenCommand::Push(id) => {
163 if let Some(current) = self.current() {
164 push_lifecycle(events, ScreenLifecycleEvent::Pause(current))?;
165 }
166 self.push(id)?;
167 push_lifecycle(events, ScreenLifecycleEvent::Mount(id))?;
168 Ok(ScreenTransition {
169 from,
170 to: self.current(),
171 command,
172 })
173 }
174 ScreenCommand::Pop => {
175 let old = self.pop()?;
176 push_lifecycle(events, ScreenLifecycleEvent::Unmount(old))?;
177 if let Some(current) = self.current() {
178 push_lifecycle(events, ScreenLifecycleEvent::Resume(current))?;
179 }
180 Ok(ScreenTransition {
181 from,
182 to: self.current(),
183 command,
184 })
185 }
186 ScreenCommand::Replace(id) => {
187 let old = self.pop()?;
188 push_lifecycle(events, ScreenLifecycleEvent::Unmount(old))?;
189 self.push(id)?;
190 push_lifecycle(events, ScreenLifecycleEvent::Mount(id))?;
191 Ok(ScreenTransition {
192 from,
193 to: self.current(),
194 command,
195 })
196 }
197 ScreenCommand::ClearTo(id) => {
198 while let Some(old) = self.stack.pop() {
199 push_lifecycle(events, ScreenLifecycleEvent::Unmount(old))?;
200 }
201 self.push(id)?;
202 push_lifecycle(events, ScreenLifecycleEvent::Mount(id))?;
203 Ok(ScreenTransition {
204 from,
205 to: self.current(),
206 command,
207 })
208 }
209 }
210 }
211
212 pub fn current(&self) -> Option<ScreenId> {
213 self.stack.last().copied()
214 }
215
216 pub fn as_slice(&self) -> &[ScreenId] {
217 self.stack.as_slice()
218 }
219
220 pub fn len(&self) -> usize {
221 self.stack.len()
222 }
223
224 pub fn is_empty(&self) -> bool {
225 self.stack.is_empty()
226 }
227}
228
229fn push_lifecycle<const M: usize>(
230 events: &mut Vec<ScreenLifecycleEvent, M>,
231 event: ScreenLifecycleEvent,
232) -> Result<(), ScreenStackError> {
233 events.push(event).map_err(|_| ScreenStackError::Full)
234}
235
236impl<const N: usize> Default for ScreenStack<N> {
237 fn default() -> Self {
238 Self::new()
239 }
240}