Implement simple menu system

This commit is contained in:
Sofia 2026-06-02 12:10:25 +03:00
parent b6517d2ee6
commit e30e71516e
2 changed files with 136 additions and 5 deletions

View File

@ -157,3 +157,82 @@ where
}
}
}
const MENU_HEIGHT: usize = 240 / 30;
pub trait MenuItem {
fn as_text(&self) -> String;
}
#[derive(Default, Debug, Clone)]
pub struct Menu<T: MenuItem> {
items: Vec<T>,
idx: usize,
scroll: usize,
}
impl<T: MenuItem> Menu<T> {
pub fn with_item(mut self, item: T) -> Menu<T> {
self.items.push(item);
self
}
pub fn poll<'s>(&'s mut self, io: &mut AsyncIO) -> Option<&'s T> {
use crate::async_io::KeypadButton;
for _ in 0..io.keypad.get_presses(KeypadButton::KeypadC) {
self.previous();
}
for _ in 0..io.keypad.get_presses(KeypadButton::KeypadD) {
self.next();
}
if io.keypad.get_presses(KeypadButton::KeypadA) > 0 {
Some(&self.items[self.idx])
} else {
None
}
}
pub fn previous(&mut self) {
if self.idx == 0 {
// Wrap index around
self.idx = self.items.len() - 1;
if self.idx >= (self.scroll + MENU_HEIGHT) {
self.scroll = (self.idx - MENU_HEIGHT) + 1;
}
} else {
self.idx -= 1;
if self.idx < self.scroll {
self.scroll = self.idx;
}
}
}
pub fn next(&mut self) {
self.idx = (self.idx + 1) % self.items.len();
if self.idx == 0 {
self.scroll = 0;
} else if self.idx >= (self.scroll + MENU_HEIGHT) {
self.scroll = (self.idx - MENU_HEIGHT) + 1;
}
}
pub fn draw(&self, data: &mut StateData) {
for (i, (idx, item)) in self
.items
.iter()
.enumerate()
.skip(self.scroll)
.take(MENU_HEIGHT)
.enumerate()
{
let mut settings = TextSettings::default();
if idx == self.idx {
settings.bg = Rgb565::white();
settings.fg = Rgb565::black();
}
data.draw_text(item.as_text(), Position::new(0, (i * 30) as i16), settings);
}
}
}

View File

@ -17,7 +17,7 @@ use crate::{
},
display::{Position, Rgb565},
font::{HorizontalAlignment, VerticalAlignment},
state::{ATCommandHelper, State, StateData, TextSettings},
state::{ATCommandHelper, Menu, MenuItem, State, StateData, TextSettings},
};
#[derive(Debug, Clone)]
@ -232,10 +232,7 @@ impl State for EnterPinState {
match helper.poll(&mut data.io) {
Some(response) => match response.unwrap() {
EnterPinResult::Ok => {
return Some(Box::new(TextState {
text: "SIM entered\nsuccessfully".to_owned(),
after: PhoneNumberState::default(),
}));
return Some(Box::new(MainMenuState::default()));
}
EnterPinResult::Error => {
return Some(Box::new(TextState {
@ -275,6 +272,61 @@ impl State for EnterPinState {
}
}
#[derive(Debug, Default)]
enum MainMenuItem {
#[default]
Item1,
Item2,
Item3,
Item4,
Item5,
Item6,
}
impl MenuItem for MainMenuItem {
fn as_text(&self) -> String {
match self {
MainMenuItem::Item1 => "item1",
MainMenuItem::Item2 => "item2",
MainMenuItem::Item3 => "item3",
MainMenuItem::Item4 => "item4",
MainMenuItem::Item5 => "item5",
MainMenuItem::Item6 => "item6",
}
.to_string()
}
}
#[derive(Debug)]
pub struct MainMenuState {
menu: Menu<MainMenuItem>,
}
impl Default for MainMenuState {
fn default() -> Self {
Self {
menu: Menu::default()
.with_item(MainMenuItem::Item1)
.with_item(MainMenuItem::Item2)
.with_item(MainMenuItem::Item3)
.with_item(MainMenuItem::Item4)
.with_item(MainMenuItem::Item5)
.with_item(MainMenuItem::Item6),
}
}
}
impl State for MainMenuState {
fn update(&mut self, data: &mut StateData) -> Option<Box<dyn State>> {
self.menu.poll(&mut data.io);
None
}
fn draw(&self, data: &mut StateData) {
data.clear_screen(Rgb565::black().as_color());
self.menu.draw(data);
}
}
#[derive(Clone, Default)]
pub struct PhoneNumberState {
input: NumberInput,