use embedded_graphics::{ geometry::{Point, Size}, mono_font::{ascii::FONT_10X20, MonoTextStyle}, pixelcolor::{Rgb565, RgbColor}, primitives::{Primitive, PrimitiveStyleBuilder, Rectangle}, text::renderer::TextRenderer, Drawable, }; use embedded_text::{style::TextBoxStyleBuilder, TextBox}; use heapless::String; use crate::{ app::WIDTH, keyboard::KeyCode, storage::{Message, MessageDirection, Storage, MAX_CONTACT_CALLSIGN_LENGTH}, }; use super::{textbox::TextBox as MyTextBox, Element}; const MESSAGE_BOX_WIDTH: u32 = 200; const MESSAGE_BOX_HEIGHT: u32 = 64; pub struct Chat { callsign: String<MAX_CONTACT_CALLSIGN_LENGTH>, ssid: u8, textbox: MyTextBox, } impl Chat { pub fn new(callsign: String<MAX_CONTACT_CALLSIGN_LENGTH>, ssid: u8) -> Self { Self { callsign, ssid, textbox: MyTextBox::new(1, 218, 31, 1), } } pub fn render< E, DT: embedded_graphics::prelude::DrawTarget< Color = embedded_graphics::pixelcolor::Rgb565, Error = E, >, >( &mut self, dt: &mut DT, storage: &Storage, ) -> Result<(), E> { let textbox_style = TextBoxStyleBuilder::new().build(); let character_style = MonoTextStyle::new(&FONT_10X20, Rgb565::BLACK); let rect_style = PrimitiveStyleBuilder::new() .fill_color(Rgb565::WHITE) .build(); let mut y = 28; for msg in storage.messages(&self.callsign, self.ssid) { let bounds = Rectangle::new( Point::new(4, y), Size::new(MESSAGE_BOX_WIDTH, MESSAGE_BOX_HEIGHT), ); let mut text_box = TextBox::with_textbox_style(msg.content(), bounds, character_style, textbox_style); let mut actual_bounds = calculate_bounding_box(&text_box); if msg.direction() == MessageDirection::To { let x = i32::try_from(WIDTH).unwrap() - i32::try_from(actual_bounds.size.width).unwrap() - 4; text_box.bounds.top_left.x = x; actual_bounds.top_left.x = x; } actual_bounds.into_styled(rect_style).draw(dt)?; text_box.draw(dt)?; y += i32::try_from(actual_bounds.size.height).unwrap() + 4; } self.textbox.render(dt)?; Ok(()) } pub fn key_push(&mut self, k: KeyCode, storage: &mut Storage) { if k == KeyCode::Enter { let text = self.textbox.clear(); let message = Message::new(self.callsign.clone(), self.ssid, text, MessageDirection::To); storage.push_message(message); } else { self.textbox.key_push(k); } } } fn calculate_bounding_box<S: TextRenderer>(tb: &TextBox<'_, S>) -> Rectangle { const CHAR_WIDTH: u32 = 10; const CHAR_HEIGHT: u32 = 20; let mut x = 0; let mut max_x = 0; let mut y = 1; for c in tb.text.chars() { if c == '\n' || CHAR_WIDTH * x >= tb.bounds.size.width { x = 0; y += 1; } else { x += 1; if x > max_x { max_x = x; } } } Rectangle::new( tb.bounds.top_left, Size::new(max_x * CHAR_WIDTH, y * CHAR_HEIGHT), ) }