use crate::style::Style;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cell {
pub ch: char,
pub style: Style,
}
impl Default for Cell {
fn default() -> Self {
Cell {
ch: ' ',
style: Style::default(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Screen {
rows: usize,
cols: usize,
cells: Vec<Cell>,
pub cursor: (usize, usize),
}
impl Screen {
pub fn new(rows: usize, cols: usize) -> Self {
Screen {
rows,
cols,
cells: vec![Cell::default(); rows * cols],
cursor: (0, 0),
}
}
pub fn rows(&self) -> usize {
self.rows
}
pub fn cols(&self) -> usize {
self.cols
}
pub fn cell(&self, row: usize, col: usize) -> Cell {
if row < self.rows && col < self.cols {
self.cells[row * self.cols + col]
} else {
Cell::default()
}
}
pub fn set(&mut self, row: usize, col: usize, cell: Cell) {
if row < self.rows && col < self.cols {
self.cells[row * self.cols + col] = cell;
}
}
pub fn write_str(&mut self, row: usize, col: usize, text: &str, style: Style) {
for (i, ch) in text.chars().enumerate() {
self.set(row, col + i, Cell { ch, style });
}
}
pub fn diff(&self, next: &Screen) -> Vec<u8> {
if self.rows != next.rows || self.cols != next.cols {
return next.render_full();
}
if self == next {
return Vec::new();
}
let mut out = String::new();
emit_changes(Some(self), next, &mut out);
out.into_bytes()
}
pub fn render_full(&self) -> Vec<u8> {
let mut out = String::from("\x1b[2J");
emit_changes(None, self, &mut out);
out.into_bytes()
}
}
fn emit_changes(prev: Option<&Screen>, next: &Screen, out: &mut String) {
use std::fmt::Write;
let mut style = Style::default();
let mut at: Option<(usize, usize)> = None;
for row in 0..next.rows {
for col in 0..next.cols {
let target = next.cell(row, col);
let same = match prev {
Some(p) => p.cell(row, col) == target,
None => target == Cell::default(),
};
if same {
continue;
}
if at != Some((row, col)) {
write!(out, "\x1b[{};{}H", row + 1, col + 1).unwrap();
}
out.push_str(&style.transition_to(&target.style));
style = target.style;
out.push(target.ch);
at = if col + 1 < next.cols {
Some((row, col + 1))
} else {
None
};
}
}
out.push_str(&style.transition_to(&Style::default()));
let (r, c) = next.cursor;
write!(out, "\x1b[{};{}H", r + 1, c + 1).unwrap();
}