pub struct StackNav<K, const N: usize> { /* private fields */ }Expand description
Stack-based navigation state machine.
Implementations§
Sourcepub fn new(root: K) -> Self
pub fn new(root: K) -> Self
Creates a stack containing only root.
Examples found in repository?
src/containers.rs (line 96)
94 pub fn new(root: ViewId, title_for: TitleFor) -> Self {
95 Self {
96 nav: StackNav::new(root),
97 title_for,
98 header_touch: ButtonTouchState::new(),
99 }
100 }
101
102 /// Replaces the whole stack with a new root.
103 pub fn set_root(&mut self, root: ViewId) {
104 self.nav = StackNav::new(root);
105 self.header_touch.clear();
106 }More examples
Sourcepub fn top(&self) -> K
pub fn top(&self) -> K
Returns the top-most view identifier.
Examples found in repository?
More examples
src/app/ui_app.rs (line 64)
63 pub fn active_view(&self) -> ViewId {
64 self.stack.top()
65 }
66
67 /// Returns whether a stack animation is active.
68 pub fn is_animating(&self) -> bool {
69 self.stack.is_animating()
70 }
71
72 /// Returns the body frame for the active view.
73 pub fn content_frame(&self, bounds: Rectangle) -> Rectangle {
74 self.nav_view(bounds).body
75 }
76
77 /// Returns the dirty region for the active view.
78 pub fn active_view_dirty(&self, bounds: Rectangle) -> Rectangle {
79 self.content_frame(bounds)
80 }
81
82 /// Advances the compatibility app by one tick.
83 pub fn tick(&mut self, dt_ms: u32, bounds: Rectangle) -> AppRedraw {
84 if self.stack.advance(dt_ms) {
85 return AppRedraw::StackMotion;
86 }
87 let body = self.content_frame(bounds);
88 let response = self
89 .delegate
90 .tick(self.stack.top(), dt_ms, body, &self.theme, &self.i18n);
91 self.apply_response(response)
92 }
93
94 /// Handles one touch event.
95 pub fn handle_touch(&mut self, touch: TouchEvent, bounds: Rectangle) -> AppRedraw {
96 self.handle_touch_result(touch, bounds).redraw
97 }
98
99 /// Handles one touch event and returns capture status.
100 pub fn handle_touch_result(&mut self, touch: TouchEvent, bounds: Rectangle) -> AppTouchResult {
101 if self.is_animating() {
102 return AppTouchResult {
103 redraw: AppRedraw::None,
104 captured: false,
105 };
106 }
107
108 let nav = self.nav_view(bounds);
109 let chrome = nav.handle_touch(&mut self.nav_touch, touch);
110 if chrome.action == Some(NavHeaderAction::Back) {
111 self.clear_touch_state();
112 return AppTouchResult {
113 redraw: self
114 .stack
115 .pop()
116 .map(|_| AppRedraw::StackMotion)
117 .unwrap_or(AppRedraw::None),
118 captured: true,
119 };
120 }
121 if chrome.captured {
122 return AppTouchResult {
123 redraw: if chrome.redraw {
124 AppRedraw::Interactive
125 } else {
126 AppRedraw::None
127 },
128 captured: true,
129 };
130 }
131
132 let response = self.delegate.handle_view_touch(
133 self.stack.top(),
134 touch,
135 nav.body,
136 &self.theme,
137 &self.i18n,
138 );
139 AppTouchResult {
140 redraw: self.apply_response(response),
141 captured: response.captured || response.navigation.is_some(),
142 }
143 }src/app/render.rs (line 38)
31 pub fn draw_interactive<D>(&self, display: &mut D, bounds: Rectangle)
32 where
33 D: DrawTarget<Color = Rgb565>,
34 {
35 let nav = self.nav_view(bounds);
36 nav.draw_chrome(display, &self.theme, &self.i18n, &self.nav_touch);
37 self.delegate
38 .draw_view(self.stack.top(), display, nav.body, &self.theme, &self.i18n);
39 }
40
41 /// Draws only the active view body.
42 pub fn draw_active_view<D>(&self, display: &mut D, bounds: Rectangle)
43 where
44 D: DrawTarget<Color = Rgb565>,
45 {
46 let nav = self.nav_view(bounds);
47 self.delegate
48 .draw_view(self.stack.top(), display, nav.body, &self.theme, &self.i18n);
49 }src/stack.rs (line 118)
113 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 /// Pops the current view and starts a transition.
129 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 /// Advances the active transition, if any.
148 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 /// Returns the base and overlay layers for a frame.
172 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 }src/stack_header.rs (line 68)
26pub(super) fn header_titles<'a, K: Copy, F, const N: usize>(
27 nav: &StackNav<K, N>,
28 header: Rectangle,
29 title_for: &F,
30 back_button: Option<&Button<'a, NavHeaderAction>>,
31) -> (
32 Option<Localized<'a>>,
33 HeaderTitle<'a>,
34 Option<HeaderTitle<'a>>,
35)
36where
37 F: Fn(K) -> Localized<'a>,
38{
39 let center = header.center();
40 let right = Point::new(center.x + header.size.width as i32, center.y);
41 let back = back_title_center(back_button);
42 match nav.header_transition() {
43 Some(HeaderTransition::Push { from, to, progress }) => (
44 None,
45 HeaderTitle {
46 title: title_for(from),
47 center: Point::new(lerp_i32(center.x, back.x, progress), center.y),
48 },
49 Some(HeaderTitle {
50 title: title_for(to),
51 center: Point::new(lerp_i32(right.x, center.x, progress), center.y),
52 }),
53 ),
54 Some(HeaderTransition::Pop { from, to, progress }) => (
55 None,
56 HeaderTitle {
57 title: title_for(to),
58 center: Point::new(lerp_i32(back.x, center.x, progress), center.y),
59 },
60 Some(HeaderTitle {
61 title: title_for(from),
62 center: Point::new(lerp_i32(center.x, right.x, progress), center.y),
63 }),
64 ),
65 None => (
66 nav.previous().map(title_for),
67 HeaderTitle {
68 title: title_for(nav.top()),
69 center,
70 },
71 None,
72 ),
73 }
74}Sourcepub fn depth(&self) -> usize
pub fn depth(&self) -> usize
Returns the current stack depth.
Examples found in repository?
More examples
Sourcepub fn is_animating(&self) -> bool
pub fn is_animating(&self) -> bool
Returns whether a push or pop animation is active.
Examples found in repository?
More examples
Sourcepub fn push(&mut self, key: K) -> Result<(), StackError>
pub fn push(&mut self, key: K) -> Result<(), StackError>
Pushes key and starts a transition.
Examples found in repository?
More examples
src/app/ui_app.rs (line 156)
151 fn apply_response(&mut self, response: ViewResponse<ViewId>) -> AppRedraw {
152 let mut redraw = response.redraw;
153 match response.navigation {
154 Some(AppNavigation::Push(view)) => {
155 self.clear_touch_state();
156 if self.stack.push(view).is_ok() {
157 redraw = redraw.merge(AppRedraw::StackMotion);
158 }
159 }
160 Some(AppNavigation::Pop) => {
161 self.clear_touch_state();
162 if self.stack.pop().is_ok() {
163 redraw = redraw.merge(AppRedraw::StackMotion);
164 }
165 }
166 None => {}
167 }
168 redraw
169 }Sourcepub fn pop(&mut self) -> Result<K, StackError>
pub fn pop(&mut self) -> Result<K, StackError>
Pops the current view and starts a transition.
Examples found in repository?
More examples
src/app/ui_app.rs (line 115)
100 pub fn handle_touch_result(&mut self, touch: TouchEvent, bounds: Rectangle) -> AppTouchResult {
101 if self.is_animating() {
102 return AppTouchResult {
103 redraw: AppRedraw::None,
104 captured: false,
105 };
106 }
107
108 let nav = self.nav_view(bounds);
109 let chrome = nav.handle_touch(&mut self.nav_touch, touch);
110 if chrome.action == Some(NavHeaderAction::Back) {
111 self.clear_touch_state();
112 return AppTouchResult {
113 redraw: self
114 .stack
115 .pop()
116 .map(|_| AppRedraw::StackMotion)
117 .unwrap_or(AppRedraw::None),
118 captured: true,
119 };
120 }
121 if chrome.captured {
122 return AppTouchResult {
123 redraw: if chrome.redraw {
124 AppRedraw::Interactive
125 } else {
126 AppRedraw::None
127 },
128 captured: true,
129 };
130 }
131
132 let response = self.delegate.handle_view_touch(
133 self.stack.top(),
134 touch,
135 nav.body,
136 &self.theme,
137 &self.i18n,
138 );
139 AppTouchResult {
140 redraw: self.apply_response(response),
141 captured: response.captured || response.navigation.is_some(),
142 }
143 }
144
145 /// Clears all compatibility touch state.
146 pub fn clear_touch_state(&mut self) {
147 self.nav_touch.clear();
148 self.delegate.clear_touch_state();
149 }
150
151 fn apply_response(&mut self, response: ViewResponse<ViewId>) -> AppRedraw {
152 let mut redraw = response.redraw;
153 match response.navigation {
154 Some(AppNavigation::Push(view)) => {
155 self.clear_touch_state();
156 if self.stack.push(view).is_ok() {
157 redraw = redraw.merge(AppRedraw::StackMotion);
158 }
159 }
160 Some(AppNavigation::Pop) => {
161 self.clear_touch_state();
162 if self.stack.pop().is_ok() {
163 redraw = redraw.merge(AppRedraw::StackMotion);
164 }
165 }
166 None => {}
167 }
168 redraw
169 }Sourcepub fn advance(&mut self, dt_ms: u32) -> bool
pub fn advance(&mut self, dt_ms: u32) -> bool
Advances the active transition, if any.
Examples found in repository?
More examples
Sourcepub fn layers(&self, frame: Rectangle) -> StackLayers<K>
pub fn layers(&self, frame: Rectangle) -> StackLayers<K>
Returns the base and overlay layers for a frame.
Examples found in repository?
src/stack_view.rs (line 69)
49 pub fn nav_view<'a, F>(&self, frame: Rectangle, title_for: F) -> NavView<'a, K>
50 where
51 F: Fn(K) -> Localized<'a>,
52 {
53 let header_height = HEADER_HEIGHT.min(frame.size.height.saturating_sub(1));
54 let header = Rectangle::new(frame.top_left, Size::new(frame.size.width, header_height));
55 let body = Rectangle::new(
56 frame.top_left + Point::new(0, header_height as i32),
57 Size::new(
58 frame.size.width,
59 frame.size.height.saturating_sub(header_height),
60 ),
61 );
62 let back_button = show_back_button(self).then_some(back_button(header));
63 let (back_title, active_title, secondary_title) =
64 header_titles(self, header, &title_for, back_button.as_ref());
65 NavView {
66 frame,
67 header,
68 body,
69 layers: self.layers(body),
70 back_button,
71 back_title,
72 active_title,
73 secondary_title,
74 }
75 }Builds a navigation view for a stack frame and title callback.
Examples found in repository?
More examples
Trait Implementations§
Auto Trait Implementations§
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CheckedAs for T
impl<T> CheckedAs for T
Source§fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
fn checked_as<Dst>(self) -> Option<Dst>where
T: CheckedCast<Dst>,
Casts the value.
Source§impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
impl<Src, Dst> CheckedCastFrom<Src> for Dstwhere
Src: CheckedCast<Dst>,
Source§fn checked_cast_from(src: Src) -> Option<Dst>
fn checked_cast_from(src: Src) -> Option<Dst>
Casts the value.
Source§impl<T> OverflowingAs for T
impl<T> OverflowingAs for T
Source§fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
fn overflowing_as<Dst>(self) -> (Dst, bool)where
T: OverflowingCast<Dst>,
Casts the value.
Source§impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
impl<Src, Dst> OverflowingCastFrom<Src> for Dstwhere
Src: OverflowingCast<Dst>,
Source§fn overflowing_cast_from(src: Src) -> (Dst, bool)
fn overflowing_cast_from(src: Src) -> (Dst, bool)
Casts the value.
Source§impl<T> SaturatingAs for T
impl<T> SaturatingAs for T
Source§fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
fn saturating_as<Dst>(self) -> Dstwhere
T: SaturatingCast<Dst>,
Casts the value.
Source§impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
impl<Src, Dst> SaturatingCastFrom<Src> for Dstwhere
Src: SaturatingCast<Dst>,
Source§fn saturating_cast_from(src: Src) -> Dst
fn saturating_cast_from(src: Src) -> Dst
Casts the value.
Source§impl<T> UnwrappedAs for T
impl<T> UnwrappedAs for T
Source§fn unwrapped_as<Dst>(self) -> Dstwhere
T: UnwrappedCast<Dst>,
fn unwrapped_as<Dst>(self) -> Dstwhere
T: UnwrappedCast<Dst>,
Casts the value.
Source§impl<Src, Dst> UnwrappedCastFrom<Src> for Dstwhere
Src: UnwrappedCast<Dst>,
impl<Src, Dst> UnwrappedCastFrom<Src> for Dstwhere
Src: UnwrappedCast<Dst>,
Source§fn unwrapped_cast_from(src: Src) -> Dst
fn unwrapped_cast_from(src: Src) -> Dst
Casts the value.
Source§impl<T> WrappingAs for T
impl<T> WrappingAs for T
Source§fn wrapping_as<Dst>(self) -> Dstwhere
T: WrappingCast<Dst>,
fn wrapping_as<Dst>(self) -> Dstwhere
T: WrappingCast<Dst>,
Casts the value.
Source§impl<Src, Dst> WrappingCastFrom<Src> for Dstwhere
Src: WrappingCast<Dst>,
impl<Src, Dst> WrappingCastFrom<Src> for Dstwhere
Src: WrappingCast<Dst>,
Source§fn wrapping_cast_from(src: Src) -> Dst
fn wrapping_cast_from(src: Src) -> Dst
Casts the value.