cuicui_layout/
alignment.rs

1//! enums for main axis and cross axis alignment.
2
3use std::{mem::replace, str::FromStr};
4
5#[cfg(feature = "reflect")]
6use bevy::prelude::Reflect;
7
8use crate::Oriented;
9
10/// The cross axis alignment. Aka alignment.
11///
12/// Note that you can use any alignments with any kind of constraint.
13///
14/// ```text
15///          Direction::Vertical
16///
17///    Start    |   Center    |     End     |
18/// ▕██  ⁞    ▏ | ▕    ██   ▏ | ▕    ⁞  ██▏ |
19/// ▕███████  ▏ | ▕ ███████ ▏ | ▕  ███████▏ |
20/// ▕    ⁞    ▏ | ▕    ⁞    ▏ | ▕    ⁞    ▏ |
21/// ▕███ ⁞    ▏ | ▕   ███   ▏ | ▕    ⁞ ███▏ |
22/// ▕█   ⁞    ▏ | ▕    █    ▏ | ▕    ⁞   █▏ |
23/// ▕███ ⁞    ▏ | ▕   ███   ▏ | ▕    ⁞ ███▏ |
24/// ▕    ⁞    ▏ | ▕    ⁞    ▏ | ▕    ⁞    ▏ |
25/// ▕█   ⁞    ▏ | ▕    █    ▏ | ▕    ⁞   █▏ |
26/// ```
27///
28/// ```text
29///          Direction::Horizontal
30///
31/// |   Start   |  Center   |    End    |
32/// |▁▁▁▁▁▁▁▁▁▁▁|▁▁▁▁▁▁▁▁▁▁▁|▁▁▁▁▁▁▁▁▁▁▁|
33/// |██ ███ █ ██|           |           |
34/// |██ █ █   ██| █         |           |
35/// | █ ███   ██|██ ███   ██| █         |
36/// |⋯█⋯⋯⋯⋯⋯⋯⋯⋯⋯|██⋯█⋯█⋯█⋯██|⋯█⋯⋯⋯⋯⋯⋯⋯⋯⋯|
37/// | █         | █ ███   ██| █ ███   ██|
38/// |           | █         |██ █ █   ██|
39/// |           |           |██ ███ █ ██|
40/// |▔▔▔▔▔▔▔▔▔▔▔|▔▔▔▔▔▔▔▔▔▔▔|▔▔▔▔▔▔▔▔▔▔▔|
41/// ```
42#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
43#[cfg_attr(feature = "reflect", derive(Reflect))]
44pub enum Alignment {
45    /// The items within the container are all aligned to the top or left.
46    ///
47    /// If the container's axis is `Direction::Vertical`, a start alignment
48    /// will align all items to the left.
49    #[default]
50    Start,
51
52    /// The items within the container are all centered on the container's axis.
53    Center,
54
55    /// The items within the container are all aligned to the bottom or right.
56    ///
57    /// If the container's axis is `Direction::Vertical`, an end alignment
58    /// will align all items to the right.
59    End,
60}
61
62/// The main axis alignment. Aka distribution.
63///
64/// The following suposes an [`Alignment::Start`].
65///
66/// ```text
67///          Direction::Vertical
68///
69///    Start    |  FillMain   |     End     |
70/// ▕██  ⁞    ▏ | ▕██  ⁞    ▏ | ▕    ⁞    ▏ |
71/// ▕███████  ▏ | ▕    ⁞    ▏ | ▕    ⁞    ▏ |
72/// ▕███ ⁞    ▏ | ▕███████  ▏ | ▕    ⁞    ▏ |
73/// ▕█   ⁞    ▏ | ▕    ⁞    ▏ | ▕██  ⁞    ▏ |
74/// ▕    ⁞    ▏ | ▕███ ⁞    ▏ | ▕███████  ▏ |
75/// ▕    ⁞    ▏ | ▕    ⁞    ▏ | ▕███ ⁞    ▏ |
76/// ▕    ⁞    ▏ | ▕█   ⁞    ▏ | ▕█   ⁞    ▏ |
77/// ```
78#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
79#[cfg_attr(feature = "reflect", derive(Reflect))]
80#[doc(alias = "justification")]
81pub enum Distribution {
82    /// Items are clumped together at the left/top.
83    #[default]
84    Start,
85
86    /// Items are distributed evenly, with no space left on the sides of the container.
87    FillMain,
88
89    /// Items are clumped together at the right/bottom.
90    End,
91
92    /// Items overlap at the left/top.
93    OverlapStart,
94
95    /// Items overlap centered on the middle of the container.
96    OverlapCenter,
97
98    /// Items overlap at the right/bottom.
99    OverlapEnd,
100}
101
102/// Manage cross alignment.
103#[derive(Clone, Copy, PartialEq, Debug)]
104pub(crate) struct CrossAlign {
105    cross_parent_size: f32,
106    align: Alignment,
107}
108
109impl Alignment {
110    pub(crate) const fn compute(self, parent_size: Oriented<f32>) -> CrossAlign {
111        CrossAlign { cross_parent_size: parent_size.cross, align: self }
112    }
113}
114impl CrossAlign {
115    pub fn offset(self, cross_child_size: f32) -> f32 {
116        match self.align {
117            Alignment::Start => 0.0,
118            Alignment::Center => (self.cross_parent_size - cross_child_size) / 2.0,
119            Alignment::End => self.cross_parent_size - cross_child_size,
120        }
121    }
122}
123
124/// Manage main alignment based on [`Distribution`].
125#[derive(Clone, PartialEq, Debug)]
126pub(crate) struct MainAlign {
127    offset: f32,
128    gap: f32,
129    distrib: Distribution,
130}
131
132impl Distribution {
133    /// Whether children are meant to overlap.
134    #[must_use]
135    pub const fn overlaps(self) -> bool {
136        use Distribution::{OverlapCenter, OverlapEnd, OverlapStart};
137        matches!(self, OverlapStart | OverlapCenter | OverlapEnd)
138    }
139    pub(crate) fn compute(
140        self,
141        main_size: f32,
142        child_main_size: f32,
143        single_child: bool,
144        count: f32,
145    ) -> MainAlign {
146        let (offset, gap) = match self {
147            Self::FillMain if single_child => ((main_size - child_main_size) / 2., 0.),
148            Self::OverlapCenter => (0., main_size / 2.),
149            Self::FillMain => (0., (main_size - child_main_size) / count),
150            Self::Start | Self::OverlapStart => (0., 0.),
151            Self::End => (main_size - child_main_size, 0.),
152            Self::OverlapEnd => (0., main_size),
153        };
154        MainAlign { offset, gap, distrib: self }
155    }
156}
157impl MainAlign {
158    pub fn offset(&mut self, child_size: f32) -> f32 {
159        use Distribution::{End, FillMain, Start};
160        match self.distrib {
161            Start | FillMain | End => {
162                let new_offset = self.offset + child_size + self.gap;
163                replace(&mut self.offset, new_offset)
164            }
165            Distribution::OverlapStart => 0.,
166            Distribution::OverlapCenter => self.gap - child_size / 2.,
167            Distribution::OverlapEnd => self.gap - child_size,
168        }
169    }
170}
171
172impl FromStr for Distribution {
173    type Err = ();
174
175    fn from_str(s: &str) -> Result<Self, Self::Err> {
176        match s {
177            "dS" => Ok(Self::Start),
178            "dE" => Ok(Self::End),
179            "dC" => Ok(Self::FillMain),
180            "oS" => Ok(Self::OverlapStart),
181            "oE" => Ok(Self::OverlapEnd),
182            "oC" => Ok(Self::OverlapCenter),
183            _ => Err(()),
184        }
185    }
186}
187
188impl FromStr for Alignment {
189    type Err = ();
190
191    fn from_str(s: &str) -> Result<Self, Self::Err> {
192        match s {
193            "aS" => Ok(Self::Start),
194            "aE" => Ok(Self::End),
195            "aC" => Ok(Self::Center),
196            _ => Err(()),
197        }
198    }
199}