From 413684db84ef416a2ff94702cfd7338e6b37fdc2 Mon Sep 17 00:00:00 2001 From: Stephen D <webmaster@scd31.com> Date: Fri, 29 Mar 2024 22:49:19 -0300 Subject: [PATCH] contacts --- src/contact.rs | 42 +++++++++++++++++ src/controller.rs | 112 +++++++++++++++++++++++++++++++++++--------- src/gui/selector.rs | 37 ++++++++++----- src/main.rs | 1 + 4 files changed, 158 insertions(+), 34 deletions(-) create mode 100644 src/contact.rs diff --git a/src/contact.rs b/src/contact.rs new file mode 100644 index 0000000..3b43b61 --- /dev/null +++ b/src/contact.rs @@ -0,0 +1,42 @@ +use heapless::{String, Vec}; + +#[derive(Debug, Clone)] +pub struct Contact { + callsign: String<10>, + ssid: u8, + pub name: String<30>, +} + +#[derive(Debug)] +pub struct ContactGroup { + pub contacts: Vec<Contact, 20>, +} + +impl Default for ContactGroup { + fn default() -> Self { + let mut contacts = Vec::new(); + contacts + .push(Contact { + callsign: String::try_from("VA3QAK").unwrap(), + ssid: 19, + name: String::try_from("Adrian").unwrap(), + }) + .unwrap(); + contacts + .push(Contact { + callsign: String::try_from("VE3SVF").unwrap(), + ssid: 239, + name: String::try_from("Sasha").unwrap(), + }) + .unwrap(); + contacts + .push(Contact { + callsign: String::try_from("VE3KCN").unwrap(), + ssid: 19, + name: String::try_from("Cam").unwrap(), + }) + .unwrap(); + + Self { contacts } + } +} diff --git a/src/controller.rs b/src/controller.rs index 9889ed3..c091e51 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,25 +1,96 @@ use embedded_graphics::{pixelcolor::Rgb565, prelude::DrawTarget}; +use heapless::{String, Vec}; use crate::{ + contact::{Contact, ContactGroup}, gui::{chat::Chat, selector::Selector, status::StatusBar, Element}, keyboard::KeyCode, voltage::VoltMeter, }; +pub enum View { + MainMenu(Selector<&'static str, [&'static str; 3]>), + Contacts(Selector<String<30>, Vec<String<30>, 3>>), + Chat(Chat), +} + +impl View { + pub fn main_menu_with_id(id: usize) -> Self { + let menu = Selector::new(["Contacts", "Messages", "Settings"], id); + Self::MainMenu(menu) + } + + pub fn from_main_menu_id(id: usize) -> Option<Self> { + match id { + 0 => { + let group = ContactGroup::default(); + let contact_names: Vec<_, 3> = + group.contacts.iter().map(|c| c.name.clone()).collect(); + + Some(Self::Contacts(Selector::new( + contact_names.try_into().unwrap(), + 0, + ))) + } + 1 => Some(Self::Chat(Chat::new())), + _ => None, + } + } + + pub fn to_main_menu_id(&self) -> usize { + match self { + View::MainMenu(_) => unreachable!(), + View::Contacts(_) => 0, + View::Chat(_) => 1, + } + } +} + +impl Default for View { + fn default() -> Self { + Self::main_menu_with_id(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::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::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::Chat(c) => c.touchpad_scroll(x, y), + } + } +} + pub struct Controller { status: StatusBar, - selector: Selector<4>, - chat: Chat, - in_chat: bool, + cur_view: View, } impl Controller { pub fn new(volt_meter: VoltMeter) -> Self { Self { status: StatusBar::new(volt_meter), - selector: Selector::new(["Contacts", "Messages", "Settings", "mrrp meow"]), - chat: Chat::new(), - in_chat: false, + cur_view: View::default(), } } } @@ -33,35 +104,30 @@ impl Element for Controller { self.status.render(dt)?; - if self.in_chat { - self.chat.render(dt)?; - } else { - self.selector.render(dt)?; - } + self.cur_view.render(dt)?; Ok(()) } fn key_push(&mut self, k: KeyCode) { - match k { - KeyCode::Touchpad => { - if self.selector.selected() == 1 { - self.in_chat = true; + match (&self.cur_view, k) { + (View::MainMenu(m), KeyCode::Touchpad) => { + if let Some(new_view) = View::from_main_menu_id(m.selected()) { + self.cur_view = new_view; } } - KeyCode::Back => { - self.in_chat = false; - } - _ if self.in_chat => self.chat.key_push(k), + (View::MainMenu(_), KeyCode::Back) => {} + (_, KeyCode::Back) => { + let id = self.cur_view.to_main_menu_id(); + self.cur_view = View::main_menu_with_id(id); + } - _ => {} + (_, k) => self.cur_view.key_push(k), } } fn touchpad_scroll(&mut self, x: i8, y: i8) { - if !self.in_chat { - self.selector.touchpad_scroll(x, y); - } + self.cur_view.touchpad_scroll(x, y); } } diff --git a/src/gui/selector.rs b/src/gui/selector.rs index 3c2142b..bf5cabf 100644 --- a/src/gui/selector.rs +++ b/src/gui/selector.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use embedded_graphics::{ mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder}, pixelcolor::Rgb565, @@ -17,26 +19,39 @@ const BORDER_WIDTH: u32 = 4; const SCREEN_WIDTH: u32 = 320; const SCREEN_HEIGHT: u32 = 240; -pub struct Selector<const N: usize> { - options: [&'static str; N], +pub struct Selector<T: AsRef<str>, V: AsRef<[T]>> { + options: V, width: u32, height: u32, selected: usize, y_tracker: i8, + _phantom: PhantomData<T>, } -impl<const N: usize> Selector<N> { - pub fn new(options: [&'static str; N]) -> Self { - let width = u32::try_from(options.iter().map(|x| x.len()).max().unwrap_or(0)).unwrap() +impl<T: AsRef<str>, V: AsRef<[T]>> Selector<T, V> { + pub fn new(options: V, selected: usize) -> Self { + assert!(selected < options.as_ref().len()); + + let width = u32::try_from( + options + .as_ref() + .iter() + .map(|x| x.as_ref().len()) + .max() + .unwrap_or(0), + ) + .unwrap() * CHAR_WIDTH + BORDER_WIDTH * 2; - let height = u32::try_from(N).unwrap() * (CHAR_HEIGHT + BORDER_WIDTH) + BORDER_WIDTH; + let height = u32::try_from(options.as_ref().len()).unwrap() * (CHAR_HEIGHT + BORDER_WIDTH) + + BORDER_WIDTH; Self { options, width, height, - selected: 0, + selected, y_tracker: 0, + _phantom: PhantomData, } } @@ -45,7 +60,7 @@ impl<const N: usize> Selector<N> { } } -impl<const N: usize> Element for Selector<N> { +impl<T: AsRef<str>, V: AsRef<[T]>> Element for Selector<T, V> { fn render< E, DT: embedded_graphics::prelude::DrawTarget< @@ -77,7 +92,7 @@ impl<const N: usize> Element for Selector<N> { .text_color(Rgb565::BLACK) .build(); - for (i, text) in self.options.iter().enumerate() { + for (i, text) in self.options.as_ref().iter().enumerate() { // Draw our background rectangles let rect_style = if i == self.selected { selected_rect_style @@ -101,7 +116,7 @@ impl<const N: usize> Element for Selector<N> { // Render the text Text::with_baseline( - text, + text.as_ref(), Point::new( (BORDER_WIDTH + start_x).try_into().unwrap(), (start_y + i * (CHAR_HEIGHT + BORDER_WIDTH) + 1) @@ -128,7 +143,7 @@ impl<const N: usize> Element for Selector<N> { } else if self.y_tracker > 50 { self.y_tracker = 0; - if self.selected < N - 1 { + if self.selected < self.options.as_ref().len() - 1 { self.selected += 1; } } diff --git a/src/main.rs b/src/main.rs index 7154345..9d56274 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ #![no_main] mod buffered_display; +mod contact; mod controller; mod gui; mod keyboard; -- GitLab