use crate::core::{CanDraw, Canvas, ColChar, Vec2D};
use std::{
fmt::{self, Display, Formatter},
io::{self, Write},
};
mod scale_to_fit;
mod term_utils;
mod wrapping;
pub use scale_to_fit::ScaleFitView;
pub use wrapping::WrappingMode;
#[derive(Debug, Clone)]
pub struct View {
pub width: usize,
pub height: usize,
pub background_char: ColChar,
pub wrapping_mode: WrappingMode,
pub block_until_resized: bool,
pixels: Vec<ColChar>,
}
impl View {
#[must_use]
pub fn new(width: usize, height: usize, background_char: ColChar) -> Self {
let mut view = Self {
width,
height,
background_char,
wrapping_mode: WrappingMode::Ignore,
block_until_resized: false,
pixels: Vec::with_capacity(width * height),
};
view.clear();
view
}
#[must_use]
pub const fn with_wrapping_mode(mut self, wrapping_mode: WrappingMode) -> Self {
self.wrapping_mode = wrapping_mode;
self
}
#[must_use]
pub const fn with_block_until_resized(mut self) -> Self {
self.block_until_resized = true;
self
}
#[must_use]
pub const fn size(&self) -> Vec2D {
Vec2D::new(self.width as i64, self.height as i64)
}
#[must_use]
pub fn center(&self) -> Vec2D {
self.size() / 2
}
pub fn clear(&mut self) {
self.pixels = vec![self.background_char; self.width * self.height];
}
#[inline]
pub fn draw(&mut self, element: &impl CanDraw) {
element.draw_to(self);
}
pub fn draw_double_width(&mut self, element: &impl CanDraw) {
struct DoubleWidthView<'v>(&'v mut View);
impl Canvas for DoubleWidthView<'_> {
fn plot(&mut self, pos: Vec2D, c: ColChar) {
let pos = pos * Vec2D::new(2, 1);
self.0.plot(pos, c);
self.0.plot(pos + Vec2D::new(1, 0), c);
}
}
element.draw_to(&mut DoubleWidthView(self));
}
pub fn display_render(&self) -> io::Result<()> {
let mut stdout = io::stdout().lock();
if self.block_until_resized {
let view_size = self.size();
term_utils::block_until_resized(view_size);
}
write!(stdout, "{self}")
}
}
impl Canvas for View {
fn plot(&mut self, pos: Vec2D, c: ColChar) {
if let Some(wrapped_pos) = self.wrapping_mode.handle_bounds(pos, self.size()) {
let i = self.width * wrapped_pos.y.unsigned_abs() as usize
+ wrapped_pos.x.unsigned_abs() as usize;
self.pixels[i] = c;
}
}
}
impl Display for View {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
term_utils::prepare_terminal(f).map_err(|_| fmt::Error)?;
f.write_str("\x1b[H\x1b[J")?;
for y in 0..self.height {
let row = &self.pixels[self.width * y..self.width * (y + 1)];
for x in 0..row.len() {
row[x].display_with_prev_and_next(
f,
row.get(x.wrapping_sub(1)).map(|c| c.modifier),
row.get(x + 1).map(|c| c.modifier),
)?;
}
f.write_str("\r\n")?;
}
f.write_str("\x1b[J")?;
Ok(())
}
}