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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! Structs to help convert between a relative and absolute direction.
use std::{fmt, ops, str::FromStr};

#[cfg(feature = "reflect")]
use bevy::prelude::Reflect;

/// A synonymous for [`Flow`].
pub type Axis = Flow;

/// A `T` that applies to the `width` and `height` of something.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
pub struct Size<T> {
    /// `T` on the horizontal axis.
    pub width: T,
    /// `T` on the vertical axis.
    pub height: T,
}

/// Similar to [`Size`], but relative to a [`Flow`] direction.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
pub struct Oriented<T> {
    /// `T` on the same axis as the [`Flow`].
    pub main: T,
    /// `T` on the perpendicular axis of the [`Flow`].
    pub cross: T,
}

/// The layout direction of a [`Container`].
///
/// [`Container`]: crate::Container
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
pub enum Flow {
    /// Children are arranged on the horizontal axis. May also be "width".
    #[default]
    Horizontal,

    /// Children are arranged on the vertical axis. May also be "height".
    Vertical,
}
impl Flow {
    /// Returns [`Size`] oriented according to this orientation.
    ///
    /// This is the inverse of [`Flow::absolute`].
    pub const fn relative<T: Copy>(self, Size { width, height }: Size<T>) -> Oriented<T> {
        let Size { width: main, height: cross } = self.absolute(Oriented::new(width, height));
        Oriented { main, cross }
    }
    /// Returns [`Oriented`] in oriented according to the global point of view.
    ///
    /// This is the inverse of [`Flow::relative`].
    pub const fn absolute<T: Copy>(self, Oriented { main, cross }: Oriented<T>) -> Size<T> {
        match self {
            Self::Horizontal => Size::new(main, cross),
            Self::Vertical => Size::new(cross, main),
        }
    }
}
impl Size<f32> {
    /// A `Size<f32>` with 0 width and 0 height.
    pub const ZERO: Self = Self { width: 0., height: 0. };
}
impl ops::Add for Size<f32> {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        Self {
            width: self.width + other.width,
            height: self.height + other.height,
        }
    }
}

impl<T> Size<T> {
    /// Create a [`Size`] for given `width` and `height` `T`.
    pub const fn new(width: T, height: T) -> Self {
        Self { width, height }
    }
    /// Create a [`Size`] where `width` and `height` are set to `value`.
    pub fn all(value: T) -> Self
    where
        T: Clone,
    {
        Self { width: value.clone(), height: value }
    }
    /// Apply `f` on `width` and `height`, returning a `Size` with the output
    /// values of `f`.
    pub fn map<U>(self, mut f: impl FnMut(T) -> U) -> Size<U> {
        Size { width: f(self.width), height: f(self.height) }
    }
    /// Convert the content of this `Size`
    pub fn map_into<U: From<T>>(self) -> Size<U> {
        self.map(Into::into)
    }
    /// Go from `&Size<T>` to `Size<&T>`.
    pub const fn as_ref(&self) -> Size<&T> {
        let Self { width, height } = self;
        Size { width, height }
    }
}

impl<T: Copy> Oriented<T> {
    /// Create an [`Oriented`] for given `main` and `cross` `T`.
    pub const fn new(main: T, cross: T) -> Self {
        Self { main, cross }
    }
    /// Go from `&Oriented<T>` to `Oriented<&T>`.
    pub const fn as_ref(&self) -> Oriented<&T> {
        let Self { main, cross } = self;
        Oriented { main, cross }
    }
}

impl fmt::Display for Flow {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Horizontal => f.write_str("width"),
            Self::Vertical => f.write_str("height"),
        }
    }
}
impl FromStr for Flow {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "v" => Ok(Self::Vertical),
            ">" => Ok(Self::Horizontal),
            _ => Err(()),
        }
    }
}

impl From<bevy::math::Vec2> for Size<f32> {
    fn from(value: bevy::math::Vec2) -> Self {
        Self::new(value.x, value.y)
    }
}
impl From<Size<f32>> for bevy::math::Vec2 {
    fn from(value: Size<f32>) -> Self {
        Self::new(value.width, value.height)
    }
}
impl<T: fmt::Display> fmt::Display for Size<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}×{}", self.width, self.height)
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn relative() {
        let Oriented { main: main_v, cross: cross_v } =
            Flow::Vertical.relative(Size::new("width", "height"));
        let Oriented { main: main_h, cross: cross_h } =
            Flow::Horizontal.relative(Size::new("width", "height"));

        assert_eq!(main_v, cross_h);
        assert_eq!(main_h, cross_v);
    }
    #[test]
    fn absolute() {
        let Size { width: width_v, height: height_v } =
            Flow::Vertical.absolute(Oriented::new("main", "cross"));
        let Size { width: width_h, height: height_h } =
            Flow::Horizontal.absolute(Oriented::new("main", "cross"));

        assert_eq!(width_v, height_h);
        assert_eq!(width_h, height_v);
    }
}