use crate::prelude::*;
use crate::ui::canvas::frame::cell::Cell;
use geo::point;
use std::ops::Range;
#[derive(Debug, Clone)]
pub struct Iframe {
size: U16Size,
cells: Vec<Cell>,
dirty_rows: Vec<bool>,
}
impl Iframe {
pub fn new(size: U16Size) -> Self {
let n = size.height() as usize * size.width() as usize;
Iframe {
size,
cells: vec![Cell::default(); n],
dirty_rows: vec![false; size.height() as usize], }
}
pub fn pos2range(&self, pos: U16Pos, n: usize) -> Range<usize> {
let start_idx = self.pos2idx(pos);
let end_idx = start_idx + n;
start_idx..end_idx
}
pub fn idx2range(&self, index: usize, n: usize) -> Range<usize> {
let end_idx = index + n;
index..end_idx
}
pub fn xy2idx(&self, x: usize, y: usize) -> usize {
y * self.size.width() as usize + x
}
pub fn pos2idx(&self, pos: U16Pos) -> usize {
self.xy2idx(pos.x() as usize, pos.y() as usize)
}
pub fn idx2xy(&self, index: usize) -> (usize, usize) {
debug_assert!(index <= self.cells.len());
let x = index % self.size.width() as usize;
let y = index / self.size.width() as usize;
(x, y)
}
pub fn idx2pos(&self, index: usize) -> U16Pos {
let (x, y) = self.idx2xy(index);
point!(x: x as u16, y: y as u16)
}
pub fn size(&self) -> U16Size {
self.size
}
pub fn zero_sized(&self) -> bool {
self.size.height() == 0 || self.size.width() == 0
}
pub fn set_size(&mut self, size: U16Size) -> U16Size {
let old_size = self.size;
self.size = size;
self.cells.resize(
size.height() as usize * size.width() as usize,
Cell::default(),
);
self.dirty_rows = vec![true; size.height() as usize];
old_size
}
pub fn contains_index(&self, index: usize) -> bool {
index < self.cells.len()
}
pub fn contains_range(&self, range: &Range<usize>) -> bool {
range.start < self.cells.len() && range.end <= self.cells.len()
}
pub fn get_cell(&self, pos: U16Pos) -> &Cell {
self.try_get_cell(pos).unwrap()
}
pub fn try_get_cell(&self, pos: U16Pos) -> Option<&Cell> {
let index = self.pos2idx(pos);
if self.contains_index(index) {
let result = &self.cells[index];
Some(result)
} else {
None
}
}
pub fn set_cell(&mut self, pos: U16Pos, cell: Cell) -> Cell {
self.try_set_cell(pos, cell).unwrap()
}
pub fn try_set_cell(&mut self, pos: U16Pos, cell: Cell) -> Option<Cell> {
let index = self.pos2idx(pos);
if self.contains_index(index) {
let old_cell = self.cells[index].clone();
self.cells[index] = cell;
self.dirty_rows[pos.y() as usize] = true;
Some(old_cell)
} else {
None
}
}
pub fn set_empty_cell(&mut self, pos: U16Pos) -> Cell {
self.set_cell(pos, Cell::empty())
}
pub fn try_set_empty_cell(&mut self, pos: U16Pos) -> Option<Cell> {
self.try_set_cell(pos, Cell::empty())
}
pub fn get_cells(&self) -> &Vec<Cell> {
&self.cells
}
pub fn get_cells_at(&self, pos: U16Pos, n: usize) -> &[Cell] {
self.try_get_cells_at(pos, n).unwrap()
}
pub fn try_get_cells_at(&self, pos: U16Pos, n: usize) -> Option<&[Cell]> {
let range = self.pos2range(pos, n);
if self.contains_range(&range) {
Some(&self.cells[range])
} else {
None
}
}
pub fn set_cells_at(&mut self, pos: U16Pos, cells: Vec<Cell>) -> Vec<Cell> {
self.try_set_cells_at(pos, cells).unwrap()
}
pub fn try_set_cells_at(
&mut self,
pos: U16Pos,
cells: Vec<Cell>,
) -> Option<Vec<Cell>> {
let range = self.pos2range(pos, cells.len());
if self.contains_range(&range) {
let end_at = self.idx2pos(range.end);
for row in pos.y()..(end_at.y() + 1) {
if (row as usize) < self.dirty_rows.len() {
self.dirty_rows[row as usize] = true;
}
}
Some(self.cells.splice(range, cells).collect())
} else {
None
}
}
pub fn set_empty_cells_at(&mut self, pos: U16Pos, n: usize) -> Vec<Cell> {
self.set_cells_at(pos, vec![Cell::empty(); n])
}
pub fn try_set_empty_cells_at(
&mut self,
pos: U16Pos,
n: usize,
) -> Option<Vec<Cell>> {
self.try_set_cells_at(pos, vec![Cell::empty(); n])
}
pub fn dirty_rows(&self) -> &Vec<bool> {
&self.dirty_rows
}
pub fn reset_dirty_rows(&mut self) {
self.dirty_rows = vec![false; self.size.height() as usize];
}
}
#[cfg(test)]
use compact_str::CompactString;
impl Iframe {
#[cfg(test)]
pub fn raw_symbols(&self) -> Vec<Vec<CompactString>> {
let mut results: Vec<Vec<CompactString>> = vec![];
for row in 0..self.size.height() {
let mut row_symbols: Vec<CompactString> = vec![];
for col in 0..self.size.width() {
let idx = self.xy2idx(col as usize, row as usize);
row_symbols.push(self.cells[idx].symbol().clone());
}
results.push(row_symbols);
}
results
}
#[cfg(test)]
pub fn raw_symbols_with_placeholder(&self) -> Vec<Vec<CompactString>> {
let mut results: Vec<Vec<CompactString>> = vec![];
for row in 0..self.size.height() {
let mut row_symbols: Vec<CompactString> = vec![];
for col in 0..self.size.width() {
let idx = self.xy2idx(col as usize, row as usize);
let s = self.cells[idx].symbol();
row_symbols.push(if s.is_empty() {
use compact_str::ToCompactString;
" ".to_compact_string()
} else {
s.clone()
});
}
results.push(row_symbols);
}
results
}
}