use crate::preludes::widget_creation::*;
use crate::type_aliases::HashMap;
const FULL_CHAR: char = '█';
const DUAL_CHAR_TOP: char = '▀';
const DUAL_CHAR_BOTTOM: char = '▄';
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PixelGrid {
pub filler: Option<Color>,
pub dual_char_top: char,
pub dual_char_bottom: char,
pub single_char: char,
pub size_info: WidgetSizeInfo,
pub pixel_offset: Position,
pub pixel_size: Size,
pub transparent_style_ground: StyleGround,
pixels: HashMap<Position, Color>,
widget_data: WidgetData,
}
impl PixelGrid {
pub fn new() -> Self {
Self {
filler: None,
dual_char_top: DUAL_CHAR_TOP,
dual_char_bottom: DUAL_CHAR_BOTTOM,
single_char: FULL_CHAR,
size_info: WidgetSizeInfo::Dynamic {
min: None,
max: None,
},
pixel_offset: Position::zero(),
pixel_size: Size::same(1),
transparent_style_ground: StyleGround::Transparent(StylePullLayer::Any),
pixels: HashMap::new(),
widget_data: WidgetData::new(),
}
}
#[inline(always)]
pub fn get_pixel(&self, position: Position) -> Option<Color> {
return match self.pixels.get(&position) {
Some(s) => Some(*s),
None => self.filler,
};
}
#[inline(always)]
pub fn set_pixel(&mut self, position: Position, color: Option<impl Into<Color>>) {
let color: Option<Color> = match color {
Some(s) => Some(s.into()),
None => None,
};
match color {
Some(s) => self.pixels.insert(position, s),
None => self.pixels.remove(&position),
};
}
#[inline(always)]
pub fn clear_all(&mut self) {
self.pixels = HashMap::new(); }
#[inline(always)]
pub fn assemble_pixel_content(&self, top_pixel: Option<Color>, bottom_pixel: Option<Color>) -> Option<Content> {
let content: Option<Content>;
if top_pixel == bottom_pixel {
content = Some(match top_pixel {
Some(s) => Content::Styled(self.single_char, Style::new().fg(Some(s))),
None => Content::Styled(' ', Style::new().bg(self.transparent_style_ground)),
});
} else {
content = Some(match (top_pixel, bottom_pixel) {
(Some(top), Some(bottom)) => Content::Styled(self.dual_char_top, Style::new().fg(Some(top)).bg(Some(bottom))),
(Some(top), None) => Content::Styled(self.dual_char_top, Style::new().fg(Some(top)).bg(self.transparent_style_ground)),
(None, Some(bottom)) => Content::Styled(self.dual_char_bottom, Style::new().fg(Some(bottom)).bg(self.transparent_style_ground)),
_ => unreachable!(),
});
}
return content;
}
}
impl Widget for PixelGrid {
fn draw(&mut self, canvas: &mut Canvas, _state_frame: Option<&EventStateFrame>) {
let scale_col: f64 = self.pixel_size.cols.max(1) as f64;
let scale_row: f64 = self.pixel_size.rows.max(1) as f64;
for row in 0..canvas.transform.size.rows {
for col in 0..canvas.transform.size.cols {
let draw_position = Position::new(col as i16, row as i16);
let mut top_position = Position::new(col as i16, (row as i16) << 1);
let mut bottom_position = top_position + Position::new(0, 1);
top_position = Position::new(
(top_position.col as f64 / scale_col).floor() as i16,
(top_position.row as f64 / scale_row).floor() as i16,
);
bottom_position = Position::new(
(bottom_position.col as f64 / scale_col).floor() as i16,
(bottom_position.row as f64 / scale_row).floor() as i16,
);
top_position = top_position - self.pixel_offset;
bottom_position = bottom_position - self.pixel_offset;
let top_pixel = self.get_pixel(top_position);
let bottom_pixel = self.get_pixel(bottom_position);
let content = self.assemble_pixel_content(top_pixel, bottom_pixel);
canvas.set(draw_position, content);
}
}
}
fn widget_info(&self) -> WidgetInfo {
WidgetInfo {
size_info: self.size_info,
}
}
fn widget_data(&mut self) -> &mut WidgetData {
return &mut self.widget_data;
}
}