use egui::{Rect, Pos2, Vec2, Margin, Align, Ui, Sense, Response, Layout};
use egui_extras::Size;
use crate::{
grid::*,
helper::*
};
#[derive(Clone)]
pub struct GridBuilder {
units: Vec<Row>,
spacing: Vec2,
row_as_col: bool,
creation_cache: Vec<(usize, usize)>,
clip: bool,
use_default_spacing: bool,
default_layout: Layout
}
impl GridBuilder {
pub fn new() -> GridBuilder {
GridBuilder {
units: Vec::new(),
spacing: Vec2::ZERO,
row_as_col: false,
creation_cache: Vec::new(),
clip: false,
use_default_spacing: true,
default_layout: Layout::default()
}
}
pub fn spacing(mut self, width: f32, height: f32) -> Self {
self.spacing = Vec2 { x: width, y: height };
self.use_default_spacing = false;
self
}
pub fn spacing_vec2(mut self, spacing: Vec2) -> Self {
self.spacing = spacing;
self.use_default_spacing = false;
self
}
pub fn clip(mut self, clip: bool) -> Self {
self.clip = clip;
self
}
pub fn new_row(mut self, size: Size) -> Self {
self.units.push(Row::new(size, Align::Min));
self
}
pub fn new_row_align(mut self, size: Size, align: Align) -> Self {
self.units.push(Row::new(size, align));
self
}
pub fn align(mut self, align: Align) -> Self {
let len = self.units.len();
if len > 0 {
self.units[len-1].align(align);
}
self
}
pub fn cell(mut self, size: Size) -> Self {
self.add_cells(size, 1, Margin::same(0.)); self
}
pub fn cells(mut self, size: Size, amount: i32) -> Self {
self.add_cells(size, amount, Margin::same(0.)); self
}
pub fn with_margin(mut self, margin: Margin) -> Self {
if self.creation_cache.len() > 0 {
for item in self.creation_cache.iter() {
self.units[item.0].cells[item.1].edit_margin(margin);
}
}
self
}
pub fn with_layout(mut self, layout: Layout) -> Self {
if self.creation_cache.len() > 0 {
for item in self.creation_cache.iter() {
self.units[item.0].cells[item.1].edit_layout(layout);
}
}
self
}
pub fn layout_standard(mut self, layout: Layout) -> Self {
self.default_layout = layout;
self
}
pub fn nest(mut self, grid: GridBuilder) -> Self {
let len = self.units.len();
if len > 0 {
let cell_len = self.units[len-1].cells.len();
if cell_len > 0 {
self.units[len-1].cells[cell_len-1].nest(grid);
}
}
self
}
pub fn nest_at(mut self, row: i32, cell: i32, grid: GridBuilder) -> Self {
let u_row = row as usize;
let u_cell = cell as usize;
if self.units.get(u_row).is_some() {
if self.units[u_row].cells.get(u_cell).is_some() {
self.units[u_row].cells[u_cell].nest(grid);
}
}
self
}
pub fn show(self, ui: &mut Ui, grid: impl FnOnce(Grid)) -> Response {
let allocated_space = ui.available_rect_before_wrap();
let pure_cells = self.into_real_cells(allocated_space, ui.style().spacing.item_spacing.clone());
let mut bounds = Pos2::new(0., 0.);
grid(Grid::new(ui, pure_cells, &mut bounds));
ui.allocate_rect(Rect{ min: allocated_space.min, max: bounds}, Sense::hover())
}
pub fn rows_as_columns(mut self, vertical: bool) -> Self {
self.row_as_col = vertical;
self
}
fn add_cells(&mut self, size: Size, amount: i32, margin: Margin) {
let len = self.units.len();
if len > 0 {
let cel_len = self.units[len-1].cells.len();
self.creation_cache = Vec::new();
for c in 1..=amount {
self.units[len-1].cells.push(Cell::new(size, margin, self.default_layout));
self.creation_cache.push((len-1, cel_len+(c as usize)-1));
}
}
}
fn into_real_cells(&self, whole_rect: Rect, def_spacing: Vec2) -> Vec<PureCell> {
let mut cells_final = Vec::new();
let whole_h; let whole_w;
if self.row_as_col { (whole_w, whole_h) = (whole_rect.height(), whole_rect.width()); }
else { (whole_h, whole_w) = (whole_rect.height(), whole_rect.width()); }
let spacing;
if self.use_default_spacing { spacing = swap_spacing(def_spacing, self.row_as_col); }
else { spacing = swap_spacing(self.spacing, self.row_as_col); }
let row_lengths = row_set_as_f32(&self.units, &spacing.y, &whole_h);
let mut pointer2d = Pos2::new(whole_rect.min.x,whole_rect.min.y);
let mut row_index = 0;
for row in self.units.iter() {
let cell_lengths = cell_set_as_f32(&row.cells, &spacing.x, &whole_w);
let mut length_sum = -spacing.x; for length in cell_lengths.iter() { length_sum += length + spacing.x; }
let grand_offset: f32 = {
match &row.align {
Align::Min => { 0. },
Align::Center => { (whole_w - length_sum) * 0.5 },
Align::Max => { whole_w - length_sum }
}
};
pointer2d.x += grand_offset;
let mut cell_index = 0;
for cell in row.cells.iter() {
let mut rect = Rect {
min: pointer2d.clone(),
max: Pos2::new(pointer2d.x + cell_lengths[cell_index], pointer2d.y + row_lengths[row_index])
};
if self.row_as_col { rect = reflect(rect, whole_rect.min); }
let margin = &(row.cells[cell_index].margin);
rect.min.x += margin.left; rect.min.y += margin.top;
rect.max.x -= margin.right; rect.max.y -= margin.bottom;
match &row.cells[cell_index].group {
Option::Some(grid) => { cells_final.extend(grid.into_real_cells(rect, def_spacing)); },
Option::None => { cells_final.push(PureCell::new(cell.get_layout(), self.clip, rect)); }
}
pointer2d.x += cell_lengths[cell_index] + spacing.x;
cell_index += 1;
}
pointer2d.x = whole_rect.min.x.clone();
pointer2d.y += row_lengths[row_index] + spacing.y;
row_index += 1;
}
cells_final
}
}
#[derive(Clone)]
pub(crate) struct Row {
pub size: Size,
cells: Vec<Cell>,
align: Align
}
impl Row {
pub fn new(size: Size, align: Align) -> Row {
Row { size: size, cells: Vec::new(), align: align }
}
fn align(&mut self, align: Align) {
self.align = align;
}
}
#[derive(Clone)]
pub(crate) struct Cell {
pub size: Size,
margin: Margin,
layout: Layout,
pub group: Option<GridBuilder>,
}
impl Cell {
pub fn new(size: Size, margin: Margin, layout: Layout) -> Cell {
Cell { size: size, group: None, margin: margin, layout: layout }
}
pub fn nest(&mut self, grid: GridBuilder) {
self.group = Some(grid);
}
pub fn edit_margin(&mut self, margin: Margin) { self.margin = margin; }
pub fn edit_layout(&mut self, layout: Layout) { self.layout = layout; }
pub fn get_layout(&self) -> Layout { self.layout }
}
pub(crate) struct PureCell {
rect: Rect,
layout: Layout,
clip: bool
}
impl PureCell {
pub fn new(layout: Layout, clip: bool, rect: Rect) -> PureCell {
PureCell {
layout: layout, clip: clip, rect: rect
}
}
pub fn rect(&self) -> Rect { self.rect }
pub fn layout(&self) -> Layout { self.layout }
pub fn clip(&self) -> bool { self.clip }
}