use std::convert::TryInto as _; use std::io::Write as _; #[derive(Clone, Debug)] pub struct Row { cells: Vec, wrapped: bool, } impl Row { pub fn new(cols: u16) -> Self { Self { cells: vec![crate::cell::Cell::default(); cols as usize], wrapped: false, } } fn cols(&self) -> u16 { self.cells.len().try_into().unwrap() } pub fn clear(&mut self, bgcolor: crate::attrs::Color) { for cell in &mut self.cells { cell.clear(bgcolor); } self.wrapped = false; } fn cells(&self) -> impl Iterator { self.cells.iter() } pub fn cells_mut( &mut self, ) -> impl Iterator { self.cells.iter_mut() } pub fn get(&self, col: u16) -> Option<&crate::cell::Cell> { self.cells.get(col as usize) } pub fn get_mut(&mut self, col: u16) -> Option<&mut crate::cell::Cell> { self.cells.get_mut(col as usize) } pub fn insert(&mut self, i: usize, cell: crate::cell::Cell) { self.cells.insert(i, cell); } pub fn remove(&mut self, i: usize) { self.cells.remove(i); } pub fn truncate(&mut self, len: usize) { self.cells.truncate(len); } pub fn resize(&mut self, len: usize, cell: crate::cell::Cell) { self.cells.resize(len, cell); } pub fn wrap(&mut self, wrap: bool) { self.wrapped = wrap; } pub fn wrapped(&self) -> bool { self.wrapped } pub fn write_contents( &self, contents: &mut String, start: u16, width: u16, ) { let mut prev_was_wide = false; for cell in self .cells() .skip(start as usize) .take(width.min(self.content_width(start, false)) as usize) { if prev_was_wide { prev_was_wide = false; continue; } if cell.has_contents() { // using write! here is significantly slower, for some reason // write!(contents, "{}", cell.contents()).unwrap(); contents.push_str(&cell.contents()); } else { contents.push(' '); } prev_was_wide = cell.is_wide(); } } pub fn write_contents_formatted( &self, contents: &mut Vec, start: u16, width: u16, row: u16, wrapping: bool, mut prev_pos: crate::grid::Pos, mut prev_attrs: crate::attrs::Attrs, ) -> (crate::grid::Pos, crate::attrs::Attrs) { let mut prev_was_wide = false; for (col, cell) in self .cells() .enumerate() .skip(start as usize) .take(width.min(self.content_width(start, true)) as usize) { if prev_was_wide { prev_was_wide = false; continue; } prev_was_wide = cell.is_wide(); let pos = crate::grid::Pos { row, col: col.try_into().unwrap(), }; if cell.has_contents() || cell.bgcolor() != crate::attrs::Color::Default { if pos != prev_pos { if pos.row == prev_pos.row + 1 { if !wrapping || prev_pos.col != self.cols() || pos.col != 0 { write!( contents, "{}{}", crate::term::CRLF::default(), crate::term::MoveRight::new(pos.col) ) .unwrap(); } } else if prev_pos.row == pos.row { write!( contents, "{}", crate::term::MoveRight::new( pos.col - prev_pos.col ) ) .unwrap(); } else { write!(contents, "{}", crate::term::MoveTo::new(pos)) .unwrap(); } prev_pos = pos; } let attrs = cell.attrs(); if cell.has_contents() { if &prev_attrs != attrs { attrs.write_escape_code_diff(contents, &prev_attrs); prev_attrs = *attrs; } // using write! here is significantly slower, for some // reason // write!(contents, "{}", cell.contents()).unwrap(); contents.extend(cell.contents().as_bytes()); prev_pos.col += if cell.is_wide() { 2 } else { 1 }; } else { if prev_attrs.bgcolor != attrs.bgcolor { attrs.write_escape_code_diff(contents, &prev_attrs); prev_attrs = *attrs; } write!( contents, "{}", crate::term::EraseChar::default(), ) .unwrap(); } } } (prev_pos, prev_attrs) } pub fn write_contents_diff( &self, contents: &mut Vec, prev: &Self, start: u16, width: u16, row: u16, wrapping: bool, mut prev_pos: crate::grid::Pos, mut prev_attrs: crate::attrs::Attrs, ) -> (crate::grid::Pos, crate::attrs::Attrs) { let mut prev_was_wide = false; for (col, (cell, prev_cell)) in self .cells() .zip(prev.cells()) .enumerate() .skip(start as usize) .take(width as usize) { if prev_was_wide { prev_was_wide = false; continue; } prev_was_wide = cell.is_wide(); let pos = crate::grid::Pos { row, col: col.try_into().unwrap(), }; if cell != prev_cell { if pos != prev_pos { if pos.row == prev_pos.row + 1 { if !wrapping || prev_pos.col != self.cols() || pos.col != 0 { write!( contents, "{}{}", crate::term::CRLF::default(), crate::term::MoveRight::new(pos.col) ) .unwrap(); } } else if prev_pos.row == pos.row && prev_pos.col < pos.col { write!( contents, "{}", crate::term::MoveRight::new( pos.col - prev_pos.col ) ) .unwrap(); } else { write!(contents, "{}", crate::term::MoveTo::new(pos)) .unwrap(); } prev_pos = pos; } let attrs = cell.attrs(); if cell.has_contents() { if &prev_attrs != attrs { attrs.write_escape_code_diff(contents, &prev_attrs); prev_attrs = *attrs; } // using write! here is significantly slower, for some // reason // write!(contents, "{}", cell.contents()).unwrap(); contents.extend(cell.contents().as_bytes()); prev_pos.col += if cell.is_wide() { 2 } else { 1 }; } else { if prev_attrs.bgcolor != attrs.bgcolor { attrs.write_escape_code_diff(contents, &prev_attrs); prev_attrs = *attrs; } write!( contents, "{}", crate::term::EraseChar::default(), ) .unwrap(); } } } (prev_pos, prev_attrs) } fn content_width(&self, start: u16, formatting: bool) -> u16 { for (col, cell) in self.cells.iter().skip(start as usize).enumerate().rev() { if cell.has_contents() || (formatting && cell.bgcolor() != crate::attrs::Color::Default) { let width: u16 = col.try_into().unwrap(); return width + 1; } } 0 } }