embedded_multi_page_hmi/page/
menu.rs

1use super::basic::BasicPage;
2use arrayvec::ArrayString;
3
4/// A menu page organizes navigation to sub pages
5///
6/// A menu pages lists the titles of the sub-pages and allows
7/// navigation to a specific sub-page
8pub struct MenuPage<'a> {
9    pub basic: BasicPage,
10    selected: usize,
11    max_items: usize,
12    pub sub_titles: ArrayString<100>, // is public to be accessed from outside implementation of PageInterface trait
13    back: Option<&'a str>,            // the Back menu entry in language
14}
15
16impl<'a> MenuPage<'a> {
17    pub fn new(basic: BasicPage, back: Option<&'a str>) -> Self {
18        MenuPage {
19            basic,
20            selected: 1,
21            max_items: 1,
22            sub_titles: ArrayString::<100>::new(),
23            back,
24        }
25    }
26}
27
28use super::super::*;
29
30impl PageInteractionInterface for MenuPage<'_> {
31    fn dispatch(&mut self, interaction: Interaction) -> PageNavigation {
32        match interaction {
33            Interaction::Action => match self.back {
34                None => PageNavigation::NthSubpage(self.selected),
35                Some(_) => {
36                    // Back navigation is simulated and last in list
37                    if self.selected == self.max_items {
38                        PageNavigation::Up
39                    } else {
40                        PageNavigation::NthSubpage(self.selected)
41                    }
42                }
43            },
44            Interaction::Back => PageNavigation::Up,
45            Interaction::Home => PageNavigation::Home,
46            Interaction::Next => {
47                self.selected += 1;
48                if self.selected > self.max_items {
49                    self.selected = 1;
50                }
51                PageNavigation::Update
52            }
53            // if previous interaction is not available, this implementation is never called
54            // but it does not hurt
55            Interaction::Previous => {
56                self.selected -= 1;
57                if self.selected == 0 {
58                    self.selected = 1;
59                }
60                PageNavigation::Update
61            }
62        }
63    }
64}
65
66impl PageBaseInterface for MenuPage<'_> {
67    fn update<'a>(
68        &mut self,
69        title_of_subpages: Option<Box<dyn Iterator<Item = &'a str> + 'a>>,
70    ) -> Result<PageNavigation, PageError> {
71        if let Some(title_iterator) = title_of_subpages {
72            self.max_items = 0;
73            self.sub_titles.clear();
74
75            for title in title_iterator {
76                self.max_items += 1;
77                if self.max_items == self.selected {
78                    self.sub_titles.push_str("[ ");
79                }
80                self.sub_titles.push_str(title);
81                if self.max_items == self.selected {
82                    self.sub_titles.push_str(" ]");
83                }
84                self.sub_titles.push(' ');
85            }
86
87            // Optional back navigation menu entry is always placed at the end
88            if let Some(back_text) = self.back {
89                self.max_items += 1;
90                if self.max_items == self.selected {
91                    self.sub_titles.push_str("[ ");
92                }
93                self.sub_titles.push_str(back_text);
94                if self.max_items == self.selected {
95                    self.sub_titles.push_str(" ]");
96                }
97                self.sub_titles.push(' ');
98            }
99        }
100        Ok(PageNavigation::Update)
101    }
102
103    fn title(&self) -> &str {
104        self.basic.title
105    }
106}
107
108#[cfg(test)]
109mod tests;