use super::*;
use crate::geometry::*;
pub struct Grid<W> {
row_weights: Vec<u8>,
col_weights: Vec<u8>,
cells: Vec<(usize, usize, W)>,
}
impl<W> Default for Grid<W> {
fn default() -> Self {
Self {
row_weights: Default::default(),
col_weights: Default::default(),
cells: Default::default(),
}
}
}
impl<W> Grid<W> {
pub fn add(mut self, cell: (usize, usize), weight: (u8, u8), widget: W) -> Self {
let (col, row) = cell;
let (col_weight, row_weight) = weight;
if row >= self.row_weights.len() {
self.row_weights.resize(row + 1, 0)
}
if self.row_weights[row] < row_weight {
self.row_weights[row] = row_weight
}
if col >= self.col_weights.len() {
self.col_weights.resize(col + 1, 0)
}
if self.col_weights[col] < col_weight {
self.col_weights[col] = col_weight
}
self.cells.push((col, row, widget));
self
}
fn cell_scope(size: Point, element: usize, col_weights: &[u8], row_weights: &[u8]) -> Window {
let cols = col_weights.len();
let rows = row_weights.len();
if element >= cols * rows {
return window(point(0, 0), size);
}
let row = element / cols;
let col = element % cols;
let cell_x = Self::cell_size(col_weights, col, size.col);
let cell_y = Self::cell_size(row_weights, row, size.row);
window(point(cell_x.0, cell_y.0), point(cell_x.1, cell_y.1))
}
fn cell_size(grid: &[u8], idx: usize, size: u16) -> (u16, u16) {
if idx >= grid.len() {
return (0, size);
}
let sum = grid.iter().fold(0u16, |s, c| s + *c as u16);
let scale = if sum == 0 {
0f32
} else {
size as f32 / sum as f32
};
let start = grid[..idx]
.iter()
.cloned()
.fold(0u16, |s, c| s + (scale * c as f32) as u16);
let cell = if idx + 1 == grid.len() {
size - start
} else {
(scale * grid[idx] as f32) as u16
};
(start, cell)
}
pub fn get_cell_for_point(&self, point: Point, scope: Window) -> Option<(usize, usize)> {
for col in 0..self.col_weights.len() {
for row in 0..self.row_weights.len() {
let cell_scope = Self::cell_scope(
scope.size(),
row * self.col_weights.len() + col,
&self.col_weights,
&self.row_weights,
) + scope.position();
if (cell_scope & point).is_some() {
return Some((col, row));
}
}
}
None
}
pub fn get_cell_scope_by_idx(&self, i: usize, scope: Window) -> Window {
Self::cell_scope(scope.size(), i, &self.col_weights, &self.row_weights) + scope.position()
}
pub fn get_cell_scope(&self, i: (usize, usize), scope: Window) -> Window {
let (x, y) = i;
let i = x + y * self.cols();
self.get_cell_scope_by_idx(i, scope)
}
pub fn rows(&self) -> usize {
self.row_weights.len()
}
pub fn cols(&self) -> usize {
self.col_weights.len()
}
}
impl<'a, M, A: AppEvent> Grid<Box<dyn Widget<M, A> + 'a>> {
pub fn boxed() -> Self {
Self {
row_weights: Default::default(),
col_weights: Default::default(),
cells: Default::default(),
}
}
pub fn add_box(
self,
cell: (usize, usize),
weight: (u8, u8),
widget: impl Widget<M, A> + 'a,
) -> Self {
self.add(cell, weight, Box::new(widget))
}
}
impl<W, M, A:AppEvent> Widget<M, A> for Grid<W>
where
W: Widget<M, A>,
{
fn update(
&mut self,
model: &mut M,
input: &Event<A>,
screen: &mut Screen,
painter: &Painter,
) -> Window {
for cell in self.cells.iter_mut() {
let (col, row, element) = cell;
let ord = *row * self.col_weights.len() + *col;
let cell_scope = Self::cell_scope(
painter.scope().size(),
ord,
&self.col_weights,
&self.row_weights,
);
let absolute_scope = painter.scope().relative_crop(&cell_scope);
element.update(model, input, screen, &painter.with_scope(absolute_scope));
}
painter.scope()
}
}
#[test]
fn test_layout() {
let row_weights = vec![100, 100];
let col_weights = vec![100, 100, 100];
let size = point(10, 13);
assert_eq!(
Grid::<()>::cell_scope(size, 0usize, &col_weights, &row_weights),
window(point(0, 0), point(3, 6))
);
assert_eq!(
Grid::<()>::cell_scope(size, 2usize, &col_weights, &row_weights),
window(point(6, 0), point(4, 6))
);
assert_eq!(
Grid::<()>::cell_scope(size, 3usize, &col_weights, &row_weights),
window(point(0, 6), point(3, 7))
);
assert_eq!(
Grid::<()>::cell_scope(size, 5usize, &col_weights, &row_weights),
window(point(6, 6), point(4, 7))
);
}
#[test]
fn test_layout2() {
let row_weights = vec![50, 50];
let col_weights = vec![50, 50, 50];
let size = point(148, 109);
assert_eq!(
Grid::<()>::cell_scope(size, 0usize, &col_weights, &row_weights),
window(point(0, 0), point(49, 54))
);
assert_eq!(
Grid::<()>::cell_scope(size, 2usize, &col_weights, &row_weights),
window(point(98, 0), point(50, 54))
);
assert_eq!(
Grid::<()>::cell_scope(size, 3usize, &col_weights, &row_weights),
window(point(0, 54), point(49, 55))
);
assert_eq!(
Grid::<()>::cell_scope(size, 4usize, &col_weights, &row_weights),
window(point(49, 54), point(49, 55))
);
assert_eq!(
Grid::<()>::cell_scope(size, 5usize, &col_weights, &row_weights),
window(point(98, 54), point(50, 55))
);
}
#[test]
fn test_layout3() {
let row_weights = vec![50, 50, 50];
let col_weights = vec![50, 50, 50];
let size = point(5, 5);
assert_eq!(
Grid::<()>::cell_scope(size, 0usize, &col_weights, &row_weights),
window(point(0, 0), point(1, 1))
);
assert_eq!(
Grid::<()>::cell_scope(size, 4usize, &col_weights, &row_weights),
window(point(1, 1), point(1, 1))
);
assert_eq!(
Grid::<()>::cell_scope(size, 8usize, &col_weights, &row_weights),
window(point(2, 2), point(3, 3))
);
}
#[test]
fn test_layout_0() {
let row_weights = vec![0, 0, 0];
let col_weights = vec![0, 1, 0];
let size = point(5, 5);
assert_eq!(
Grid::<()>::cell_scope(size, 0usize, &col_weights, &row_weights),
window(point(0, 0), point(0, 0))
);
assert_eq!(
Grid::<()>::cell_scope(size, 4usize, &col_weights, &row_weights),
window(point(0, 0), point(5, 0))
);
assert_eq!(
Grid::<()>::cell_scope(size, 8usize, &col_weights, &row_weights),
window(point(5, 0), point(0, 5))
);
}
#[test]
fn test_layout_free() {
let row_weights = vec![];
let col_weights = vec![];
let size = point(5, 5);
assert_eq!(
Grid::<()>::cell_scope(size, 0usize, &col_weights, &row_weights),
window(point(0, 0), point(5, 5))
);
assert_eq!(
Grid::<()>::cell_scope(size, 4usize, &col_weights, &row_weights),
window(point(0, 0), point(5, 5))
);
assert_eq!(
Grid::<()>::cell_scope(size, 8usize, &col_weights, &row_weights),
window(point(0, 0), point(5, 5))
);
}