1use embedded_graphics::{prelude::Point, primitives::Rectangle};
2use heapless::Vec;
3
4use super::{Animation, Curve, Layer, lerp_i32};
5
6const STACK_ANIMATION_MS: u32 = 320;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum StackError {
11 Busy,
13 Full,
15 RootLocked,
17}
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub struct StackLayers<K> {
22 pub base: Layer<K>,
24 pub overlay: Option<Layer<K>>,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29enum Transition<K> {
30 Push {
31 from: K,
32 to: K,
33 animation: Animation,
34 },
35 Pop {
36 from: K,
37 to: K,
38 animation: Animation,
39 },
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub(crate) enum HeaderTransition<K> {
44 Push { from: K, to: K, progress: u16 },
45 Pop { from: K, to: K, progress: u16 },
46}
47
48#[derive(Debug)]
50pub struct StackNav<K, const N: usize> {
51 stack: Vec<K, N>,
52 transition: Option<Transition<K>>,
53}
54
55impl<K: Copy, const N: usize> StackNav<K, N> {
56 pub fn new(root: K) -> Self {
58 let mut stack = Vec::new();
59 let _ = stack.push(root);
60 Self {
61 stack,
62 transition: None,
63 }
64 }
65
66 pub fn top(&self) -> K {
68 *self
69 .stack
70 .last()
71 .expect("stack always contains a root view")
72 }
73
74 pub fn depth(&self) -> usize {
76 self.stack.len()
77 }
78
79 pub fn is_animating(&self) -> bool {
81 self.transition.is_some()
82 }
83
84 pub(crate) fn previous(&self) -> Option<K> {
85 self.stack.get(self.stack.len().checked_sub(2)?).copied()
86 }
87
88 pub(crate) fn header_transition(&self) -> Option<HeaderTransition<K>> {
89 match self.transition {
90 None => None,
91 Some(Transition::Push {
92 from,
93 to,
94 animation,
95 }) => Some(HeaderTransition::Push {
96 from,
97 to,
98 progress: animation.progress_permille(),
99 }),
100 Some(Transition::Pop {
101 from,
102 to,
103 animation,
104 }) => Some(HeaderTransition::Pop {
105 from,
106 to,
107 progress: animation.progress_permille(),
108 }),
109 }
110 }
111
112 pub fn push(&mut self, key: K) -> Result<(), StackError> {
114 if self.transition.is_some() {
115 return Err(StackError::Busy);
116 }
117
118 let from = self.top();
119 self.stack.push(key).map_err(|_| StackError::Full)?;
120 self.transition = Some(Transition::Push {
121 from,
122 to: key,
123 animation: Animation::new(STACK_ANIMATION_MS, Curve::EaseInOut),
124 });
125 Ok(())
126 }
127
128 pub fn pop(&mut self) -> Result<K, StackError> {
130 if self.transition.is_some() {
131 return Err(StackError::Busy);
132 }
133 if self.stack.len() <= 1 {
134 return Err(StackError::RootLocked);
135 }
136
137 let from = self.stack.pop().ok_or(StackError::RootLocked)?;
138 let to = self.top();
139 self.transition = Some(Transition::Pop {
140 from,
141 to,
142 animation: Animation::new(STACK_ANIMATION_MS, Curve::EaseInOut),
143 });
144 Ok(from)
145 }
146
147 pub fn advance(&mut self, dt_ms: u32) -> bool {
149 let Some(mut transition) = self.transition.take() else {
150 return false;
151 };
152
153 let was_running = match &transition {
154 Transition::Push { animation, .. } | Transition::Pop { animation, .. } => {
155 animation.is_running()
156 }
157 };
158 let is_running = match &mut transition {
159 Transition::Push { animation, .. } | Transition::Pop { animation, .. } => {
160 animation.advance(dt_ms)
161 }
162 };
163
164 if is_running {
165 self.transition = Some(transition);
166 }
167
168 was_running
169 }
170
171 pub fn layers(&self, frame: Rectangle) -> StackLayers<K> {
173 let width = frame.size.width as i32;
174 let idle = Layer::new(self.top(), frame, Point::zero());
175
176 match self.transition {
177 None => StackLayers {
178 base: idle,
179 overlay: None,
180 },
181 Some(Transition::Push {
182 from,
183 to,
184 animation,
185 }) => {
186 let progress = animation.progress_permille();
187 let base = Layer::new(from, frame, Point::zero());
188 let overlay = Layer::new(to, frame, Point::new(lerp_i32(width, 0, progress), 0));
189 StackLayers {
190 base,
191 overlay: Some(overlay),
192 }
193 }
194 Some(Transition::Pop {
195 from,
196 to,
197 animation,
198 }) => {
199 let progress = animation.progress_permille();
200 let base = Layer::new(to, frame, Point::zero());
201 let overlay = Layer::new(from, frame, Point::new(lerp_i32(0, width, progress), 0));
202 StackLayers {
203 base,
204 overlay: Some(overlay),
205 }
206 }
207 }
208 }
209}