glyph_ui 0.1.0

TUI library utilizing the Elm architecture
Documentation
use euclid::{Rect, Size2D};

use crate::{unit::Cell, View as ViewTrait};

use super::{Column, Row, View};

// THIS TRAIT IS SEMVER EXEMPT AND IS INTENDED FOR INTERNAL USE ONLY.
//
// If you're looking at this trait and wondering what "main" and "cross" axes
// are, this link is for you:
//
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#the_two_axes_of_flexbox
//
// The purpose of this trait is to separate the important logic from the axes of
// operation, so that layouting bugfixes will trivially apply to both row and
// column layouts.
#[doc(hidden)]
pub trait Flex<T, M>: private::Sealed {
    fn axes_from_size<D>(size: Size2D<D, Cell>) -> (D, D)
    where
        D: Copy;
    fn axes_to_size<D>(main: D, cross: D) -> Size2D<D, Cell>
    where
        D: Copy;

    fn inner_main_axis<V>(inner: &V) -> Size2D<u16, Cell>
    where
        V: ViewTrait<T, M>;

    fn width(&self) -> Size2D<u16, Cell>;
    fn height(&self) -> Size2D<u16, Cell>;

    fn new_element_rect(
        main_axis_start: u16,
        element_main_axis: u16,
        printer_cross_axis: u16,
    ) -> Rect<u16, Cell>;
}

mod private {
    use super::View;

    pub trait Sealed {}

    impl<T, M, D> Sealed for View<'_, T, M, D> {}
}

impl<T, M> Flex<T, M> for View<'_, T, M, Row> {
    fn axes_from_size<D>(size: Size2D<D, Cell>) -> (D, D)
    where
        D: Copy,
    {
        size.to_tuple()
    }

    fn axes_to_size<D>(main: D, cross: D) -> Size2D<D, Cell>
    where
        D: Copy,
    {
        (main, cross).into()
    }

    fn inner_main_axis<V>(inner: &V) -> Size2D<u16, Cell>
    where
        V: ViewTrait<T, M>,
    {
        inner.width()
    }

    fn width(&self) -> Size2D<u16, Cell> {
        let width = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.width().width)
            .sum();

        let height = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.width().height)
            .max()
            .unwrap_or(0);

        (width, height).into()
    }

    fn height(&self) -> Size2D<u16, Cell> {
        let width = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.width().width)
            .sum();

        let height = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.width().height)
            .max()
            .unwrap_or(0);

        (width, height).into()
    }

    fn new_element_rect(
        main_axis_start: u16,
        element_main_axis: u16,
        printer_cross_axis: u16,
    ) -> Rect<u16, Cell> {
        Rect::new(
            (main_axis_start, 0).into(),
            (element_main_axis, printer_cross_axis).into(),
        )
    }
}

impl<T, M> Flex<T, M> for View<'_, T, M, Column> {
    fn axes_from_size<D>(size: Size2D<D, Cell>) -> (D, D)
    where
        D: Copy,
    {
        let (cross, main) = size.to_tuple();
        (main, cross)
    }

    fn axes_to_size<D>(main: D, cross: D) -> Size2D<D, Cell>
    where
        D: Copy,
    {
        (cross, main).into()
    }

    fn inner_main_axis<V>(inner: &V) -> Size2D<u16, Cell>
    where
        V: ViewTrait<T, M>,
    {
        inner.height()
    }

    fn width(&self) -> Size2D<u16, Cell> {
        let width = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.height().width)
            .max()
            .unwrap_or(0);

        let height = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.height().height)
            .sum();

        (width, height).into()
    }

    fn height(&self) -> Size2D<u16, Cell> {
        let width = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.height().width)
            .max()
            .unwrap_or(0);

        let height = self
            .inners
            .iter()
            .map(|(inner, _layout)| inner.height().height)
            .sum();

        (width, height).into()
    }

    fn new_element_rect(
        main_axis_start: u16,
        element_main_axis: u16,
        printer_cross_axis: u16,
    ) -> Rect<u16, Cell> {
        Rect::new(
            (0, main_axis_start).into(),
            (printer_cross_axis, element_main_axis).into(),
        )
    }
}