Skip to content
Snippets Groups Projects
controller.rs 5.95 KiB
Newer Older
Stephen D's avatar
Stephen D committed
use embedded_graphics::{pixelcolor::Rgb565, prelude::DrawTarget};
Stephen D's avatar
Stephen D committed
use heapless::{String, Vec};
Stephen D's avatar
Stephen D committed

use crate::{
Stephen D's avatar
Stephen D committed
    contact::{ContactGroup, MAX_CONTACTS, MAX_CONTACT_NAME_LENGTH},
Stephen D's avatar
Stephen D committed
    gui::{chat::Chat, contact_view::ContactView, selector::Selector, status::StatusBar, Element},
Stephen D's avatar
Stephen D committed
    keyboard::KeyCode,
    voltage::VoltMeter,
};

Stephen D's avatar
Stephen D committed
pub enum View {
    MainMenu(Selector<&'static str, [&'static str; 3]>),
Stephen D's avatar
Stephen D committed
    Contacts(
        Selector<
            String<MAX_CONTACT_NAME_LENGTH>,
            Vec<String<MAX_CONTACT_NAME_LENGTH>, MAX_CONTACTS>,
        >,
    ),
Stephen D's avatar
Stephen D committed
    ContactOptions(Selector<&'static str, [&'static str; 3]>, usize),
Stephen D's avatar
Stephen D committed
    #[allow(clippy::enum_variant_names)]
Stephen D's avatar
Stephen D committed
    ContactView(ContactView, usize),
Stephen D's avatar
Stephen D committed
    Chat(Chat),
}

impl View {
Stephen D's avatar
Stephen D committed
    pub fn new_main_menu(id: usize) -> Self {
Stephen D's avatar
Stephen D committed
        let menu = Selector::new(["Contacts", "Messages", "Settings"], id);
        Self::MainMenu(menu)
    }

Stephen D's avatar
Stephen D committed
    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> {
Stephen D's avatar
Stephen D committed
        match id {
Stephen D's avatar
Stephen D committed
            0 => Some(Self::new_contacts(0, contacts)),
Stephen D's avatar
Stephen D committed
            1 => Some(Self::Chat(Chat::new())),
            _ => None,
        }
    }

    pub fn to_main_menu_id(&self) -> usize {
        match self {
            View::Contacts(_) => 0,
            View::Chat(_) => 1,
Stephen D's avatar
Stephen D committed
            _ => unreachable!(),
        }
    }

    pub fn from_contact_options_id(
        id: usize,
        contact_id: usize,
        contacts: &ContactGroup,
    ) -> Option<Self> {
Stephen D's avatar
Stephen D committed
        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,
Stephen D's avatar
Stephen D committed
        }
    }
}

impl Default for View {
    fn default() -> Self {
Stephen D's avatar
Stephen D committed
        Self::new_main_menu(0)
Stephen D's avatar
Stephen D committed
    }
}

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),
Stephen D's avatar
Stephen D committed
            View::ContactOptions(c, _) => c.render(dt),
Stephen D's avatar
Stephen D committed
            View::ContactView(c, _) => c.render(dt),
Stephen D's avatar
Stephen D committed
            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),
Stephen D's avatar
Stephen D committed
            View::ContactOptions(c, _) => c.key_push(k),
Stephen D's avatar
Stephen D committed
            View::ContactView(c, _) => c.key_push(k),
Stephen D's avatar
Stephen D committed
            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),
Stephen D's avatar
Stephen D committed
            View::ContactOptions(c, _) => c.touchpad_scroll(x, y),
Stephen D's avatar
Stephen D committed
            View::ContactView(c, _) => c.touchpad_scroll(x, y),
Stephen D's avatar
Stephen D committed
            View::Chat(c) => c.touchpad_scroll(x, y),
        }
    }
}

Stephen D's avatar
Stephen D committed
pub struct Controller {
    status: StatusBar,
Stephen D's avatar
Stephen D committed
    cur_view: View,
Stephen D's avatar
Stephen D committed

    // May belong in a DataStore struct
    contacts: ContactGroup,
Stephen D's avatar
Stephen D committed
}

impl Controller {
    pub fn new(volt_meter: VoltMeter) -> Self {
        Self {
            status: StatusBar::new(volt_meter),
Stephen D's avatar
Stephen D committed
            cur_view: View::default(),
Stephen D's avatar
Stephen D committed

            contacts: ContactGroup::default(),
Stephen D's avatar
Stephen D committed
        }
    }
}

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)?;

Stephen D's avatar
Stephen D committed
        self.cur_view.render(dt)?;
Stephen D's avatar
Stephen D committed

        Ok(())
    }

    fn key_push(&mut self, k: KeyCode) {
Stephen D's avatar
Stephen D committed
        match (&self.cur_view, k) {
            (View::MainMenu(m), KeyCode::Touchpad) => {
Stephen D's avatar
Stephen D committed
                if let Some(new_view) = View::from_main_menu_id(m.selected(), &self.contacts) {
Stephen D's avatar
Stephen D committed
                    self.cur_view = new_view;
Stephen D's avatar
Stephen D committed
            (View::Contacts(c), KeyCode::Touchpad) => {
                // TODO need to handle "Add New" button
                self.cur_view = View::new_contact_options(c.selected());
            }

Stephen D's avatar
Stephen D committed
            (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;
                }
            }

Stephen D's avatar
Stephen D committed
            (View::MainMenu(_), KeyCode::Back) => {}
Stephen D's avatar
Stephen D committed

            (View::ContactOptions(_, id), KeyCode::Back) => {
                self.cur_view = View::new_contacts(*id, &self.contacts);
            }

Stephen D's avatar
Stephen D committed
            (View::ContactView(cv, contact_id), KeyCode::Back) => {
                self.contacts.contacts[*contact_id] = cv.contact();
Stephen D's avatar
Stephen D committed
                self.cur_view = View::new_contact_options(*contact_id)
            }

Stephen D's avatar
Stephen D committed
            (_, KeyCode::Back) => {
                let id = self.cur_view.to_main_menu_id();
Stephen D's avatar
Stephen D committed
                self.cur_view = View::new_main_menu(id);
Stephen D's avatar
Stephen D committed
            }
Stephen D's avatar
Stephen D committed
            (_, k) => self.cur_view.key_push(k),
Stephen D's avatar
Stephen D committed
        }
    }

    fn touchpad_scroll(&mut self, x: i8, y: i8) {
Stephen D's avatar
Stephen D committed
        self.cur_view.touchpad_scroll(x, y);