cotis-layout 0.1.0-alpha.1

Flexbox-style layout engine for Cotis
Documentation
use crate::layout_struct::layout_states::LayoutElement;
use crate::layout_struct::layout_tree::{LayoutTree, LayoutTreeCursorBorrow, LayoutTreeIndex};
use crate::layout_struct::{
    RenderCommandClipEnd, RenderCommandClipStart, RenderCommandOutput, RenderCommandOutputElement,
    TextDrawPayload,
};
use cotis::utils::ElementId;
use std::collections::{HashMap, VecDeque};

pub fn calculate_render_commands<Config>(
    tree: &'_ LayoutTree,
    current_tree_index: LayoutTreeIndex,
    open_nodes: &mut VecDeque<LayoutTreeIndex>,
    render_commands: &mut Vec<RenderCommandOutput<Config>>,
    configs: &mut HashMap<ElementId, Config>,
    text_fragments: &HashMap<ElementId, TextDrawPayload>,
) {
    let self_node = LayoutTreeCursorBorrow::new_from_index(tree, &current_tree_index).unwrap();
    let self_node = self_node.get_local();
    let is_layout_root_node = current_tree_index.len() == 1;
    if !is_layout_root_node {
        render_commands.push(self_node.self_element.to_commands(configs, text_fragments));
    }
    if self_node.self_element.config.clip.horizontal || self_node.self_element.config.clip.vertical
    {
        let mut render_commands_inside = Vec::new();
        let mut open_nodes_inside = VecDeque::new();

        render_commands.push(RenderCommandOutput::ClipStart(RenderCommandClipStart {
            bounds: self_node.self_element.info.to_bounding_box(),
            id: self_node.self_element.id,
            z_index: self_node.self_element.config.floating.z_index,
        }));

        for node in self_node.children_elements.iter() {
            let mut index = current_tree_index.clone();
            index.push(node.id);
            open_nodes_inside.push_back(index);
        }
        while let Some(node) = open_nodes_inside.pop_front() {
            calculate_render_commands(
                tree,
                node,
                &mut open_nodes_inside,
                &mut render_commands_inside,
                configs,
                text_fragments,
            );
        }
        sort_commands(
            &mut render_commands_inside,
            self_node.self_element.config.floating.z_index,
        );
        render_commands.append(&mut render_commands_inside);

        render_commands.push(RenderCommandOutput::ClipEnd(RenderCommandClipEnd {
            bounds: self_node.self_element.info.to_bounding_box(),
            id: self_node.self_element.id,
            z_index: self_node.self_element.config.floating.z_index,
        }));
    } else {
        for node in self_node.children_elements.iter() {
            let mut index = current_tree_index.clone();
            index.push(node.id);
            open_nodes.push_back(index);
        }
    }
}

pub(crate) fn sort_commands<Config>(
    render_commands: &mut [RenderCommandOutput<Config>],
    z_index: i32,
) {
    render_commands.sort_by(|a, b| {
        let z_a = match a {
            RenderCommandOutput::Element(a) => a.z_index,
            RenderCommandOutput::ClipStart(a) => a.z_index,
            RenderCommandOutput::ClipEnd(a) => a.z_index,
        };
        let z_b = match b {
            RenderCommandOutput::Element(a) => a.z_index,
            RenderCommandOutput::ClipStart(a) => a.z_index,
            RenderCommandOutput::ClipEnd(a) => a.z_index,
        };

        z_a.cmp(&z_b)
    });
    for command in render_commands.iter_mut() {
        match command {
            RenderCommandOutput::Element(c) => {
                c.z_index = z_index;
            }
            RenderCommandOutput::ClipStart(c) => {
                c.z_index = z_index;
            }
            RenderCommandOutput::ClipEnd(c) => {
                c.z_index = z_index;
            }
        }
    }
}

impl LayoutElement {
    pub fn to_commands<Config>(
        &self,
        configs: &mut HashMap<ElementId, Config>,
        text_fragments: &HashMap<ElementId, TextDrawPayload>,
    ) -> RenderCommandOutput<Config> {
        let text = text_fragments.get(&self.id).cloned();
        RenderCommandOutput::Element(RenderCommandOutputElement {
            bounds: self.info.to_bounding_box(),
            z_index: self.config.floating.z_index,
            text,
            custom: configs
                .remove(&self.id)
                .expect("There should be a config for this element"),
        })
    }
}