use client_domain::{BoundingBox, Color, DisplayPort, FontSize}; use embedded_graphics::{ mono_font::{ascii::FONT_6X10, ascii::FONT_10X20, MonoTextStyle}, pixelcolor::Rgb565, prelude::*, primitives::{PrimitiveStyle, Rectangle}, text::Text, }; #[derive(Debug)] pub enum DisplayError { Draw(String), } impl std::fmt::Display for DisplayError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { DisplayError::Draw(e) => write!(f, "draw: {e}"), } } } pub struct Esp32DisplayAdapter { inner: Box, } trait ErasedDisplay { fn draw_text_span(&mut self, text: &str, x: u16, y: u16, color: Rgb565, font: FontSize) -> Result<(), DisplayError>; fn fill_rect(&mut self, bounds: BoundingBox, color: Rgb565) -> Result<(), DisplayError>; fn flush(&mut self) -> Result<(), DisplayError>; } impl ErasedDisplay for D where D: DrawTarget, D::Error: std::fmt::Debug, { fn draw_text_span(&mut self, text: &str, x: u16, y: u16, color: Rgb565, font: FontSize) -> Result<(), DisplayError> { let (style, y_offset) = match font { FontSize::Small => (MonoTextStyle::new(&FONT_6X10, color), 10), FontSize::Large => (MonoTextStyle::new(&FONT_10X20, color), 20), }; Text::new(text, Point::new(x as i32, y as i32 + y_offset), style) .draw(self) .map_err(|e| DisplayError::Draw(format!("{e:?}")))?; Ok(()) } fn fill_rect(&mut self, bounds: BoundingBox, color: Rgb565) -> Result<(), DisplayError> { Rectangle::new( Point::new(bounds.x as i32, bounds.y as i32), Size::new(bounds.width as u32, bounds.height as u32), ) .into_styled(PrimitiveStyle::with_fill(color)) .draw(self) .map_err(|e| DisplayError::Draw(format!("{e:?}")))?; Ok(()) } fn flush(&mut self) -> Result<(), DisplayError> { Ok(()) } } fn to_rgb565(c: Color) -> Rgb565 { Rgb565::new(c.0 >> 3, c.1 >> 2, c.2 >> 3) } impl Esp32DisplayAdapter { pub fn new(display: D) -> Self where D: DrawTarget + 'static, D::Error: std::fmt::Debug, { Self { inner: Box::new(display), } } } impl DisplayPort for Esp32DisplayAdapter { type Error = DisplayError; fn draw_text_span( &mut self, text: &str, x: u16, y: u16, color: Color, font: FontSize, ) -> Result<(), Self::Error> { self.inner.draw_text_span(text, x, y, to_rgb565(color), font) } fn fill_rect(&mut self, bounds: BoundingBox, color: Color) -> Result<(), Self::Error> { self.inner.fill_rect(bounds, to_rgb565(color)) } fn flush(&mut self) -> Result<(), Self::Error> { self.inner.flush() } }