Skip to content
Snippets Groups Projects
controller.rs 7.99 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
    gui::{
        chat::Chat, chat_list::ChatList, contact_view::ContactView, selector::Selector,
        status::StatusBar, Element,
    },
Stephen D's avatar
Stephen D committed
    keyboard::KeyCode,
    storage::{ContactIter, Storage, MAX_CONTACTS, MAX_CONTACT_NAME_LENGTH},
Stephen D's avatar
Stephen D committed
    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)]
    ContactView(ContactView, usize, usize),
Stephen D's avatar
Stephen D committed
    ChatList(ChatList),
Stephen D's avatar
Stephen D committed
    Chat(Chat, usize),
Stephen D's avatar
Stephen D committed
}

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: &ContactIter) -> Self {
        let mut contact_names: Vec<_, MAX_CONTACTS> = contacts.map(|c| c.name.clone()).collect();
Stephen D's avatar
Stephen D committed

        // 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, selected_id: usize) -> Self {
        let opts = Selector::new(["View", "Edit", "Message"], selected_id);
Stephen D's avatar
Stephen D committed
        Self::ContactOptions(opts, contact_id)
    }

Stephen D's avatar
Stephen D committed
    pub fn from_main_menu_id(id: usize, contacts: &ContactIter, storage: &Storage) -> 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::ChatList(ChatList::new(storage, 0))),
Stephen D's avatar
Stephen D committed
            _ => None,
        }
    }

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

    pub fn from_contact_options_id(
        id: usize,
        contact_id: usize,
Stephen D's avatar
Stephen D committed
        storage: &Storage,
Stephen D's avatar
Stephen D committed
    ) -> Option<Self> {
Stephen D's avatar
Stephen D committed
        let mut contacts = storage.contacts();

Stephen D's avatar
Stephen D committed
        match id {
            0 => {
Stephen D's avatar
Stephen D committed
                let contact = contacts
                    .nth(contact_id)
Stephen D's avatar
Stephen D committed
                    .unwrap_or_else(|| storage.new_contact());
Stephen D's avatar
Stephen D committed
                Some(View::ContactView(
                    ContactView::new(contact, false),
                    contact_id,
Stephen D's avatar
Stephen D committed
                ))
            }

            1 => {
Stephen D's avatar
Stephen D committed
                let contact = contacts
                    .nth(contact_id)
Stephen D's avatar
Stephen D committed
                    .unwrap_or_else(|| storage.new_contact());
Stephen D's avatar
Stephen D committed
                Some(View::ContactView(
                    ContactView::new(contact, true),
                    contact_id,
Stephen D's avatar
Stephen D committed
                ))
            }

Stephen D's avatar
Stephen D committed
            2 => {
                let contact = contacts
                    .nth(contact_id)
                    .unwrap_or_else(|| storage.new_contact());
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
                Some(View::Chat(Chat::new(contact.callsign, contact.ssid), 0))
            }
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
            _ => unreachable!(),
        }
    }
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
    fn render<E, DT: DrawTarget<Color = Rgb565, Error = E>>(
        &mut self,
        dt: &mut DT,
Stephen D's avatar
Stephen D committed
        storage: &Storage,
Stephen D's avatar
Stephen D committed
    ) -> 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),
            View::ContactView(c, _, _) => c.render(dt),
Stephen D's avatar
Stephen D committed
            View::ChatList(c) => c.render(dt, storage),
            View::Chat(c, _) => c.render(dt, storage),
Stephen D's avatar
Stephen D committed
        }
    }

Stephen D's avatar
Stephen D committed
    fn key_push(&mut self, k: KeyCode, storage: &mut Storage) {
Stephen D's avatar
Stephen D committed
        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),
            View::ContactView(c, _, _) => {
Stephen D's avatar
Stephen D committed
                c.key_push(k);
            }
Stephen D's avatar
Stephen D committed
            View::ChatList(c) => c.key_push(k),
