use cvkg_core::{LayoutView, SizeProposal, Rect, LayoutCache, Size};
pub struct HStack {
spacing: f32,
children: Vec<Box<dyn LayoutView>>,
}
impl HStack {
pub fn new(spacing: f32) -> Self {
Self {
spacing,
children: Vec::new(),
}
}
pub fn add_view<V: LayoutView + 'static>(&mut self, view: V) {
self.children.push(Box::new(view));
}
pub fn compute_layout(spacing: f32, bounds: Rect, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Vec<Rect> {
let mut rects = Vec::with_capacity(subviews.len());
let mut x = bounds.x;
for child in subviews {
let desired = child.size_that_fits(SizeProposal::unspecified(), &[], cache);
rects.push(Rect {
x,
y: bounds.y,
width: desired.width,
height: bounds.height,
});
x += desired.width + spacing;
}
rects
}
}
impl LayoutView for HStack {
fn size_that_fits(&self, proposal: SizeProposal, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Size {
let mut width = 0.0f32;
let mut height = 0.0f32;
for (i, child) in subviews.iter().enumerate() {
let child_size = child.size_that_fits(proposal, &[], cache);
width += child_size.width;
height = height.max(child_size.height);
if i < subviews.len() - 1 {
width += self.spacing;
}
}
Size { width, height }
}
fn place_subviews(&self, bounds: Rect, subviews: &mut [&mut dyn LayoutView], cache: &mut LayoutCache) {
let mut x = bounds.x;
let y = bounds.y;
for (_i, child) in subviews.iter_mut().enumerate() {
let desired_size = child.size_that_fits(SizeProposal::unspecified(), &[], cache);
let child_rect = Rect {
x,
y,
width: desired_size.width,
height: bounds.height,
};
child.place_subviews(child_rect, &mut [], cache);
x += desired_size.width + self.spacing;
}
}
}
pub struct VStack {
spacing: f32,
children: Vec<Box<dyn LayoutView>>,
}
impl VStack {
pub fn new(spacing: f32) -> Self {
Self {
spacing,
children: Vec::new(),
}
}
pub fn add_view<V: LayoutView + 'static>(&mut self, view: V) {
self.children.push(Box::new(view));
}
pub fn compute_layout(spacing: f32, bounds: Rect, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Vec<Rect> {
let mut rects = Vec::with_capacity(subviews.len());
let mut y = bounds.y;
for child in subviews {
let desired = child.size_that_fits(SizeProposal::unspecified(), &[], cache);
rects.push(Rect {
x: bounds.x,
y,
width: bounds.width,
height: desired.height,
});
y += desired.height + spacing;
}
rects
}
}
impl LayoutView for VStack {
fn size_that_fits(&self, proposal: SizeProposal, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Size {
let mut width = 0.0f32;
let mut height = 0.0f32;
for (i, child) in subviews.iter().enumerate() {
let child_size = child.size_that_fits(proposal, &[], cache);
width = width.max(child_size.width);
height += child_size.height;
if i < subviews.len() - 1 {
height += self.spacing;
}
}
Size { width, height }
}
fn place_subviews(&self, bounds: Rect, subviews: &mut [&mut dyn LayoutView], cache: &mut LayoutCache) {
let x = bounds.x;
let mut y = bounds.y;
for (_i, child) in subviews.iter_mut().enumerate() {
let desired_size = child.size_that_fits(SizeProposal::unspecified(), &[], cache);
let child_rect = Rect {
x,
y,
width: bounds.width,
height: desired_size.height,
};
child.place_subviews(child_rect, &mut [], cache);
y += desired_size.height + self.spacing;
}
}
}
pub struct ZStack {
children: Vec<Box<dyn LayoutView>>,
}
impl ZStack {
pub fn new() -> Self {
Self {
children: Vec::new(),
}
}
pub fn add_view<V: LayoutView + 'static>(&mut self, view: V) {
self.children.push(Box::new(view));
}
}
impl LayoutView for ZStack {
fn size_that_fits(&self, proposal: SizeProposal, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Size {
let mut width = 0.0f32;
let mut height = 0.0f32;
for child in subviews.iter() {
let child_size = child.size_that_fits(proposal, &[], cache);
width = width.max(child_size.width);
height = height.max(child_size.height);
}
Size { width, height }
}
fn place_subviews(&self, bounds: Rect, subviews: &mut [&mut dyn LayoutView], cache: &mut LayoutCache) {
for child in subviews.iter_mut() {
child.place_subviews(bounds, &mut [], cache);
}
}
}
pub struct Spacer;
impl LayoutView for Spacer {
fn size_that_fits(&self, proposal: SizeProposal, _subviews: &[&dyn LayoutView], _cache: &mut LayoutCache) -> Size {
Size {
width: proposal.width.unwrap_or(0.0),
height: proposal.height.unwrap_or(0.0),
}
}
fn place_subviews(&self, _bounds: Rect, _subviews: &mut [&mut dyn LayoutView], _cache: &mut LayoutCache) {}
}
pub struct Flex {
pub orientation: cvkg_core::Orientation,
pub spacing: f32,
}
impl Flex {
pub fn new(orientation: cvkg_core::Orientation, spacing: f32) -> Self {
Self { orientation, spacing }
}
}
impl LayoutView for Flex {
fn size_that_fits(&self, proposal: SizeProposal, _subviews: &[&dyn LayoutView], _cache: &mut LayoutCache) -> Size {
Size {
width: proposal.width.unwrap_or(100.0),
height: proposal.height.unwrap_or(100.0),
}
}
fn place_subviews(&self, bounds: Rect, subviews: &mut [&mut dyn LayoutView], cache: &mut LayoutCache) {
if subviews.is_empty() { return; }
let n = subviews.len() as f32;
match self.orientation {
cvkg_core::Orientation::Horizontal => {
let total_spacing = self.spacing * (n - 1.0);
let item_width = (bounds.width - total_spacing) / n;
for (i, child) in subviews.iter_mut().enumerate() {
let child_rect = Rect {
x: bounds.x + i as f32 * (item_width + self.spacing),
y: bounds.y,
width: item_width,
height: bounds.height,
};
child.place_subviews(child_rect, &mut [], cache);
}
}
cvkg_core::Orientation::Vertical => {
let total_spacing = self.spacing * (n - 1.0);
let item_height = (bounds.height - total_spacing) / n;
for (i, child) in subviews.iter_mut().enumerate() {
let child_rect = Rect {
x: bounds.x,
y: bounds.y + i as f32 * (item_height + self.spacing),
width: bounds.width,
height: item_height,
};
child.place_subviews(child_rect, &mut [], cache);
}
}
}
}
}