use crate::utils as crate_utils;
use std::{
fmt::{self, Display, Formatter},
io::{self, Write},
};
mod pixel;
mod scale_to_fit;
pub mod utils;
mod view_element;
mod wrapping;
#[allow(deprecated)]
pub use pixel::{
colchar::{ColChar, Colour, Modifier},
vec2d::Vec2D,
Pixel, Point,
};
pub use scale_to_fit::ScaleFitView;
pub use view_element::ViewElement;
pub use wrapping::Wrapping;
#[derive(Debug, Clone)]
pub struct View {
pub width: usize,
pub height: usize,
pub background_char: ColChar,
pub coord_numbers_in_render: bool,
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,
coord_numbers_in_render: false,
block_until_resized: false,
pixels: Vec::with_capacity(width * height),
};
view.clear();
view
}
#[must_use]
pub const fn with_coord_numbers(mut self, coord_numbers_in_render: bool) -> Self {
self.coord_numbers_in_render = coord_numbers_in_render;
self
}
#[must_use]
pub const fn with_block_until_resized(mut self, block_until_resized: bool) -> Self {
self.block_until_resized = block_until_resized;
self
}
#[must_use]
pub const fn size(&self) -> Vec2D {
Vec2D::new(self.width as isize, self.height as isize)
}
#[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];
}
pub fn plot(&mut self, pos: Vec2D, c: ColChar, wrapping: Wrapping) {
if let Some(wrapped_pos) = wrapping.handle_bounds(pos, self.size()) {
let i = self.width * wrapped_pos.y.unsigned_abs() + wrapped_pos.x.unsigned_abs();
self.pixels[i] = c;
}
}
pub fn blit(&mut self, element: &impl ViewElement, wrapping: Wrapping) {
for pixel in element.active_pixels() {
self.plot(pixel.pos, pixel.fill_char, wrapping);
}
}
pub fn blit_double_width(&mut self, element: &impl ViewElement, wrapping: Wrapping) {
for pixel in element.active_pixels() {
let pos = pixel.pos * Vec2D::new(2, 1);
self.plot(pos, pixel.fill_char, wrapping);
self.plot(pos + Vec2D::new(1, 0), pixel.fill_char, wrapping);
}
}
pub fn display_render(&self) -> io::Result<()> {
let mut stdout = io::stdout().lock();
if self.block_until_resized {
let view_size = self.size();
crate_utils::block_until_resized(view_size);
}
write!(stdout, "{self}")
}
}
impl Display for View {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
crate::utils::prepare_terminal(f).map_err(|_| fmt::Error)?;
f.write_str("\x1b[H\x1b[J")?;
if self.coord_numbers_in_render {
let nums: String = (0..self.width)
.map(|i| i.to_string().chars().last().unwrap_or(' '))
.collect();
writeln!(f, " {nums}")?;
}
for y in 0..self.height {
if self.coord_numbers_in_render {
let num = y.to_string().chars().last().unwrap_or(' ');
write!(f, "{num}")?;
}
let row = &self.pixels[self.width * y..self.width * (y + 1)];
row[0].display_with_prev_and_next(f, None, Some(row[1].modifier))?;
for x in 1..(row.len() - 1) {
row[x].display_with_prev_and_next(
f,
Some(row[x - 1].modifier),
Some(row[x + 1].modifier),
)?;
}
row[row.len() - 1].display_with_prev_and_next(
f,
Some(row[row.len() - 2].modifier),
None,
)?;
f.write_str("\r\n")?;
}
f.write_str("\x1b[J")?;
Ok(())
}
}