Layout Engine

Flexbox-inspired layout system.

Overview

Constraints → Measure → Layout → Paint
    ↓            ↓         ↓        ↓
  bounds     sizes   positions  commands

Constraints System

pub struct Constraints {
    pub min_width: f32,
    pub max_width: f32,
    pub min_height: f32,
    pub max_height: f32,
}

Measure Phase

Bottom-up size computation:

fn measure(&self, constraints: Constraints) -> Size {
    // Children measure first
    let child_sizes: Vec<Size> = self.children()
        .iter()
        .map(|c| c.measure(constraints))
        .collect();

    // Parent uses child sizes
    let width = child_sizes.iter().map(|s| s.width).sum();
    constraints.constrain(Size::new(width, 50.0))
}

Layout Phase

Top-down positioning:

fn layout(&mut self, bounds: Rect) -> LayoutResult {
    let mut x = bounds.x;

    for child in self.children_mut() {
        let child_bounds = Rect::new(x, bounds.y, 100.0, bounds.height);
        child.layout(child_bounds);
        x += 100.0;
    }

    LayoutResult { size: bounds.size() }
}

Main/Cross Axis

LayoutMain AxisCross Axis
RowHorizontalVertical
ColumnVerticalHorizontal

Alignment

// Main axis: distribute along direction
MainAxisAlignment::Start
MainAxisAlignment::Center
MainAxisAlignment::SpaceBetween

// Cross axis: perpendicular alignment
CrossAxisAlignment::Start
CrossAxisAlignment::Center
CrossAxisAlignment::Stretch

Verified Test

#[test]
fn test_layout_engine() {
    use presentar_widgets::Column;
    use presentar_core::{Constraints, Size, Rect, Widget};

    let mut col = Column::new();
    col.measure(Constraints::loose(Size::new(400.0, 300.0)));
    col.layout(Rect::new(0.0, 0.0, 400.0, 300.0));
}