Skip to content
Snippets Groups Projects
Commit 413684db authored by Stephen D's avatar Stephen D
Browse files

contacts

parent 5386c939
No related branches found
No related tags found
No related merge requests found
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 }
}
}
use embedded_graphics::{pixelcolor::Rgb565, prelude::DrawTarget}; use embedded_graphics::{pixelcolor::Rgb565, prelude::DrawTarget};
use heapless::{String, Vec};
use crate::{ use crate::{
contact::{Contact, ContactGroup},
gui::{chat::Chat, selector::Selector, status::StatusBar, Element}, gui::{chat::Chat, selector::Selector, status::StatusBar, Element},
keyboard::KeyCode, keyboard::KeyCode,
voltage::VoltMeter, 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 { pub struct Controller {
status: StatusBar, status: StatusBar,
selector: Selector<4>, cur_view: View,
chat: Chat,
in_chat: bool,
} }
impl Controller { impl Controller {
pub fn new(volt_meter: VoltMeter) -> Self { pub fn new(volt_meter: VoltMeter) -> Self {
Self { Self {
status: StatusBar::new(volt_meter), status: StatusBar::new(volt_meter),
selector: Selector::new(["Contacts", "Messages", "Settings", "mrrp meow"]), cur_view: View::default(),
chat: Chat::new(),
in_chat: false,
} }
} }
} }
...@@ -33,35 +104,30 @@ impl Element for Controller { ...@@ -33,35 +104,30 @@ impl Element for Controller {
self.status.render(dt)?; self.status.render(dt)?;
if self.in_chat { self.cur_view.render(dt)?;
self.chat.render(dt)?;
} else {
self.selector.render(dt)?;
}
Ok(()) Ok(())
} }
fn key_push(&mut self, k: KeyCode) { fn key_push(&mut self, k: KeyCode) {
match k { match (&self.cur_view, k) {
KeyCode::Touchpad => { (View::MainMenu(m), KeyCode::Touchpad) => {
if self.selector.selected() == 1 { if let Some(new_view) = View::from_main_menu_id(m.selected()) {
self.in_chat = true; 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) { fn touchpad_scroll(&mut self, x: i8, y: i8) {
if !self.in_chat { self.cur_view.touchpad_scroll(x, y);
self.selector.touchpad_scroll(x, y);
}
} }
} }
use core::marker::PhantomData;
use embedded_graphics::{ use embedded_graphics::{
mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder}, mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder},
pixelcolor::Rgb565, pixelcolor::Rgb565,
...@@ -17,26 +19,39 @@ const BORDER_WIDTH: u32 = 4; ...@@ -17,26 +19,39 @@ const BORDER_WIDTH: u32 = 4;
const SCREEN_WIDTH: u32 = 320; const SCREEN_WIDTH: u32 = 320;
const SCREEN_HEIGHT: u32 = 240; const SCREEN_HEIGHT: u32 = 240;
pub struct Selector<const N: usize> { pub struct Selector<T: AsRef<str>, V: AsRef<[T]>> {
options: [&'static str; N], options: V,
width: u32, width: u32,
height: u32, height: u32,
selected: usize, selected: usize,
y_tracker: i8, y_tracker: i8,
_phantom: PhantomData<T>,
} }
impl<const N: usize> Selector<N> { impl<T: AsRef<str>, V: AsRef<[T]>> Selector<T, V> {
pub fn new(options: [&'static str; N]) -> Self { pub fn new(options: V, selected: usize) -> Self {
let width = u32::try_from(options.iter().map(|x| x.len()).max().unwrap_or(0)).unwrap() 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 * CHAR_WIDTH
+ BORDER_WIDTH * 2; + 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 { Self {
options, options,
width, width,
height, height,
selected: 0, selected,
y_tracker: 0, y_tracker: 0,
_phantom: PhantomData,
} }
} }
...@@ -45,7 +60,7 @@ impl<const N: usize> Selector<N> { ...@@ -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< fn render<
E, E,
DT: embedded_graphics::prelude::DrawTarget< DT: embedded_graphics::prelude::DrawTarget<
...@@ -77,7 +92,7 @@ impl<const N: usize> Element for Selector<N> { ...@@ -77,7 +92,7 @@ impl<const N: usize> Element for Selector<N> {
.text_color(Rgb565::BLACK) .text_color(Rgb565::BLACK)
.build(); .build();
for (i, text) in self.options.iter().enumerate() { for (i, text) in self.options.as_ref().iter().enumerate() {
// Draw our background rectangles // Draw our background rectangles
let rect_style = if i == self.selected { let rect_style = if i == self.selected {
selected_rect_style selected_rect_style
...@@ -101,7 +116,7 @@ impl<const N: usize> Element for Selector<N> { ...@@ -101,7 +116,7 @@ impl<const N: usize> Element for Selector<N> {
// Render the text // Render the text
Text::with_baseline( Text::with_baseline(
text, text.as_ref(),
Point::new( Point::new(
(BORDER_WIDTH + start_x).try_into().unwrap(), (BORDER_WIDTH + start_x).try_into().unwrap(),
(start_y + i * (CHAR_HEIGHT + BORDER_WIDTH) + 1) (start_y + i * (CHAR_HEIGHT + BORDER_WIDTH) + 1)
...@@ -128,7 +143,7 @@ impl<const N: usize> Element for Selector<N> { ...@@ -128,7 +143,7 @@ impl<const N: usize> Element for Selector<N> {
} else if self.y_tracker > 50 { } else if self.y_tracker > 50 {
self.y_tracker = 0; self.y_tracker = 0;
if self.selected < N - 1 { if self.selected < self.options.as_ref().len() - 1 {
self.selected += 1; self.selected += 1;
} }
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#![no_main] #![no_main]
mod buffered_display; mod buffered_display;
mod contact;
mod controller; mod controller;
mod gui; mod gui;
mod keyboard; mod keyboard;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment