chiro 0.1.3

A curses-style terminal view that runs inside a pixel buffer (Rewrite of chiropterm)
Documentation
use std::collections::{HashMap, hash_map::Entry};

use crate::{Drawable, shared::{At, Zel, ToZel, build_rect}, Font};

use super::{charset::box_char, BoxSide};

pub struct BoxArt {
    font: Font,
    content: HashMap<Zel, u8>,
}

impl BoxArt {
    pub fn new(font: Font) -> BoxArt {
        BoxArt {
            font,
            content: HashMap::new(),
        }
    }

    pub fn draw<'d>(&self, cursor: At<'d, impl Drawable+'d>) {
        let sz = self.font.char_size();
        let sz_x = sz.width as i32;
        let sz_y = sz.height as i32;

        for (xy, value) in self.content.iter() {
            if let Some(bc) = box_char(*value) {
                cursor.font(self.font).shifted((xy.x * sz_x, xy.y * sz_y)).put(bc);
            }
        }
    }

    pub fn add_box(&mut self, top: impl ToZel, bot: impl ToZel, double_border: bool) {
        let mut top = top.to_zel();
        let mut bot = bot.to_zel();
        let zel_dims = self.font.char_size();
        top.x /= zel_dims.width as i32;
        top.y /= zel_dims.height as i32;
        bot.x /= zel_dims.width as i32;
        bot.y /= zel_dims.height as i32;

        let rect = build_rect(top, bot);

        if rect.size.width <= 1 {
            for y in rect.min_y()..rect.max_y() - 1 {
                self.add((rect.min_x(), y), BoxSide::Bottom, double_border)
            }

            for y in rect.min_y() + 1..rect.max_y() {
                self.add((rect.min_x(), y), BoxSide::Top, double_border)
            }
            return;
        }

        if rect.size.height <= 1 {
            for x in rect.min_x()..rect.max_x() - 1 {
                self.add((x, rect.min_y()), BoxSide::Right, double_border)
            }
            for x in rect.min_x() + 1..rect.max_x() {
                self.add((x, rect.min_y()), BoxSide::Left, double_border)
            }

            return;
        }

        for x in rect.min_x()..rect.max_x() - 1 {
            self.add((x, rect.min_y()), BoxSide::Right, double_border);
            self.add((x, rect.max_y() - 1), BoxSide::Right, double_border);
        }

        for x in rect.min_x() + 1..rect.max_x() {
            self.add((x, rect.min_y()), BoxSide::Left, double_border);
            self.add((x, rect.max_y() - 1), BoxSide::Left, double_border);
        }

        for y in rect.min_y()..rect.max_y() - 1 {
            self.add((rect.min_x(), y), BoxSide::Bottom, double_border);
            self.add((rect.max_x() - 1, y), BoxSide::Bottom, double_border);
        }

        for y in rect.min_y() + 1..rect.max_y() {
            self.add((rect.min_x(), y), BoxSide::Top, double_border);
            self.add((rect.max_x() - 1, y), BoxSide::Top, double_border);
        }
    }

    fn add(&mut self, at: (i32, i32), side: BoxSide, double_border: bool) {
        let norm_side = 3 - side as u8;

        let new = 1 << (2 * norm_side + if double_border { 1 } else { 0 });
        match self.content.entry(at.to_zel()) {
            Entry::Occupied(mut o) => { *o.get_mut() |= new; }
            Entry::Vacant(v) => { v.insert(new); }
        };
    }
}