use crate::prelude::*;
#[derive(Clone)]
pub struct LayoutContainer {
pub bounds: Rect,
pub padding: Padding,
pub layout_type: LayoutType,
}
#[derive(Clone)]
pub struct Padding {
pub left: f32,
pub right: f32,
pub top: f32,
pub bottom: f32,
}
impl Padding {
pub fn all(value: f32) -> Self {
Self {
left: value,
right: value,
top: value,
bottom: value,
}
}
pub fn horizontal_vertical(h: f32, v: f32) -> Self {
Self {
left: h,
right: h,
top: v,
bottom: v,
}
}
pub fn symmetric(horizontal: f32, vertical: f32) -> Self {
Self::horizontal_vertical(horizontal, vertical)
}
}
#[derive(Clone)]
pub enum LayoutType {
Vertical {
gap: f32,
alignment: Alignment,
},
Horizontal {
gap: f32,
alignment: Alignment,
},
Grid {
columns: usize,
gap: Vec2,
alignment: Alignment,
},
Absolute,
}
#[derive(Clone, Copy, Debug)]
pub enum Alignment {
Start,
Center,
End,
}
pub struct LayoutBuilder {
container: LayoutContainer,
items: Vec<LayoutItem>,
}
#[derive(Clone)]
pub struct LayoutItem {
pub size: Vec2,
pub position: Vec2,
pub anchor: Vec2, }
impl LayoutBuilder {
pub fn new(bounds: Rect) -> Self {
Self {
container: LayoutContainer {
bounds,
padding: Padding::all(0.0),
layout_type: LayoutType::Vertical {
gap: 0.0,
alignment: Alignment::Start,
},
},
items: Vec::new(),
}
}
pub fn with_padding(mut self, padding: Padding) -> Self {
self.container.padding = padding;
self
}
pub fn vertical(mut self, gap: f32) -> Self {
self.container.layout_type = LayoutType::Vertical {
gap,
alignment: Alignment::Start,
};
self
}
pub fn horizontal(mut self, gap: f32) -> Self {
self.container.layout_type = LayoutType::Horizontal {
gap,
alignment: Alignment::Start,
};
self
}
pub fn grid(mut self, columns: usize, gap: Vec2) -> Self {
self.container.layout_type = LayoutType::Grid {
columns,
gap,
alignment: Alignment::Start,
};
self
}
pub fn with_alignment(mut self, alignment: Alignment) -> Self {
match &mut self.container.layout_type {
LayoutType::Vertical { alignment: a, .. } => *a = alignment,
LayoutType::Horizontal { alignment: a, .. } => *a = alignment,
LayoutType::Grid { alignment: a, .. } => *a = alignment,
LayoutType::Absolute => {}
}
self
}
pub fn add_item(mut self, size: Vec2) -> Self {
self.items.push(LayoutItem {
size,
position: Vec2::ZERO, anchor: Vec2::new(0.5, 0.5), });
self
}
pub fn add_multiple_items(mut self, count: usize, size: Vec2) -> Self {
for _ in 0..count {
self = self.add_item(size);
}
self
}
pub fn calculate(self) -> LayoutResult {
let layout_type = self.container.layout_type.clone();
match layout_type {
LayoutType::Vertical { gap, alignment } => {
self.calculate_vertical_layout(gap, alignment)
}
LayoutType::Horizontal { gap, alignment } => {
self.calculate_horizontal_layout(gap, alignment)
}
LayoutType::Grid {
columns,
gap,
alignment,
} => self.calculate_grid_layout(columns, gap, alignment),
LayoutType::Absolute => LayoutResult { items: self.items },
}
}
fn calculate_vertical_layout(mut self, gap: f32, alignment: Alignment) -> LayoutResult {
let content_bounds = self.get_content_bounds();
let mut current_y = content_bounds.max.y;
for item in &mut self.items {
let x = match alignment {
Alignment::Start => content_bounds.min.x + item.size.x * 0.5,
Alignment::Center => content_bounds.center().x,
Alignment::End => content_bounds.max.x - item.size.x * 0.5,
};
current_y -= item.size.y * 0.5;
item.position = Vec2::new(x, current_y);
current_y -= item.size.y * 0.5 + gap;
}
LayoutResult { items: self.items }
}
fn calculate_horizontal_layout(mut self, gap: f32, alignment: Alignment) -> LayoutResult {
let content_bounds = self.get_content_bounds();
let mut current_x = content_bounds.min.x;
for item in &mut self.items {
let y = match alignment {
Alignment::Start => content_bounds.max.y - item.size.y * 0.5,
Alignment::Center => content_bounds.center().y,
Alignment::End => content_bounds.min.y + item.size.y * 0.5,
};
current_x += item.size.x * 0.5;
item.position = Vec2::new(current_x, y);
current_x += item.size.x * 0.5 + gap;
}
LayoutResult { items: self.items }
}
fn calculate_grid_layout(
mut self,
columns: usize,
gap: Vec2,
_alignment: Alignment,
) -> LayoutResult {
let content_bounds = self.get_content_bounds();
for (index, item) in self.items.iter_mut().enumerate() {
let row = index / columns;
let col = index % columns;
let x = content_bounds.min.x + col as f32 * (item.size.x + gap.x) + item.size.x * 0.5;
let y = content_bounds.max.y - row as f32 * (item.size.y + gap.y) - item.size.y * 0.5;
item.position = Vec2::new(x, y);
}
LayoutResult { items: self.items }
}
fn get_content_bounds(&self) -> Rect {
let padding = &self.container.padding;
Rect {
min: Vec2::new(
self.container.bounds.min.x + padding.left,
self.container.bounds.min.y + padding.bottom,
),
max: Vec2::new(
self.container.bounds.max.x - padding.right,
self.container.bounds.max.y - padding.top,
),
}
}
}
pub struct LayoutResult {
pub items: Vec<LayoutItem>,
}