Skip to main content

StackNav

Struct StackNav 

Source
pub struct StackNav<K, const N: usize> { /* private fields */ }
Expand description

Stack-based navigation state machine.

Implementations§

Source§

impl<K: Copy, const N: usize> StackNav<K, N>

Source

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
Hide additional examples
src/app/ui_app.rs (line 26)
22    pub fn new(root: ViewId, delegate: Delegate, theme: FsTheme, i18n: I18n<'a>) -> Self {
23        Self {
24            theme,
25            i18n,
26            stack: StackNav::new(root),
27            nav_touch: ButtonTouchState::new(),
28            delegate,
29        }
30    }
Source

pub fn top(&self) -> K

Returns the top-most view identifier.

Examples found in repository?
src/containers.rs (line 120)
119    pub fn top_view(&self) -> ViewId {
120        self.nav.top()
121    }
More examples
Hide additional 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}
Source

pub fn depth(&self) -> usize

Returns the current stack depth.

Examples found in repository?
src/containers.rs (line 125)
124    pub fn depth(&self) -> usize {
125        self.nav.depth()
126    }
More examples
Hide additional examples
src/stack_header.rs (line 23)
22pub(super) fn show_back_button<K: Copy, const N: usize>(nav: &StackNav<K, N>) -> bool {
23    nav.depth() > 1 || matches!(nav.header_transition(), Some(HeaderTransition::Pop { .. }))
24}
Source

pub fn is_animating(&self) -> bool

Returns whether a push or pop animation is active.

Examples found in repository?
src/containers.rs (line 130)
129    pub fn is_animating(&self) -> bool {
130        self.nav.is_animating()
131    }
More examples
Hide additional examples
src/app/ui_app.rs (line 69)
68    pub fn is_animating(&self) -> bool {
69        self.stack.is_animating()
70    }
Source

pub fn push(&mut self, key: K) -> Result<(), StackError>

Pushes key and starts a transition.

Examples found in repository?
src/containers.rs (line 110)
109    pub fn push_view(&mut self, view: ViewId) -> Result<(), StackError> {
110        self.nav.push(view)
111    }
More examples
Hide additional 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    }
Source

pub fn pop(&mut self) -> Result<K, StackError>

Pops the current view and starts a transition.

Examples found in repository?
src/containers.rs (line 115)
114    pub fn pop_view(&mut self) -> Result<ViewId, StackError> {
115        self.nav.pop()
116    }
More examples
Hide additional 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    }
Source

pub fn advance(&mut self, dt_ms: u32) -> bool

Advances the active transition, if any.

Examples found in repository?
src/containers.rs (line 135)
134    pub fn advance(&mut self, dt_ms: u32) -> bool {
135        self.nav.advance(dt_ms)
136    }
More examples
Hide additional examples
src/app/ui_app.rs (line 84)
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    }
Source

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    }
Source§

impl<K: Copy, const N: usize> StackNav<K, N>

Source

pub fn nav_view<'a, F>(&self, frame: Rectangle, title_for: F) -> NavView<'a, K>
where F: Fn(K) -> Localized<'a>,

Builds a navigation view for a stack frame and title callback.

Examples found in repository?
src/containers.rs (line 140)
139    pub fn nav_view(&self, frame: Rectangle) -> NavView<'a, ViewId> {
140        self.nav.nav_view(frame, &self.title_for)
141    }
More examples
Hide additional examples
src/app/render.rs (line 90)
88    pub(super) fn nav_view(&self, bounds: Rectangle) -> crate::NavView<'a, ViewId> {
89        self.stack
90            .nav_view(bounds, |view| self.delegate.title(view))
91    }

Trait Implementations§

Source§

impl<K: Debug, const N: usize> Debug for StackNav<K, N>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<K, const N: usize> Freeze for StackNav<K, N>
where K: Freeze,

§

impl<K, const N: usize> RefUnwindSafe for StackNav<K, N>
where K: RefUnwindSafe,

§

impl<K, const N: usize> Send for StackNav<K, N>
where K: Send,

§

impl<K, const N: usize> Sync for StackNav<K, N>
where K: Sync,

§

impl<K, const N: usize> Unpin for StackNav<K, N>
where K: Unpin,

§

impl<K, const N: usize> UnsafeUnpin for StackNav<K, N>
where K: UnsafeUnpin,

§

impl<K, const N: usize> UnwindSafe for StackNav<K, N>
where K: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Az for T

Source§

fn az<Dst>(self) -> Dst
where T: Cast<Dst>,

Casts the value.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<Src, Dst> CastFrom<Src> for Dst
where Src: Cast<Dst>,

Source§

fn cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> CheckedAs for T

Source§

fn checked_as<Dst>(self) -> Option<Dst>
where T: CheckedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> CheckedCastFrom<Src> for Dst
where Src: CheckedCast<Dst>,

Source§

fn checked_cast_from(src: Src) -> Option<Dst>

Casts the value.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> OverflowingAs for T

Source§

fn overflowing_as<Dst>(self) -> (Dst, bool)
where T: OverflowingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> OverflowingCastFrom<Src> for Dst
where Src: OverflowingCast<Dst>,

Source§

fn overflowing_cast_from(src: Src) -> (Dst, bool)

Casts the value.
Source§

impl<T> SaturatingAs for T

Source§

fn saturating_as<Dst>(self) -> Dst
where T: SaturatingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> SaturatingCastFrom<Src> for Dst
where Src: SaturatingCast<Dst>,

Source§

fn saturating_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> UnwrappedAs for T

Source§

fn unwrapped_as<Dst>(self) -> Dst
where T: UnwrappedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> UnwrappedCastFrom<Src> for Dst
where Src: UnwrappedCast<Dst>,

Source§

fn unwrapped_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> WrappingAs for T

Source§

fn wrapping_as<Dst>(self) -> Dst
where T: WrappingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> WrappingCastFrom<Src> for Dst
where Src: WrappingCast<Dst>,

Source§

fn wrapping_cast_from(src: Src) -> Dst

Casts the value.