stevenkhan's picture
Upload spectral-core/src/grid.rs
41a96ee verified
use crate::cell::Cell;
/// A line of terminal cells.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Line {
pub cells: Vec<Cell>,
pub dirty: bool,
pub wrapped: bool,
}
impl Line {
pub fn new(cols: usize) -> Self {
Self {
cells: vec![Cell::default(); cols],
dirty: false,
wrapped: false,
}
}
pub fn clear(&mut self) {
for cell in &mut self.cells {
cell.reset();
}
self.dirty = true;
self.wrapped = false;
}
pub fn resize(&mut self, cols: usize) {
if self.cells.len() < cols {
self.cells.resize(cols, Cell::default());
} else if self.cells.len() > cols {
self.cells.truncate(cols);
}
}
}
/// Terminal grid with scrollback buffer.
pub struct Grid {
pub lines: Vec<Line>,
pub scrollback: Vec<Line>,
pub cols: usize,
pub rows: usize,
pub scrollback_limit: usize,
pub cursor_row: usize,
pub cursor_col: usize,
pub saved_cursor_row: usize,
pub saved_cursor_col: usize,
pub top_margin: usize,
pub bottom_margin: usize,
}
impl Grid {
pub fn new(cols: usize, rows: usize, scrollback_limit: usize) -> Self {
Self {
lines: (0..rows).map(|_| Line::new(cols)).collect(),
scrollback: Vec::new(),
cols,
rows,
scrollback_limit,
cursor_row: 0,
cursor_col: 0,
saved_cursor_row: 0,
saved_cursor_col: 0,
top_margin: 0,
bottom_margin: rows.saturating_sub(1),
}
}
pub fn resize(&mut self, cols: usize, rows: usize) {
let old_rows = self.lines.len();
for line in &mut self.lines {
line.resize(cols);
}
if rows > old_rows {
self.lines.reserve(rows - old_rows);
for _ in old_rows..rows {
self.lines.push(Line::new(cols));
}
} else if rows < old_rows {
let excess = old_rows - rows;
let limit = self.scrollback_limit;
for line in self.lines.drain(0..excess) {
if self.scrollback.len() >= limit {
self.scrollback.remove(0);
}
self.scrollback.push(line);
}
}
self.cols = cols;
self.rows = rows;
self.cursor_row = self.cursor_row.min(rows.saturating_sub(1));
self.cursor_col = self.cursor_col.min(cols.saturating_sub(1));
self.top_margin = 0;
self.bottom_margin = rows.saturating_sub(1);
}
pub fn clear_screen(&mut self) {
for line in &mut self.lines {
line.clear();
}
}
pub fn clear_line(&mut self, row: usize) {
if let Some(line) = self.lines.get_mut(row) {
line.clear();
}
}
pub fn clear_line_right(&mut self, row: usize, col: usize) {
if let Some(line) = self.lines.get_mut(row) {
for cell in &mut line.cells[col..] {
cell.reset();
}
line.dirty = true;
}
}
pub fn clear_line_left(&mut self, row: usize, col: usize) {
if let Some(line) = self.lines.get_mut(row) {
for cell in &mut line.cells[..=col] {
cell.reset();
}
line.dirty = true;
}
}
pub fn scroll_up(&mut self, n: usize) {
let top = self.top_margin;
let bottom = self.bottom_margin;
let n = n.min(bottom.saturating_sub(top) + 1);
for _ in 0..n {
let removed = self.lines.remove(top);
if top == 0 && self.scrollback.len() < self.scrollback_limit {
self.scrollback.push(removed);
}
let mut new_line = Line::new(self.cols);
new_line.dirty = true;
self.lines.insert(bottom, new_line);
}
for i in top..=bottom {
if let Some(line) = self.lines.get_mut(i) {
line.dirty = true;
}
}
}
pub fn scroll_down(&mut self, n: usize) {
let top = self.top_margin;
let bottom = self.bottom_margin;
let n = n.min(bottom.saturating_sub(top) + 1);
for _ in 0..n {
self.lines.remove(bottom);
let mut new_line = Line::new(self.cols);
new_line.dirty = true;
self.lines.insert(top, new_line);
}
for i in top..=bottom {
if let Some(line) = self.lines.get_mut(i) {
line.dirty = true;
}
}
}
pub fn line(&self, row: usize) -> Option<&Line> {
self.lines.get(row)
}
pub fn line_mut(&mut self, row: usize) -> Option<&mut Line> {
self.lines.get_mut(row)
}
pub fn cell(&self, row: usize, col: usize) -> Option<&Cell> {
self.lines.get(row)?.cells.get(col)
}
pub fn cell_mut(&mut self, row: usize, col: usize) -> Option<&mut Cell> {
self.lines.get_mut(row)?.cells.get_mut(col)
}
}