use crate::prelude::*;
use crate::ui::canvas::frame::cell::Cell;
use roaring::RoaringBitmap;
use std::ops::Range;
#[cfg(test)]
use compact_str::CompactString;
#[cfg(test)]
use compact_str::ToCompactString;
#[derive(Debug, Clone)]
pub struct Iframe {
size: U16Size,
cells: Vec<Cell>,
dirty_marks: RoaringBitmap,
}
impl Iframe {
pub fn new(size: U16Size) -> Self {
let n = size.height() as usize * size.width() as usize;
Iframe {
size,
cells: vec![Cell::empty(); n],
dirty_marks: RoaringBitmap::new(),
}
}
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 as u16, y as u16)
}
pub fn size(&self) -> U16Size {
self.size
}
pub fn is_zero_sized(&self) -> bool {
self.size.is_zero()
}
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::empty(),
);
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_marks.insert(index as u32);
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: &[Cell]) {
self.try_set_cells_at(pos, cells).unwrap();
}
pub fn set_n_cells_at(&mut self, pos: U16Pos, cell: Cell, n: usize) {
self.try_set_n_cells_at(pos, cell, n).unwrap();
}
pub fn try_set_cells_at(
&mut self,
pos: U16Pos,
cells: &[Cell],
) -> Option<()> {
let range = self.pos2range(pos, cells.len());
if self.contains_range(&range) {
self
.dirty_marks
.insert_range((range.start as u32)..(range.end as u32));
self.cells[range].clone_from_slice(cells);
Some(())
} else {
None
}
}
pub fn try_set_n_cells_at(
&mut self,
pos: U16Pos,
cell: Cell,
n: usize,
) -> Option<()> {
let range = self.pos2range(pos, n);
if self.contains_range(&range) {
self
.dirty_marks
.insert_range((range.start as u32)..(range.end as u32));
self.cells[range].fill(cell);
Some(())
} else {
None
}
}
pub fn dirty_marks(&self) -> &RoaringBitmap {
&self.dirty_marks
}
pub fn reset_dirty_marks(&mut self) {
self.dirty_marks.clear();
}
}
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() {
" ".to_compact_string()
} else {
s.clone()
});
}
results.push(row_symbols);
}
results
}
}