Stephen D's avatar
Stephen D committed
            View::Chat(c, _) => c.key_push(k, storage),
Stephen D's avatar
Stephen D committed
        }
    }

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

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

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

Stephen D's avatar
Stephen D committed
    storage: Storage,
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

Stephen D's avatar
Stephen D committed
            storage: Storage::new(),
Stephen D's avatar
Stephen D committed
        }
    }
}

impl Element for Controller {
Stephen D's avatar
Stephen D committed
    type KeyPushReturn = ();
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
    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, &self.storage)?;
Stephen D's avatar
Stephen D committed

        Ok(())
    }

    fn key_push(&mut self, k: KeyCode) {
Stephen D's avatar
Stephen D committed
        match (&mut self.cur_view, k) {
Stephen D's avatar
Stephen D committed
            (View::MainMenu(m), KeyCode::Touchpad) => {
Stephen D's avatar
Stephen D committed
                if let Some(new_view) =
Stephen D's avatar
Stephen D committed
                    View::from_main_menu_id(m.selected(), &self.storage.contacts(), &self.storage)
Stephen D's avatar
Stephen D committed
                {
Stephen D's avatar
Stephen D committed
                    self.cur_view = new_view;
Stephen D's avatar
Stephen D committed
            (View::Contacts(c), KeyCode::Touchpad) => {
Stephen D's avatar
Stephen D committed
                if c.selected() == self.storage.contacts_len() {
Stephen D's avatar
Stephen D committed
                    // Go direct to edit page
                    self.cur_view =
Stephen D's avatar
Stephen D committed
                        View::from_contact_options_id(1, c.selected(), &self.storage).unwrap();
Stephen D's avatar
Stephen D committed
                } else {
                    self.cur_view = View::new_contact_options(c.selected(), 0);
Stephen D's avatar
Stephen D committed
            }

Stephen D's avatar
Stephen D committed
            (View::ContactOptions(c, contact_id), KeyCode::Touchpad) => {
Stephen D's avatar
Stephen D committed
                if let Some(new_view) =
                    View::from_contact_options_id(c.selected(), *contact_id, &self.storage)
                {
Stephen D's avatar
Stephen D committed
                    self.cur_view = new_view;
                }
            }

Stephen D's avatar
Stephen D committed
            (View::ChatList(cl), KeyCode::Touchpad) => {
Stephen D's avatar
Stephen D committed
                let (callsign, ssid) = self
                    .storage
                    .message_addressees()
                    .nth(cl.selected())
                    .unwrap();

                self.cur_view = View::Chat(Chat::new(callsign, ssid), cl.selected());
Stephen D's avatar
Stephen D committed
            }

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

            (View::ContactOptions(_, id), KeyCode::Back) => {
Stephen D's avatar
Stephen D committed
                self.cur_view = View::new_contacts(*id, &self.storage.contacts());
Stephen D's avatar
Stephen D committed
            }

            (View::ContactView(cv, contact_id, selected_id), KeyCode::Back) => {
                // TODO only need to call this if we actually edited
Stephen D's avatar
Stephen D committed
                self.storage.save_contact(cv.contact());

                self.cur_view = View::new_contact_options(*contact_id, *selected_id);
Stephen D's avatar
Stephen D committed
            }

Stephen D's avatar
Stephen D committed
            (View::Chat(_, selected_id), KeyCode::Back) => {
Stephen D's avatar
Stephen D committed
                self.cur_view = View::ChatList(ChatList::new(&self.storage, *selected_id));
Stephen D's avatar
Stephen D committed
            }

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
            }
            (View::ContactView(cv, contact_id, _), k) => {
Stephen D's avatar
Stephen D committed
                if cv.key_push(k) {
Stephen D's avatar
Stephen D committed
                    self.storage.delete_contact(*contact_id);
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
                    self.cur_view = View::new_contacts(*contact_id, &self.storage.contacts());
Stephen D's avatar
Stephen D committed
            (_, k) => self.cur_view.key_push(k, &mut self.storage),
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);