use core::mem;
use ratatui_core::{
buffer::{Buffer, Cell},
layout::{Position, Rect},
};
use crate::{
cell_filter::{CellValidator, FilterProcessor},
CellFilter,
};
pub struct CellIterator<'a> {
current: u32,
area: Rect,
buf: &'a mut Buffer,
predicate: Option<CellValidator<'a>>,
}
impl<'a> CellIterator<'a> {
pub fn new(
buf: &'a mut Buffer,
area: Rect,
filter_processor: Option<&'a FilterProcessor>,
) -> Self {
Self {
current: 0,
area: area.intersection(buf.area),
buf,
predicate: filter_processor
.filter(|p| p.filter_ref() != &CellFilter::All) .map(|f| f.validator()),
}
}
pub fn for_each_cell<F>(self, mut f: F)
where
F: FnMut(Position, &mut Cell),
{
let area = self.area;
let predicate = self.predicate.as_ref();
for y in area.y..area.bottom() {
for x in area.x..area.right() {
let pos = Position::new(x, y);
if let Some(cell) = self.buf.cell_mut(pos) {
if predicate.is_none_or(|p| p.is_valid(pos, cell)) {
f(pos, cell);
}
}
}
}
}
fn cell_mut(&mut self) -> Option<(Position, &mut Cell)> {
let x = (self.current % self.area.width as u32) as u16;
let y = (self.current / self.area.width as u32) as u16;
let pos = Position::new(self.area.x + x, self.area.y + y);
let cell = self.buf.cell_mut(pos)?;
Some((pos, cell))
}
}
impl<'a> Iterator for CellIterator<'a> {
type Item = (Position, &'a mut Cell);
fn next(&mut self) -> Option<Self::Item> {
let area = self.area.area();
while self.current < area {
let (pos, cell) = self.cell_mut()?;
let cell: &'a mut Cell = unsafe { mem::transmute(cell) };
self.current += 1;
if self
.predicate
.as_ref()
.is_none_or(|p| p.is_valid(pos, cell))
{
return Some((pos, cell));
}
}
None
}
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use ratatui_core::{buffer::Buffer, layout::Rect, style::Color};
use super::*;
#[test]
fn test_normal_iteration() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 2));
buffer[(0, 0)].set_char('A');
buffer[(1, 0)].set_char('B');
buffer[(2, 0)].set_char('C');
buffer[(0, 1)].set_char('D');
buffer[(1, 1)].set_char('E');
buffer[(2, 1)].set_char('F');
let mut iter = CellIterator::new(&mut buffer, Rect::new(0, 0, 3, 2), None);
let mut positions = Vec::new();
let mut chars = Vec::new();
for (pos, cell) in &mut iter {
positions.push(pos);
chars.push(cell.symbol().chars().next().unwrap_or(' '));
}
assert_eq!(positions.len(), 6);
assert_eq!(chars, vec!['A', 'B', 'C', 'D', 'E', 'F']);
assert_eq!(positions[0], Position::new(0, 0));
assert_eq!(positions[1], Position::new(1, 0));
assert_eq!(positions[2], Position::new(2, 0));
assert_eq!(positions[3], Position::new(0, 1));
assert_eq!(positions[4], Position::new(1, 1));
assert_eq!(positions[5], Position::new(2, 1));
}
#[test]
fn test_for_each_cell() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 2));
buffer[(0, 0)].set_char('A');
buffer[(1, 0)].set_char('B');
buffer[(2, 0)].set_char('C');
buffer[(0, 1)].set_char('D');
buffer[(1, 1)].set_char('E');
buffer[(2, 1)].set_char('F');
let iter = CellIterator::new(&mut buffer, Rect::new(0, 0, 3, 2), None);
let mut positions = Vec::new();
let mut chars = Vec::new();
iter.for_each_cell(|pos, cell| {
positions.push(pos);
chars.push(cell.symbol().chars().next().unwrap_or(' '));
cell.set_fg(Color::Red);
});
assert_eq!(positions.len(), 6);
assert_eq!(chars, vec!['A', 'B', 'C', 'D', 'E', 'F']);
assert_eq!(positions[0], Position::new(0, 0));
assert_eq!(positions[1], Position::new(1, 0));
assert_eq!(positions[2], Position::new(2, 0));
assert_eq!(positions[3], Position::new(0, 1));
assert_eq!(positions[4], Position::new(1, 1));
assert_eq!(positions[5], Position::new(2, 1));
assert_eq!(buffer[(0, 0)].fg, Color::Red);
assert_eq!(buffer[(1, 0)].fg, Color::Red);
assert_eq!(buffer[(2, 0)].fg, Color::Red);
}
}