cotis-layout 0.1.0-alpha.1

Flexbox-style layout engine for Cotis
Documentation
use crate::layout_struct::layout_states::{LayoutElement, LayoutElementInfo, element_info_states};
use cotis_defaults::element_configs::style::sizing::{AxisSizing, DoubleAxisSizing, Sizing};
use cotis_defaults::element_configs::style::types::LayoutDirection;

#[derive(Debug, Clone, PartialEq, Copy)]
pub(crate) enum Axis {
    Width,
    Height,
}

impl Axis {
    pub fn get_direction(&self) -> LayoutDirection {
        match self {
            Axis::Width => LayoutDirection::LeftToRight,
            Axis::Height => LayoutDirection::TopToBottom,
        }
    }

    pub(crate) fn axis_sizing(self, sizing: &DoubleAxisSizing) -> AxisSizing {
        match self {
            Axis::Width => sizing.width,
            Axis::Height => sizing.height,
        }
    }
}

impl LayoutElementInfo {
    pub(crate) fn temp_axis(&self, axis: Axis) -> Option<f32> {
        match axis {
            Axis::Width => self.temp_width(),
            Axis::Height => self.temp_height(),
        }
    }

    pub(crate) fn min_axis(&self, axis: Axis) -> Option<f32> {
        match axis {
            Axis::Width => self.min_width(),
            Axis::Height => self.min_height(),
        }
    }

    pub(crate) fn get_final_axis(&self, axis: Axis) -> Option<f32> {
        match axis {
            Axis::Width => self.get_final_width(),
            Axis::Height => self.get_final_height(),
        }
    }
}

impl LayoutElement {
    pub(crate) fn internal_space_axis(&self, axis: Axis) -> f32 {
        match axis {
            Axis::Width => self.internal_space_width(),
            Axis::Height => self.internal_space_height(),
        }
    }

    pub(crate) fn wants_to_grow_axis(&self, axis: Axis) -> bool {
        if self.config.floating.config.is_some() {
            return false;
        }
        match self.config.layout.sizing {
            Sizing::DoubleAxis(ref sizing) => match axis.axis_sizing(sizing) {
                AxisSizing::Grow(_, max) => self.info.temp_axis(axis).unwrap_or(max) < max,
                _ => false,
            },
        }
    }

    pub(crate) fn can_shrink_axis(&self, axis: Axis) -> bool {
        if self.config.floating.config.is_some() {
            return false;
        }
        match self.config.layout.sizing {
            Sizing::DoubleAxis(ref sizing) => {
                if matches!(axis.axis_sizing(sizing), AxisSizing::Percent(_)) {
                    return false;
                }
            }
        }
        match (self.info.temp_axis(axis), self.info.min_axis(axis)) {
            (Some(temp), Some(min)) => temp > min,
            _ => false,
        }
    }

    fn is_percent_sizing_for_axis(&self, axis: Axis) -> Option<f32> {
        match self.config.layout.sizing {
            Sizing::DoubleAxis(ref axis_sizing) => match axis.axis_sizing(axis_sizing) {
                AxisSizing::Percent(p) => Some(p),
                _ => None,
            },
        }
    }

    fn resolve_floating_axis_size(&self, total_outer: f32, total_inner: f32, axis: Axis) -> f32 {
        let ex = self.config.floating.config.unwrap().expand;
        if ex.width != 0. && ex.height == 0. {
            match axis {
                Axis::Width => ex.width,
                Axis::Height => ex.height,
            }
        } else {
            let Sizing::DoubleAxis(ref sizing) = self.config.layout.sizing;
            let temp = self.info.temp_axis(axis).unwrap_or(0.);
            match axis.axis_sizing(sizing) {
                AxisSizing::Fit(_, max) => temp.min(max),
                AxisSizing::Grow(_, max) => temp.max(total_inner).min(max),
                AxisSizing::Fixed(fix) => fix,
                AxisSizing::Percent(per) => total_outer * per,
                AxisSizing::InnerPercent(per) => total_inner * per,
            }
        }
    }

