use embedded_graphics::{pixelcolor::Rgb565, prelude::DrawTarget}; use heapless::{String, Vec}; use crate::{ contact::{ContactGroup, MAX_CONTACTS, MAX_CONTACT_NAME_LENGTH}, gui::{chat::Chat, contact_view::ContactView, selector::Selector, status::StatusBar, Element}, keyboard::KeyCode, voltage::VoltMeter, }; pub enum View { MainMenu(Selector<&'static str, [&'static str; 3]>), Contacts( Selector< String<MAX_CONTACT_NAME_LENGTH>, Vec<String<MAX_CONTACT_NAME_LENGTH>, MAX_CONTACTS>, >, ), ContactOptions(Selector<&'static str, [&'static str; 3]>, usize), #[allow(clippy::enum_variant_names)] ContactView(ContactView, usize), Chat(Chat), } impl View { pub fn new_main_menu(id: usize) -> Self { let menu = Selector::new(["Contacts", "Messages", "Settings"], id); Self::MainMenu(menu) } pub fn new_contacts(id: usize, contacts: &ContactGroup) -> Self { let mut contact_names: Vec<_, MAX_CONTACTS> = contacts.contacts.iter().map(|c| c.name.clone()).collect(); // If we run out of space, we automatically hide the "Add" button let _ = contact_names.push(String::try_from("Add New").unwrap()); Self::Contacts(Selector::new(contact_names, id)) } pub fn new_contact_options(contact_id: usize) -> Self { let opts = Selector::new(["View", "Edit", "Message"], 0); Self::ContactOptions(opts, contact_id) } pub fn from_main_menu_id(id: usize, contacts: &ContactGroup) -> Option<Self> { match id { 0 => Some(Self::new_contacts(0, contacts)), 1 => Some(Self::Chat(Chat::new())), _ => None, } } pub fn to_main_menu_id(&self) -> usize { match self { View::Contacts(_) => 0, View::Chat(_) => 1, _ => unreachable!(), } } pub fn from_contact_options_id( id: usize, contact_id: usize, contacts: &ContactGroup, ) -> Option<Self> { match id { 0 => { let contact = contacts.contacts[contact_id].clone(); Some(View::ContactView( ContactView::new(contact, false), contact_id, )) } 1 => { let contact = contacts.contacts[contact_id].clone(); Some(View::ContactView( ContactView::new(contact, true), contact_id, )) } _ => None, } } } impl Default for View { fn default() -> Self { Self::new_main_menu(0) } } impl Element for View { fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>( &mut self, dt: &mut DT, ) -> Result<(), E> { match self { View::MainMenu(m) => m.render(dt), View::Contacts(c) => c.render(dt), View::ContactOptions(c, _) => c.render(dt), View::ContactView(c, _) => c.render(dt), View::Chat(c) => c.render(dt), } } fn key_push(&mut self, k: KeyCode) { match self { View::MainMenu(m) => m.key_push(k), View::Contacts(c) => c.key_push(k), View::ContactOptions(c, _) => c.key_push(k), View::ContactView(c, _) => c.key_push(k), View::Chat(c) => c.key_push(k), } } fn touchpad_scroll(&mut self, x: i8, y: i8) { match self { View::MainMenu(m) => m.touchpad_scroll(x, y), View::Contacts(c) => c.touchpad_scroll(x, y), View::ContactOptions(c, _) => c.touchpad_scroll(x, y), View::ContactView(c, _) => c.touchpad_scroll(x, y), View::Chat(c) => c.touchpad_scroll(x, y), } } } pub struct Controller { status: StatusBar, cur_view: View, // May belong in a DataStore struct contacts: ContactGroup, } impl Controller { pub fn new(volt_meter: VoltMeter) -> Self { Self { status: StatusBar::new(volt_meter), cur_view: View::default(), contacts: ContactGroup::default(), } } } impl Element for Controller { fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>( &mut self, dt: &mut DT, ) -> Result<(), E> { dt.clear(Rgb565::new(0, 32, 16))?; self.status.render(dt)?; self.cur_view.render(dt)?; Ok(()) } fn key_push(&mut self, k: KeyCode) { match (&self.cur_view, k) { (View::MainMenu(m), KeyCode::Touchpad) => { if let Some(new_view) = View::from_main_menu_id(m.selected(), &self.contacts) { self.cur_view = new_view; } } (View::Contacts(c), KeyCode::Touchpad) => { // TODO need to handle "Add New" button self.cur_view = View::new_contact_options(c.selected()); } (View::ContactOptions(c, contact_id), KeyCode::Touchpad) => { if let Some(new_view) = View::from_contact_options_id(c.selected(), *contact_id, &self.contacts) { self.cur_view = new_view; } } (View::MainMenu(_), KeyCode::Back) => {} (View::ContactOptions(_, id), KeyCode::Back) => { self.cur_view = View::new_contacts(*id, &self.contacts); } (View::ContactView(cv, contact_id), KeyCode::Back) => { self.contacts.contacts[*contact_id] = cv.contact(); self.cur_view = View::new_contact_options(*contact_id) } (_, KeyCode::Back) => { let id = self.cur_view.to_main_menu_id(); self.cur_view = View::new_main_menu(id); } (_, k) => self.cur_view.key_push(k), } } fn touchpad_scroll(&mut self, x: i8, y: i8) { self.cur_view.touchpad_scroll(x, y); } }