use core::marker::PhantomData; use embedded_graphics::{ mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder}, pixelcolor::Rgb565, prelude::{Point, RgbColor, Size}, primitives::{Primitive, PrimitiveStyleBuilder, Rectangle}, text::{Baseline, Text}, Drawable, }; use crate::keyboard::KeyCode; use super::{ scroll_tracker::{ScrollAction, ScrollTracker}, Element, }; const BACKGROUND_COLOR: Rgb565 = Rgb565::new(31, 41, 0); // #FFA500 const BORDER_COLOR: Rgb565 = Rgb565::new(31, 26, 0); // 0xFF6600 const CHAR_WIDTH: u32 = 10; const CHAR_HEIGHT: u32 = 20; const BORDER_WIDTH: u32 = 4; const SCREEN_WIDTH: u32 = 320; const SCREEN_HEIGHT: u32 = 240; pub struct Selector<T: AsRef<str>, V: AsRef<[T]>> { options: V, width: u32, height: u32, selected: usize, tracker: ScrollTracker, _phantom: PhantomData<T>, } 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(options.as_ref().len()).unwrap() * (CHAR_HEIGHT + BORDER_WIDTH) + BORDER_WIDTH; Self { options, width, height, selected, tracker: ScrollTracker::new(), _phantom: PhantomData, } } pub fn selected(&self) -> usize { self.selected } } impl<T: AsRef<str>, V: AsRef<[T]>> Element for Selector<T, V> { type KeyPushReturn = (); fn render< E, DT: embedded_graphics::prelude::DrawTarget< Color = embedded_graphics::pixelcolor::Rgb565, Error = E, >, >( &mut self, dt: &mut DT, ) -> Result<(), E> { // corner let start_x = i32::try_from(SCREEN_WIDTH / 2).unwrap() - i32::try_from(self.width / 2).unwrap(); let start_y = i32::try_from(SCREEN_HEIGHT / 2).unwrap() - i32::try_from(self.height / 2).unwrap(); let unselected_rect_style = PrimitiveStyleBuilder::new() .stroke_color(BORDER_COLOR) .stroke_width(BORDER_WIDTH) .fill_color(BACKGROUND_COLOR) .build(); let selected_rect_style = PrimitiveStyleBuilder::new() .stroke_color(BORDER_COLOR) .stroke_width(BORDER_WIDTH) .fill_color(Rgb565::WHITE) .build(); let text_style = MonoTextStyleBuilder::new() .font(&FONT_10X20) .text_color(Rgb565::BLACK) .build(); for (i, text) in self.options.as_ref().iter().enumerate() { // Draw our background rectangles let rect_style = if i == self.selected { selected_rect_style } else { unselected_rect_style }; let i: u32 = i.try_into().unwrap(); Rectangle::new( Point::new( start_x, start_y + i32::try_from(i * (CHAR_HEIGHT + BORDER_WIDTH)).unwrap(), ), Size::new(self.width, CHAR_HEIGHT + BORDER_WIDTH), ) .into_styled(rect_style) .draw(dt)?; // Render the text Text::with_baseline( text.as_ref(), Point::new( i32::try_from(BORDER_WIDTH).unwrap() + start_x, start_y + i32::try_from(i * (CHAR_HEIGHT + BORDER_WIDTH) + 1).unwrap(), ), text_style, Baseline::Top, ) .draw(dt)?; } Ok(()) } fn key_push(&mut self, _k: KeyCode) {} fn touchpad_scroll(&mut self, _x: i8, y: i8) { match self.tracker.scroll(y) { ScrollAction::Previous => { if self.selected > 0 { self.selected -= 1; } } ScrollAction::Next => { if self.selected < self.options.as_ref().len() - 1 { self.selected += 1; } } ScrollAction::None => {} } } }