layout_engine 0.7.0

A small project to mimic css flexbox and css grid
Documentation
#![doc=include_str!("../README.md")]
#![deny(clippy::missing_const_for_fn)]

use std::ops::{Add, Not};

use layout::Vec2;

pub mod align;
#[cfg(feature = "either")]
mod either;
pub mod flexbox;
pub mod grid;
pub mod layout;
pub use align::Alignment;

#[derive(Debug, Clone, Copy, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Orientation {
    Vertical,
    Horizontal,
}

impl Not for Orientation {
    type Output = Self;

    fn not(self) -> Self::Output {
        match self {
            Orientation::Vertical => Orientation::Horizontal,
            Orientation::Horizontal => Orientation::Vertical,
        }
    }
}

/// A struct containing the minimum layout of a
/// widget alongisde the naturual layout.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MinimumNatural<T> {
    /// The absolute minimum this widget can have
    pub minimum: T,
    /// The naturual size for this widget
    pub natural: T,
}

impl<T: Clone> MinimumNatural<T> {
    /// Creates a new [`Self`] using the same minimum and naturual sizing
    pub fn same(t: T) -> Self {
        Self {
            minimum: t.clone(),
            natural: t,
        }
    }
}

impl<T: Add<U, Output = P>, U, P> Add<MinimumNatural<U>> for MinimumNatural<T> {
    type Output = MinimumNatural<P>;

    fn add(self, other: MinimumNatural<U>) -> MinimumNatural<P> {
        MinimumNatural {
            minimum: self.minimum + other.minimum,
            natural: self.natural + other.natural,
        }
    }
}

/// A layout trait
///
/// It uses height to width management like
/// [Gtk](https://docs.gtk.org/gtk4/class.Widget.html#height-for-width-geometry-management)
pub trait Layout {
    /// Given a height, how much width would this like to have at
    /// minimum.
    fn width_for_height(&self, height: usize) -> MinimumNatural<usize>;
    /// Given a width, how much height would this like to have at
    /// minimum.
    fn height_for_width(&self, width: usize) -> MinimumNatural<usize>;

    /// What would this widget like to have in an ideal world.
    // TODO: Make distinction between x minimum and y minimum
    fn prefered_size(&self) -> MinimumNatural<Vec2>;

    /// How would this widget choose to allocate itself given the parent's size
    fn prefered_size_of_container(
        &self,
        container: Vec2,
    ) -> MinimumNatural<Vec2>;
}

pub trait LayoutExt: Layout {
    /// Gets the sub direction from the primary direction given the orientation
    ///
    /// That means if:
    ///
    ///  - [`Orientation::Horizontal`] this acts like [`Layout::height_for_width`]
    ///  - [`Orientation::Vertical`] this acts like [`Layout::width_for_height`]
    fn sub_for_prim(
        &self,
        prim: usize,
        orientation: Orientation,
    ) -> MinimumNatural<usize> {
        match orientation {
            Orientation::Vertical => self.width_for_height(prim),
            Orientation::Horizontal => self.height_for_width(prim),
        }
    }
}

impl<T> LayoutExt for T where T: Layout {}

/// Provides a default `prefered_size_of_container` implementation of sorts.
///
/// It will try and 'squash' the widget into the container.
/// Prior to `0.6` this was the default for `prefered_size_of_container`
pub fn squash<L: Layout>(l: &L, container: Vec2) -> MinimumNatural<Vec2> {
    let mut size = l.prefered_size();

    // Compute the naturual size
    if size.natural.x > container.x {
        size.natural.x = container.x;
        size.natural.y = l.height_for_width(size.natural.x).natural;
    }
    if size.natural.y > container.y {
        size.natural.y = container.y;
        size.natural.x = l.width_for_height(size.natural.y).natural;
    }

    // Compute the minimum size
    let mut size = l.prefered_size();
    if size.minimum.x > container.x {
        size.minimum.x = container.x;
        size.minimum.y = l.height_for_width(size.minimum.x).minimum;
    }
    if size.minimum.y > container.y {
        size.minimum.y = container.y;
        size.minimum.x = l.width_for_height(size.minimum.y).minimum;
    }

    size
}

impl<T: Layout> Layout for &T {
    fn width_for_height(&self, height: usize) -> MinimumNatural<usize> {
        (*self).width_for_height(height)
    }

    fn height_for_width(&self, width: usize) -> MinimumNatural<usize> {
        (*self).height_for_width(width)
    }

    fn prefered_size(&self) -> MinimumNatural<Vec2> {
        (*self).prefered_size()
    }

    fn prefered_size_of_container(
        &self,
        container: Vec2,
    ) -> MinimumNatural<Vec2> {
        (*self).prefered_size_of_container(container)
    }
}