use crate::{
Bounds, BoxConstraints, BoxSizing, GlobalId, IntrinsicSize, LayoutError, Position, Size,
};
use std::fmt::Debug;
mod block;
mod empty;
mod horizontal;
mod vertical;
pub use block::BlockLayout;
pub use empty::EmptyLayout;
pub use horizontal::HorizontalLayout;
pub use vertical::VerticalLayout;
#[cfg(feature = "ffi")]
use crate::ffi::LayoutNode;
pub fn solve_layout(root: &mut dyn Layout, window_size: Size) -> Vec<LayoutError> {
match root.get_intrinsic_size().width {
BoxSizing::Fixed(width) => root.set_max_width(width),
_ => {
if root.constraints().max_width.is_none() {
root.set_max_width(window_size.width);
}
}
}
match root.get_intrinsic_size().height {
BoxSizing::Fixed(height) => root.set_max_height(height),
_ => root.set_max_height(window_size.height),
}
let _ = root.solve_min_constraints();
root.solve_max_constraints();
root.update_size();
root.position_children();
root.collect_errors()
}
pub trait Layout: Debug + private::Sealed {
fn label(&self) -> String;
fn solve_min_constraints(&mut self) -> (f32, f32);
fn solve_max_constraints(&mut self);
fn position_children(&mut self);
fn update_size(&mut self);
fn collect_errors(&mut self) -> Vec<LayoutError>;
fn id(&self) -> GlobalId;
fn constraints(&self) -> BoxConstraints;
fn get_intrinsic_size(&self) -> IntrinsicSize;
fn size(&self) -> Size;
fn position(&self) -> Position;
fn bounds(&self) -> Bounds {
Bounds::new(self.position(), self.size())
}
fn children(&self) -> &[Box<dyn Layout>];
fn set_max_width(&mut self, width: f32);
fn set_max_height(&mut self, height: f32);
fn set_min_width(&mut self, width: f32);
fn set_min_height(&mut self, height: f32);
fn set_position(&mut self, position: Position) {
self.set_x(position.x);
self.set_y(position.y);
}
fn set_x(&mut self, x: f32);
fn set_y(&mut self, y: f32);
fn iter(&self) -> LayoutIter<'_>;
fn get(&self, id: GlobalId) -> Option<&dyn Layout> {
self.iter().find(|&layout| layout.id() == id)
}
#[cfg(feature = "ffi")]
fn as_layout_node(&self) -> LayoutNode {
LayoutNode {
id: self.id(),
position: self.position(),
size: self.size(),
}
}
}
mod private {
pub trait Sealed {}
impl Sealed for super::EmptyLayout {}
impl Sealed for super::BlockLayout {}
impl Sealed for super::HorizontalLayout {}
impl Sealed for super::VerticalLayout {}
}
pub struct LayoutIter<'a> {
stack: Vec<&'a dyn Layout>,
}
impl<'a> Iterator for LayoutIter<'a> {
type Item = &'a dyn Layout;
fn next(&mut self) -> Option<Self::Item> {
if let Some(layout) = self.stack.pop() {
let children = layout.children();
let m = children.iter().map(|child| child.as_ref());
self.stack.extend(m.rev());
return Some(layout);
}
None
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn root_max_width() {
let mut layout = EmptyLayout::new()
.max_width(20.0)
.intrinsic_size(IntrinsicSize::fill());
solve_layout(&mut layout, Size::unit(200.0));
assert_eq!(layout.size().width, 20.0);
}
}