1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#![doc=include_str!("../README.md")]
#![deny(clippy::missing_const_for_fn)]

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

use layout::Vec2;

pub mod align;
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)
    }
}