use unicode_width::UnicodeWidthStr;
use super::Cell;
use super::sgr::SgrState;
mod parse;
#[cfg(test)]
mod tests;
pub struct VirtualScreen {
width: u16,
height: u16,
cells: Vec<Cell>,
cursor: (u16, u16),
sgr: SgrState,
cursor_visible: bool,
}
impl VirtualScreen {
pub fn new(width: u16, height: u16) -> Self {
let len = width as usize * height as usize;
Self {
width,
height,
cells: vec![Cell::EMPTY; len],
cursor: (0, 0),
sgr: SgrState::default(),
cursor_visible: true,
}
}
pub fn width(&self) -> u16 {
self.width
}
pub fn height(&self) -> u16 {
self.height
}
pub fn cursor(&self) -> (u16, u16) {
self.cursor
}
pub fn cursor_visible(&self) -> bool {
self.cursor_visible
}
pub fn sgr(&self) -> SgrState {
self.sgr
}
pub fn cell(&self, x: u16, y: u16) -> Option<&Cell> {
if x >= self.width || y >= self.height {
return None;
}
let i = y as usize * self.width as usize + x as usize;
self.cells.get(i)
}
pub fn row(&self, y: u16) -> String {
if y >= self.height {
return String::new();
}
let mut out = String::new();
for x in 0..self.width {
let c = self.cell(x, y).expect("in-bounds");
if c.is_spacer() {
continue; }
out.push_str(c.symbol());
}
out
}
pub fn rows(&self) -> Vec<String> {
(0..self.height).map(|y| self.row(y)).collect()
}
pub fn resize(&mut self, width: u16, height: u16) {
let mut new = vec![Cell::EMPTY; width as usize * height as usize];
let common_w = self.width.min(width);
let common_h = self.height.min(height);
for y in 0..common_h {
for x in 0..common_w {
let old_i = y as usize * self.width as usize + x as usize;
let new_i = y as usize * width as usize + x as usize;
new[new_i] = self.cells[old_i].clone();
}
}
self.width = width;
self.height = height;
self.cells = new;
self.cursor.0 = self.cursor.0.min(width.saturating_sub(1));
self.cursor.1 = self.cursor.1.min(height.saturating_sub(1));
}
fn write_grapheme(&mut self, g: &str) {
if g.is_empty() {
return;
}
if g == "\n" {
self.cursor.1 = self
.cursor
.1
.saturating_add(1)
.min(self.height.saturating_sub(1));
self.cursor.0 = 0;
return;
}
if g == "\r" {
self.cursor.0 = 0;
return;
}
if g.as_bytes().iter().all(|b| b.is_ascii_control()) {
return;
}
let width = UnicodeWidthStr::width(g).max(1) as u16;
let (x, y) = self.cursor;
if y >= self.height || x >= self.width {
return;
}
let mut cell = Cell::new(g);
cell.fg = self.sgr.fg;
cell.bg = self.sgr.bg;
cell.modifier = self.sgr.modifier;
let i = y as usize * self.width as usize + x as usize;
self.cells[i] = cell;
if width == 2 && x + 1 < self.width {
let spacer_i = y as usize * self.width as usize + (x + 1) as usize;
let mut spacer = Cell::EMPTY;
spacer.fg = self.sgr.fg;
spacer.bg = self.sgr.bg;
spacer.modifier = self.sgr.modifier;
spacer.set_spacer();
self.cells[spacer_i] = spacer;
}
self.cursor.0 = (x + width).min(self.width);
}
}