use ribir_core::prelude::*;
use super::{Align, Direction, JustifyContent};
#[derive(Declare, Default, MultiChild)]
pub struct Row {
#[declare(default)]
pub align_items: Align,
#[declare(default)]
pub justify_content: JustifyContent,
}
#[derive(Declare, Default, MultiChild)]
pub struct Column {
#[declare(default)]
pub align_items: Align,
#[declare(default)]
pub justify_content: JustifyContent,
}
impl Render for Row {
fn measure(&self, clamp: BoxClamp, ctx: &mut MeasureCtx) -> Size {
perform_linear_measure(
Direction::Horizontal,
self.align_items,
self.justify_content,
clamp,
ctx,
)
}
fn place_children(&self, size: Size, ctx: &mut PlaceCtx) {
perform_linear_layout_positions(
Direction::Horizontal,
self.align_items,
self.justify_content,
size,
ctx,
)
}
#[cfg(feature = "debug")]
fn debug_name(&self) -> std::borrow::Cow<'static, str> { std::borrow::Cow::Borrowed("row") }
}
impl Render for Column {
fn measure(&self, clamp: BoxClamp, ctx: &mut MeasureCtx) -> Size {
perform_linear_measure(Direction::Vertical, self.align_items, self.justify_content, clamp, ctx)
}
fn place_children(&self, size: Size, ctx: &mut PlaceCtx) {
perform_linear_layout_positions(
Direction::Vertical,
self.align_items,
self.justify_content,
size,
ctx,
)
}
#[cfg(feature = "debug")]
fn debug_name(&self) -> std::borrow::Cow<'static, str> { std::borrow::Cow::Borrowed("column") }
}
fn perform_linear_measure(
dir: Direction, align_items: Align, justify_content: JustifyContent, clamp: BoxClamp,
ctx: &mut MeasureCtx,
) -> Size {
let cross_max = dir.cross_max_of(&clamp);
let child_clamp = if align_items == Align::Stretch && cross_max.is_finite() {
dir.with_fixed_cross(clamp.loose(), cross_max)
} else {
dir.with_cross_max(clamp.loose(), cross_max)
};
let (ctx, children) = ctx.split_children();
let (mut main, mut cross) = (0., 0f32);
for child in children {
let child_size = ctx.layout_child(child, child_clamp);
main += dir.main_of(child_size);
cross = cross.max(dir.cross_of(child_size));
}
let main_container = dir.container_main(&clamp, main);
let main = dir.main_clamp(main, &clamp);
let main = if justify_content.is_space_layout() { main_container } else { main };
let cross = dir.cross_clamp(cross, &clamp);
dir.to_size(main, cross)
}
fn perform_linear_layout_positions(
dir: Direction, align_items: Align, justify_content: JustifyContent, size: Size,
ctx: &mut PlaceCtx,
) {
let child_cnt = ctx.children().count();
let main_container = dir.main_of(size);
let cross_container = dir.cross_of(size);
let (ctx, children) = ctx.split_children();
let total_main: f32 = children
.map(|c| {
let size = ctx.widget_box_size(c).unwrap();
dir.main_of(size)
})
.sum();
let (mut main_pos, step) =
justify_content.item_offset_and_step(main_container - total_main, child_cnt);
let (ctx, children) = ctx.split_children();
for child in children {
let child_size = ctx.widget_box_size(child).unwrap();
let cross_pos = align_items.align_value(dir.cross_of(child_size), cross_container);
let pos = dir.to_point(main_pos, cross_pos);
ctx.update_position(child, pos);
main_pos += dir.main_of(child_size) + step;
}
}
#[cfg(test)]
mod tests {
use ribir_core::test_helper::*;
use ribir_dev_helper::*;
use super::*;
use crate::prelude::*;
widget_layout_test!(
row_stretch_height,
WidgetTester::new(fn_widget! {
@Row {
align_items: Align::Stretch,
@Container { size: Size::new(100., 50.) }
}
})
.with_wnd_size(Size::new(500., 200.)),
LayoutCase::new(&[0, 0]).with_size(Size::new(100., 200.))
);
}