cotis-layout 0.1.0-alpha.1

Flexbox-style layout engine for Cotis
Documentation
use crate::layout_algorithm::axis_utils::Axis;
use crate::layout_struct::layout_states::ChildWrapping;
use crate::layout_struct::layout_tree::{LayoutTreeCursor, LayoutTreeNodeLocal};

pub fn recursive_wrap_left_to_right(local: &mut LayoutTreeCursor) -> bool {
    let mut subtree_dirty = false;
    for child in local.get_all_child_ids() {
        subtree_dirty |= recursive_wrap_left_to_right(&mut local.get_child_cursor(child).unwrap());
    }
    let mut node = local.get_local();
    let is_text_wrap = node.self_element.config.layout.wrapping == ChildWrapping::Text;
    if is_text_wrap {
        wrap_elements_left_to_right(&mut node);
        subtree_dirty = true;
    } else if subtree_dirty {
        node.recalculate_min_height_from_children();
    }
    subtree_dirty
}

pub fn wrap_elements_left_to_right(local_node: &mut LayoutTreeNodeLocal) {
    let padding = &local_node.self_element.config.layout.padding;
    let total_width = local_node
        .self_element
        .info
        .get_final_width()
        .expect("Element must have final width at this point")
        - padding.left
        - padding.right;
    let gap = local_node.self_element.config.layout.child_gap;

    let mut finished_line_heights: Vec<f32> = Vec::new();
    let mut row_width = 0.0f32;
    let mut row_max_height = 0.0f32;
    let mut has_in_flow_child = false;
    let mut started = false;

    for child in &local_node.children_elements {
        if child.config.floating.config.is_some() {
            continue;
        }
        has_in_flow_child = true;
        let width = child
            .info
            .get_final_axis(Axis::Width)
            .expect("Element must have final width at this point");
        let height = child
            .info
            .min_axis(Axis::Height)
            .expect("Element must have a minimum height at this point");

        if !started {
            row_width = width;
            row_max_height = height;
            started = true;
        } else if row_width + gap + width > total_width {
            finished_line_heights.push(row_max_height);
            row_width = width;
            row_max_height = height;
        } else {
            row_width += gap + width;
            row_max_height = row_max_height.max(height);
        }
    }

    let lines_inner_height = if has_in_flow_child {
        let between_lines = gap * finished_line_heights.len() as f32;
        finished_line_heights.iter().sum::<f32>() + row_max_height + between_lines
    } else {
        0.0
    };

    let total_height = lines_inner_height + padding.top + padding.bottom;
    local_node.self_element.info.set_min_height(total_height);
    // Keep preferred in sync with wrapped height: `temp_height` drives free-space and parent
    // aggregation before `upgrade_height`; without this, only `min_height` reflects wrapping.
    local_node.self_element.info.set_tmp_height(total_height);
}