    pub(crate) fn set_floating_for_axis(&mut self, total_outer: f32, total_inner: f32, axis: Axis) {
        if self.config.floating.config.is_none() {
            return;
        }
        let dim = self.resolve_floating_axis_size(total_outer, total_inner, axis);
        match axis {
            Axis::Width => match &mut self.info {
                LayoutElementInfo::SelfMinSize(old) => {
                    self.info = LayoutElementInfo::TotalSizeWidthMinHeight(
                        element_info_states::TotalSizeWidthMinHeight {
                            width: dim,
                            min_height: old.min_size_height,
                            preferred_size_height: old.preferred_size_height,
                        },
                    )
                }
                LayoutElementInfo::TotalMinSize(old) => {
                    self.info = LayoutElementInfo::TotalSizeWidthMinHeight(
                        element_info_states::TotalSizeWidthMinHeight {
                            width: dim,
                            min_height: old.min_size_height,
                            preferred_size_height: old.preferred_size_height,
                        },
                    )
                }
                _ => panic!("Tried to set percent width to final width"),
            },
            Axis::Height => match &mut self.info {
                LayoutElementInfo::TotalSizeWidthMinHeight(old) => {
                    self.info = LayoutElementInfo::TotalSize(element_info_states::TotalSize {
                        width: old.width,
                        height: dim,
                    })
                }
                LayoutElementInfo::TextWrappedMinHeight(old) => {
                    self.info = LayoutElementInfo::TotalSize(element_info_states::TotalSize {
                        width: old.width,
                        height: dim,
                    })
                }
                _ => panic!("Tried to set percent width to final height or none final width"),
            },
        }
    }

    pub(crate) fn set_percent_sizing_for_axis(&mut self, total: f32, axis: Axis) {
        let Some(percent) = self.is_percent_sizing_for_axis(axis) else {
            return;
        };
        let dim = percent * total;
        match axis {
            Axis::Width => match &mut self.info {
                LayoutElementInfo::SelfMinSize(old) => {
                    self.info = LayoutElementInfo::TotalSizeWidthMinHeight(
                        element_info_states::TotalSizeWidthMinHeight {
                            width: dim,
                            min_height: old.min_size_height,
                            preferred_size_height: old.preferred_size_height,
                        },
                    )
                }
                LayoutElementInfo::TotalMinSize(old) => {
                    self.info = LayoutElementInfo::TotalSizeWidthMinHeight(
                        element_info_states::TotalSizeWidthMinHeight {
                            width: dim,
                            min_height: old.min_size_height,
                            preferred_size_height: old.preferred_size_height,
                        },
                    )
                }
                _ => panic!("Tried to set percent width to final width"),
            },
            Axis::Height => match &mut self.info {
                LayoutElementInfo::TotalSizeWidthMinHeight(old) => {
                    self.info = LayoutElementInfo::TotalSize(element_info_states::TotalSize {
                        width: old.width,
                        height: dim,
                    })
                }
                LayoutElementInfo::TextWrappedMinHeight(old) => {
                    self.info = LayoutElementInfo::TotalSize(element_info_states::TotalSize {
                        width: old.width,
                        height: dim,
                    })
                }
                _ => panic!("Tried to set percent width to final height or none final width"),
            },
        }
    }

    pub(crate) fn grow_axis_at_most(&mut self, size: f32, axis: Axis) {
        let Sizing::DoubleAxis(ref sizing) = self.config.layout.sizing;
        if let AxisSizing::Grow(_, max) = axis.axis_sizing(sizing) {
            let cur = self.info.temp_axis(axis).unwrap_or(0.);
            let next = (cur + size).min(max);
            match axis {
                Axis::Width => self.info.set_tmp_width(next),
                Axis::Height => self.info.set_tmp_height(next),
            }
        }
    }

    pub(crate) fn grow_axis_to(&mut self, size: f32, axis: Axis) {
        let Sizing::DoubleAxis(ref sizing) = self.config.layout.sizing;
        if let AxisSizing::Grow(_, max) = axis.axis_sizing(sizing) {
            let next = size.min(max);
            match axis {
                Axis::Width => self.info.set_tmp_width(next),
                Axis::Height => self.info.set_tmp_height(next),
            }
        }
    }

    pub(crate) fn shrink_axis_at_most(&mut self, size: f32, axis: Axis) {
        match axis {
            Axis::Width => self
                .info
                .set_tmp_width(self.info.temp_width().unwrap_or(0.) - size),
            Axis::Height => self
                .info
                .set_tmp_height(self.info.temp_height().unwrap_or(0.) - size),
        }
    }

    pub(crate) fn shrink_axis_to(&mut self, size: f32, axis: Axis) {
        let Sizing::DoubleAxis(ref sizing) = self.config.layout.sizing;
        if let AxisSizing::Grow(min, max) = axis.axis_sizing(sizing) {
            let next = size.max(min).min(max);
            match axis {
                Axis::Width => self.info.set_tmp_width(next),
                Axis::Height => self.info.set_tmp_height(next),
            }
        }
        if let AxisSizing::Fit(min, max) = axis.axis_sizing(sizing) {
            let next = size.max(min).min(max);
            match axis {
                Axis::Width => self.info.set_tmp_width(next),
                Axis::Height => self.info.set_tmp_height(next),
            }
        }
    }

    pub(crate) fn upgrade_axis(&mut self, axis: Axis) {
        match axis {
            Axis::Width => self.info.upgrade_width(),
            Axis::Height => self.info.upgrade_height(),
        }
    }
}