cascada 0.3.0

A general purpose UI layout engine
Documentation
use crate::constraints::impl_constraints;
use crate::{
    BoxConstraints, BoxSizing, GlobalId, IntrinsicSize, Layout, LayoutIter, Position, Size,
};

/// An empty [`Layout`] with no child notes.
#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
pub struct EmptyLayout {
    id: GlobalId,
    pub(crate) size: Size,
    position: Position,
    intrinsic_size: IntrinsicSize,
    constraints: BoxConstraints,
    errors: Vec<crate::LayoutError>,
    label: Option<String>,
}

impl EmptyLayout {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn set_id(mut self, id: GlobalId) -> Self {
        self.id = id;
        self
    }

    pub fn with_label(mut self, label: &str) -> Self {
        self.label = Some(label.to_string());
        self
    }

    impl_constraints!();
}

impl Layout for EmptyLayout {
    fn label(&self) -> String {
        self.label.clone().unwrap_or("EmptyLayout".to_string())
    }

    fn solve_min_constraints(&mut self) -> (f32, f32) {
        if let BoxSizing::Fixed(width) = self.intrinsic_size.width {
            self.constraints.min_width = Some(width);
        }

        if let BoxSizing::Fixed(height) = self.intrinsic_size.height {
            self.constraints.min_height = Some(height);
        }

        (
            self.constraints.min_width.unwrap_or_default(),
            self.constraints.min_height.unwrap_or_default(),
        )
    }

    // No children to solve for
    fn solve_max_constraints(&mut self) {}

    fn position_children(&mut self) {}

    fn update_size(&mut self) {
        match self.intrinsic_size.width {
            BoxSizing::Flex(_) => {
                self.size.width = self.constraints.max_width.unwrap_or_default();
            }
            BoxSizing::Shrink => {
                self.size.width = self.constraints.min_width.unwrap_or_default();
            }
            BoxSizing::Fixed(width) => {
                self.size.width = width;
            }
        }

        match self.intrinsic_size.height {
            BoxSizing::Flex(_) => {
                self.size.height = self.constraints.max_height.unwrap_or_default();
            }
            BoxSizing::Shrink => {
                self.size.height = self.constraints.min_height.unwrap_or_default();
            }
            BoxSizing::Fixed(height) => {
                self.size.height = height;
            }
        }
    }

    fn collect_errors(&mut self) -> Vec<crate::LayoutError> {
        self.errors.drain(..).collect::<Vec<_>>()
    }

    fn id(&self) -> GlobalId {
        self.id
    }

    fn constraints(&self) -> BoxConstraints {
        self.constraints
    }

    fn get_intrinsic_size(&self) -> IntrinsicSize {
        self.intrinsic_size
    }

    fn size(&self) -> Size {
        self.size
    }

    fn position(&self) -> Position {
        self.position
    }

    fn children(&self) -> &[Box<dyn Layout>] {
        &[]
    }

    fn set_max_width(&mut self, width: f32) {
        self.constraints.max_width = Some(width);
    }

    fn set_max_height(&mut self, height: f32) {
        self.constraints.max_height = Some(height);
    }

    fn set_min_width(&mut self, width: f32) {
        self.constraints.min_width = Some(width);
    }

    fn set_min_height(&mut self, height: f32) {
        self.constraints.min_height = Some(height);
    }

    fn set_position(&mut self, position: Position) {
        self.position = position;
    }

    fn set_x(&mut self, x: f32) {
        self.position.x = x;
    }

    fn set_y(&mut self, y: f32) {
        self.position.y = y;
    }

    fn iter(&self) -> LayoutIter<'_> {
        LayoutIter { stack: vec![self] }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::solve_layout;

    #[test]
    fn flex_sizing() {
        let window = Size::new(800.0, 800.0);
        let mut root = EmptyLayout::new();

        root.intrinsic_size.width = BoxSizing::Flex(2);
        root.intrinsic_size.height = BoxSizing::Flex(2);

        solve_layout(&mut root, window);

        assert_eq!(root.size(), window);
    }

    #[test]
    fn fixed_min_width_precedence() {
        let mut root = EmptyLayout::new()
            .intrinsic_size(IntrinsicSize::fixed(30.0, 50.0))
            .min_width(20.0);
        let (width, _) = root.solve_min_constraints();
        assert_eq!(width, 30.0);
    }

    #[test]
    fn min_width() {
        let mut root = EmptyLayout::new().min_width(20.0);
        let (width, _) = root.solve_min_constraints();
        assert_eq!(width, 20.0);
    }

    #[test]
    fn min_height() {
        let mut root = EmptyLayout::new().min_height(20.0);
        let (_, height) = root.solve_min_constraints();
        assert_eq!(height, 20.0);
    }

    #[test]
    fn fixed_sizing() {
        let window = Size::new(800.0, 800.0);
        let mut root = EmptyLayout::new();

        root.intrinsic_size.width = BoxSizing::Fixed(200.0);
        root.intrinsic_size.height = BoxSizing::Fixed(125.0);

        solve_layout(&mut root, window);

        assert_eq!(root.size(), Size::new(200.0, 125.0));
    }

    #[test]
    fn shrink_sizing() {
        let window = Size::new(800.0, 800.0);
        let mut root = EmptyLayout::new();

        solve_layout(&mut root, window);

        assert_eq!(root.size(), Size::default());
    }
}