use ratatui::prelude::*;
use super::{Chart, ChartState};
use crate::component::{Component, RenderContext};
#[derive(Clone, Debug, PartialEq)]
pub struct ChartGrid {
rows: usize,
cols: usize,
charts: Vec<Option<ChartState>>, }
impl ChartGrid {
pub fn new(rows: usize, cols: usize) -> Self {
assert!(rows > 0, "ChartGrid rows must be at least 1");
assert!(cols > 0, "ChartGrid cols must be at least 1");
Self {
rows,
cols,
charts: vec![None; rows * cols],
}
}
pub fn set(mut self, row: usize, col: usize, chart: ChartState) -> Self {
assert!(
row < self.rows,
"row index {row} out of bounds for grid with {} rows",
self.rows
);
assert!(
col < self.cols,
"col index {col} out of bounds for grid with {} cols",
self.cols
);
self.charts[row * self.cols + col] = Some(chart);
self
}
pub fn get(&self, row: usize, col: usize) -> Option<&ChartState> {
assert!(
row < self.rows,
"row index {row} out of bounds for grid with {} rows",
self.rows
);
assert!(
col < self.cols,
"col index {col} out of bounds for grid with {} cols",
self.cols
);
self.charts[row * self.cols + col].as_ref()
}
pub fn get_mut(&mut self, row: usize, col: usize) -> Option<&mut ChartState> {
assert!(
row < self.rows,
"row index {row} out of bounds for grid with {} rows",
self.rows
);
assert!(
col < self.cols,
"col index {col} out of bounds for grid with {} cols",
self.cols
);
self.charts[row * self.cols + col].as_mut()
}
pub fn set_chart(&mut self, row: usize, col: usize, chart: ChartState) {
assert!(
row < self.rows,
"row index {row} out of bounds for grid with {} rows",
self.rows
);
assert!(
col < self.cols,
"col index {col} out of bounds for grid with {} cols",
self.cols
);
self.charts[row * self.cols + col] = Some(chart);
}
pub fn take(&mut self, row: usize, col: usize) -> Option<ChartState> {
assert!(
row < self.rows,
"row index {row} out of bounds for grid with {} rows",
self.rows
);
assert!(
col < self.cols,
"col index {col} out of bounds for grid with {} cols",
self.cols
);
self.charts[row * self.cols + col].take()
}
pub fn rows(&self) -> usize {
self.rows
}
pub fn cols(&self) -> usize {
self.cols
}
pub fn cell_count(&self) -> usize {
self.rows * self.cols
}
pub fn chart_count(&self) -> usize {
self.charts.iter().filter(|c| c.is_some()).count()
}
pub fn render(&self, ctx: &mut RenderContext<'_, '_>) {
let row_constraints: Vec<Constraint> = (0..self.rows)
.map(|_| Constraint::Ratio(1, self.rows as u32))
.collect();
let row_areas = Layout::default()
.direction(Direction::Vertical)
.constraints(row_constraints)
.split(ctx.area);
let col_constraints: Vec<Constraint> = (0..self.cols)
.map(|_| Constraint::Ratio(1, self.cols as u32))
.collect();
for (row_idx, row_area) in row_areas.iter().enumerate() {
let col_areas = Layout::default()
.direction(Direction::Horizontal)
.constraints(col_constraints.clone())
.split(*row_area);
for (col_idx, col_area) in col_areas.iter().enumerate() {
let cell_idx = row_idx * self.cols + col_idx;
if let Some(chart) = &self.charts[cell_idx] {
Chart::view(chart, &mut ctx.with_area(*col_area));
}
}
}
}
}
#[cfg(test)]
mod tests;