Skip to content
Snippets Groups Projects
chat.rs 3.34 KiB
Newer Older
use embedded_graphics::{
Stephen D's avatar
Stephen D committed
    geometry::{Point, Size},
Stephen D's avatar
Stephen D committed
    mono_font::{ascii::FONT_10X20, MonoTextStyle},
    pixelcolor::{Rgb565, RgbColor},
    primitives::{Primitive, PrimitiveStyleBuilder, Rectangle},
Stephen D's avatar
Stephen D committed
    text::renderer::TextRenderer,
    Drawable,
};
Stephen D's avatar
Stephen D committed
use embedded_text::{style::TextBoxStyleBuilder, TextBox};
Stephen D's avatar
Stephen D committed
use heapless::String;
Stephen D's avatar
Stephen D committed
use crate::{
    app::WIDTH,
Stephen D's avatar
Stephen D committed
    keyboard::KeyCode,
    storage::{Message, MessageDirection, Storage, MAX_CONTACT_CALLSIGN_LENGTH},
Stephen D's avatar
Stephen D committed
use super::{textbox::TextBox as MyTextBox, Element};

const MESSAGE_BOX_WIDTH: u32 = 200;
const MESSAGE_BOX_HEIGHT: u32 = 64;
Stephen D's avatar
Stephen D committed

pub struct Chat {
Stephen D's avatar
Stephen D committed
    callsign: String<MAX_CONTACT_CALLSIGN_LENGTH>,
    ssid: u8,
Stephen D's avatar
Stephen D committed
    textbox: MyTextBox,
Stephen D's avatar
Stephen D committed
}

impl Chat {
Stephen D's avatar
Stephen D committed
    pub fn new(callsign: String<MAX_CONTACT_CALLSIGN_LENGTH>, ssid: u8) -> Self {
Stephen D's avatar
Stephen D committed
        Self {
Stephen D's avatar
Stephen D committed
            callsign,
            ssid,
Stephen D's avatar
Stephen D committed
            textbox: MyTextBox::new(1, 218, 31, 1),
Stephen D's avatar
Stephen D committed
        }
    }
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
    pub fn render<
Stephen D's avatar
Stephen D committed
        E,
        DT: embedded_graphics::prelude::DrawTarget<
            Color = embedded_graphics::pixelcolor::Rgb565,
            Error = E,
        >,
    >(
        &mut self,
        dt: &mut DT,
Stephen D's avatar
Stephen D committed
        storage: &Storage,
Stephen D's avatar
Stephen D committed
    ) -> Result<(), E> {
Stephen D's avatar
Stephen D committed
        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();

Stephen D's avatar
Stephen D committed
        let mut y = 28;
Stephen D's avatar
Stephen D committed
        for msg in storage.messages(&self.callsign, self.ssid) {
Stephen D's avatar
Stephen D committed
            let bounds = Rectangle::new(
                Point::new(4, y),
                Size::new(MESSAGE_BOX_WIDTH, MESSAGE_BOX_HEIGHT),
            );

            let mut text_box =
Stephen D's avatar
Stephen D committed
                TextBox::with_textbox_style(msg.content(), bounds, character_style, textbox_style);
Stephen D's avatar
Stephen D committed

            let mut actual_bounds = calculate_bounding_box(&text_box);
Stephen D's avatar
Stephen D committed
            if msg.direction() == MessageDirection::To {
Stephen D's avatar
Stephen D committed
                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;
Stephen D's avatar
Stephen D committed
        self.textbox.render(dt)?;

Stephen D's avatar
Stephen D committed
    }

Stephen D's avatar
Stephen D committed
    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);
        }
Stephen D's avatar
Stephen D committed
    }
Stephen D's avatar
Stephen D committed
}
Stephen D's avatar
Stephen D committed
fn calculate_bounding_box<S: TextRenderer>(tb: &TextBox<'_, S>) -> Rectangle {
Stephen D's avatar
Stephen D committed
    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 {
Stephen D's avatar
Stephen D committed
            x = 0;
            y += 1;
        } else {
            x += 1;
Stephen D's avatar
Stephen D committed

            if x > max_x {
                max_x = x;
            }
Stephen D's avatar
Stephen D committed
    }
Stephen D's avatar
Stephen D committed

    Rectangle::new(
        tb.bounds.top_left,
        Size::new(max_x * CHAR_WIDTH, y * CHAR_HEIGHT),
    )
Stephen D's avatar
Stephen D committed
}