use crate::types::Rect;
use super::node::{AlignItems, FlexDirection, JustifyContent, LayoutNode};
pub fn compute_layout(node: &mut LayoutNode, x: i32, y: i32, available_w: u16, available_h: u16) {
let pad = &node.style.padding;
let w = node.style.width.unwrap_or(available_w);
let h = node.style.height.unwrap_or(available_h);
node.rect = Rect { x, y, w, h };
if node.children.is_empty() {
return;
}
let inner_w = w.saturating_sub(pad.left + pad.right);
let inner_h = h.saturating_sub(pad.top + pad.bottom);
let inner_x = x + pad.left as i32;
let inner_y = y + pad.top as i32;
let is_row = node.style.direction == FlexDirection::Row;
let main_size = if is_row { inner_w } else { inner_h } as f32;
let cross_size = if is_row { inner_h } else { inner_w };
let mut fixed_total: f32 = 0.0;
let mut grow_total: f32 = 0.0;
for child in &node.children {
let child_main = if is_row {
child.style.width
} else {
child.style.height
};
if let Some(s) = child_main {
fixed_total += s as f32;
} else if child.style.grow > 0.0 {
grow_total += child.style.grow;
}
}
let remaining = (main_size - fixed_total).max(0.0);
let child_count = node.children.len();
let mut sizes: alloc::vec::Vec<(u16, u16)> = alloc::vec::Vec::with_capacity(child_count);
for child in &node.children {
let child_main = if is_row {
child.style.width
} else {
child.style.height
};
let m = if let Some(s) = child_main {
s
} else if child.style.grow > 0.0 && grow_total > 0.0 {
(remaining * child.style.grow / grow_total) as u16
} else {
0
};
let child_cross = if is_row {
child.style.height
} else {
child.style.width
};
let c = match node.style.align {
AlignItems::Stretch => child_cross.unwrap_or(cross_size),
_ => child_cross.unwrap_or(cross_size),
};
sizes.push((m, c));
}
let total_main: f32 = sizes.iter().map(|(m, _)| *m as f32).sum();
let free_space = (main_size - total_main).max(0.0);
let (mut offset, gap) = match node.style.justify {
JustifyContent::FlexStart => (0.0, 0.0),
JustifyContent::FlexEnd => (free_space, 0.0),
JustifyContent::Center => (free_space / 2.0, 0.0),
JustifyContent::SpaceBetween => {
if child_count > 1 {
(0.0, free_space / (child_count - 1) as f32)
} else {
(0.0, 0.0)
}
}
JustifyContent::SpaceAround => {
let g = free_space / child_count as f32;
(g / 2.0, g)
}
JustifyContent::SpaceEvenly => {
let g = free_space / (child_count + 1) as f32;
(g, g)
}
};
for (i, child) in node.children.iter_mut().enumerate() {
let (m, c) = sizes[i];
let cross_offset = match node.style.align {
AlignItems::FlexStart => 0.0,
AlignItems::FlexEnd => (cross_size - c) as f32,
AlignItems::Center => (cross_size - c) as f32 / 2.0,
AlignItems::Stretch => 0.0,
};
let (cx, cy, cw, ch) = if is_row {
(inner_x + offset as i32, inner_y + cross_offset as i32, m, c)
} else {
(inner_x + cross_offset as i32, inner_y + offset as i32, c, m)
};
compute_layout(child, cx, cy, cw, ch);
offset += m as f32 + gap;
}
}