use deref_derive::{Deref, DerefMut};
use glam::Vec2;
use ori_graphics::Rect;
use smallvec::SmallVec;
use crate::{
AlignItems, Axis, BoxConstraints, DrawContext, Event, EventContext, JustifyContent,
LayoutContext, Node,
};
pub struct FlexLayout {
pub offset: Vec2,
pub axis: Axis,
pub justify_content: JustifyContent,
pub align_items: AlignItems,
pub gap: f32,
}
impl Default for FlexLayout {
fn default() -> Self {
Self {
offset: Vec2::ZERO,
axis: Axis::Vertical,
justify_content: JustifyContent::Start,
align_items: AlignItems::Start,
gap: 0.0,
}
}
}
impl FlexLayout {
pub fn new() -> Self {
Self::default()
}
pub fn vertical() -> Self {
Self {
axis: Axis::Vertical,
..Self::default()
}
}
pub fn horizontal() -> Self {
Self {
axis: Axis::Horizontal,
..Self::default()
}
}
pub fn row() -> Self {
Self::horizontal()
}
pub fn column() -> Self {
Self::vertical()
}
}
#[derive(Default, Deref, DerefMut)]
pub struct Children {
nodes: Vec<Node>,
}
impl Children {
pub const fn new() -> Self {
Self { nodes: Vec::new() }
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn add_child(&mut self, child: impl Into<Node>) {
self.nodes.push(child.into());
}
pub fn event(&self, cx: &mut EventContext, event: &Event) {
for child in self.iter() {
child.event(cx, event);
}
}
pub fn flex_layout(
&self,
cx: &mut LayoutContext,
bc: BoxConstraints,
flex: FlexLayout,
) -> Vec2 {
let FlexLayout {
offset,
axis,
justify_content,
align_items,
gap,
} = flex;
let max_minor = axis.minor(bc.max);
let min_minor = axis.minor(bc.min);
let max_major = axis.major(bc.max);
let min_major = axis.major(bc.min);
let mut minor = min_minor;
let mut major = 0.0f32;
let mut children = SmallVec::<[f32; 8]>::with_capacity(self.len());
for (i, child) in self.iter().enumerate() {
let child_bc = BoxConstraints {
min: axis.pack(0.0, 0.0),
max: axis.pack(max_major, max_minor),
};
let size = child.layout(cx, child_bc);
let child_minor = axis.minor(size);
let child_major = axis.major(size);
children.push(child_major);
minor = minor.max(child_minor);
major += child_major;
if i > 0 {
major += gap;
}
}
if align_items == AlignItems::Stretch {
major = 0.0;
children.clear();
for (i, child) in self.iter().enumerate() {
let child_bc = BoxConstraints {
min: axis.pack(0.0, minor),
max: axis.pack(max_major, minor),
};
let size = child.layout(cx, child_bc);
let child_major = axis.major(size);
children.push(child_major);
major += child_major;
if i > 0 {
major += gap;
}
}
}
major = major.max(min_major);
let child_offsets = justify_content.justify(&children, major, gap);
for (child, align_major) in self.iter().zip(child_offsets) {
let child_minor = axis.minor(child.size());
let align_minor = align_items.align(0.0, minor, child_minor);
let child_offset = axis.pack(align_major, align_minor);
child.set_offset(offset + child_offset);
}
axis.pack(major, minor)
}
pub fn local_rect(&self) -> Rect {
let mut rect = None;
for child in self.iter() {
let rect = rect.get_or_insert_with(|| child.local_rect());
*rect = rect.union(child.local_rect());
}
rect.unwrap_or_default()
}
pub fn rect(&self) -> Rect {
let mut rect = None;
for child in self.iter() {
let rect = rect.get_or_insert_with(|| child.global_rect());
*rect = rect.union(child.global_rect());
}
rect.unwrap_or_default()
}
pub fn size(&self) -> Vec2 {
self.rect().size()
}
pub fn set_offset(&self, offset: Vec2) {
if self.is_empty() {
return;
}
let min = self.local_rect().min;
for child in self.iter() {
let child_offset = child.local_rect().min - min;
child.set_offset(offset + child_offset);
}
}
pub fn draw(&self, cx: &mut DrawContext) {
for child in self.iter() {
child.draw(cx);
}
}
}
impl IntoIterator for Children {
type Item = Node;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.nodes.into_iter()
}
}
impl<'a> IntoIterator for &'a Children {
type Item = &'a Node;
type IntoIter = std::slice::Iter<'a, Node>;
fn into_iter(self) -> Self::IntoIter {
self.nodes.iter()
}
}
impl<'a> IntoIterator for &'a mut Children {
type Item = &'a mut Node;
type IntoIter = std::slice::IterMut<'a, Node>;
fn into_iter(self) -> Self::IntoIter {
self.nodes.iter_mut()
}
}