1use crate::core::Rect;
4use crate::layout::{DesiredSize, Layout, SizeConstraints};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum Align {
9 Row,
11 Column,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum JustifyContent {
18 Start,
19 End,
20 Center,
21 SpaceBetween,
22 SpaceAround,
23 SpaceEvenly,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum CrossAlign {
29 Start,
30 End,
31 Center,
32 Stretch,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum FlexWrap {
38 NoWrap,
39 Wrap,
40}
41
42#[derive(Debug, Clone, Copy)]
44pub struct FlexItem {
45 pub grow: f32,
46 pub shrink: f32,
47 pub basis: Option<f32>,
48}
49
50impl Default for FlexItem {
51 fn default() -> Self {
52 Self {
53 grow: 0.0,
54 shrink: 1.0,
55 basis: None,
56 }
57 }
58}
59
60impl FlexItem {
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 pub fn grow(mut self, grow: f32) -> Self {
66 self.grow = grow;
67 self
68 }
69}
70
71pub struct FlexLayout {
73 direction: Align,
74 justify: JustifyContent,
75 cross_align: CrossAlign,
76 wrap: FlexWrap,
77 spacing: f32,
78}
79
80impl FlexLayout {
81 pub fn row() -> Self {
82 Self {
83 direction: Align::Row,
84 justify: JustifyContent::Start,
85 cross_align: CrossAlign::Start,
86 wrap: FlexWrap::NoWrap,
87 spacing: 0.0,
88 }
89 }
90
91 pub fn column() -> Self {
92 Self {
93 direction: Align::Column,
94 justify: JustifyContent::Start,
95 cross_align: CrossAlign::Start,
96 wrap: FlexWrap::NoWrap,
97 spacing: 0.0,
98 }
99 }
100
101 pub fn spacing(mut self, spacing: f32) -> Self {
102 self.spacing = spacing;
103 self
104 }
105
106 pub fn justify(mut self, justify: JustifyContent) -> Self {
107 self.justify = justify;
108 self
109 }
110
111 pub fn cross_align(mut self, cross_align: CrossAlign) -> Self {
112 self.cross_align = cross_align;
113 self
114 }
115
116 pub fn wrap(mut self, wrap: FlexWrap) -> Self {
117 self.wrap = wrap;
118 self
119 }
120}
121
122impl Layout for FlexLayout {
123 fn measure(&self, children: &[DesiredSize], constraints: SizeConstraints) -> DesiredSize {
124 if children.is_empty() {
125 return DesiredSize::zero();
126 }
127 let total_spacing = self.spacing * (children.len() as f32 - 1.0).max(0.0);
128 match self.direction {
129 Align::Row => {
130 let width: f32 = children.iter().map(|c| c.width).sum::<f32>() + total_spacing;
131 let height = children.iter().map(|c| c.height).fold(0.0f32, f32::max);
132 let (w, h) = constraints.clamp(width, height);
133 DesiredSize::new(w, h)
134 }
135 Align::Column => {
136 let width = children.iter().map(|c| c.width).fold(0.0f32, f32::max);
137 let height: f32 = children.iter().map(|c| c.height).sum::<f32>() + total_spacing;
138 let (w, h) = constraints.clamp(width, height);
139 DesiredSize::new(w, h)
140 }
141 }
142 }
143
144 fn arrange(&self, children: &[DesiredSize], area: Rect) -> Vec<Rect> {
145 if children.is_empty() {
146 return vec![];
147 }
148
149 if self.wrap == FlexWrap::Wrap {
150 return self.arrange_wrapped(children, area);
151 }
152
153 let mut rects = Vec::with_capacity(children.len());
154 match self.direction {
155 Align::Row => {
156 let total_child_width: f32 = children.iter().map(|c| c.width).sum();
157 let total_spacing = self.spacing * (children.len() as f32 - 1.0).max(0.0);
158 let remaining = (area.width - total_child_width - total_spacing).max(0.0);
159 let (start_offset, inter_spacing) = self.justify_offsets(remaining, children.len());
160
161 let mut x = area.x + start_offset;
162 for child in children {
163 let y = self.cross_offset(area.y, area.height, child.height);
164 let h = if self.cross_align == CrossAlign::Stretch {
165 area.height
166 } else {
167 child.height
168 };
169 rects.push(Rect::new(x, y, child.width, h));
170 x += child.width + self.spacing + inter_spacing;
171 }
172 }
173 Align::Column => {
174 let total_child_height: f32 = children.iter().map(|c| c.height).sum();
175 let total_spacing = self.spacing * (children.len() as f32 - 1.0).max(0.0);
176 let remaining = (area.height - total_child_height - total_spacing).max(0.0);
177 let (start_offset, inter_spacing) = self.justify_offsets(remaining, children.len());
178
179 let mut y = area.y + start_offset;
180 for child in children {
181 let x = self.cross_offset(area.x, area.width, child.width);
182 let w = if self.cross_align == CrossAlign::Stretch {
183 area.width
184 } else {
185 child.width
186 };
187 rects.push(Rect::new(x, y, w, child.height));
188 y += child.height + self.spacing + inter_spacing;
189 }
190 }
191 }
192 rects
193 }
194}
195
196impl FlexLayout {
197 fn justify_offsets(&self, remaining: f32, count: usize) -> (f32, f32) {
198 match self.justify {
199 JustifyContent::Start => (0.0, 0.0),
200 JustifyContent::End => (remaining, 0.0),
201 JustifyContent::Center => (remaining / 2.0, 0.0),
202 JustifyContent::SpaceBetween => {
203 if count <= 1 {
204 (0.0, 0.0)
205 } else {
206 (0.0, remaining / (count as f32 - 1.0))
207 }
208 }
209 JustifyContent::SpaceAround => {
210 let gap = remaining / count as f32;
211 (gap / 2.0, gap)
212 }
213 JustifyContent::SpaceEvenly => {
214 let gap = remaining / (count as f32 + 1.0);
215 (gap, gap)
216 }
217 }
218 }
219
220 fn cross_offset(&self, area_start: f32, area_size: f32, child_size: f32) -> f32 {
221 match self.cross_align {
222 CrossAlign::Start | CrossAlign::Stretch => area_start,
223 CrossAlign::End => area_start + area_size - child_size,
224 CrossAlign::Center => area_start + (area_size - child_size) / 2.0,
225 }
226 }
227
228 fn arrange_wrapped(&self, children: &[DesiredSize], area: Rect) -> Vec<Rect> {
229 let mut rects = vec![Rect::new(0.0, 0.0, 0.0, 0.0); children.len()];
230
231 match self.direction {
232 Align::Row => {
233 let mut x = area.x;
234 let mut y = area.y;
235 let mut line_height: f32 = 0.0;
236
237 for (i, child) in children.iter().enumerate() {
238 if x + child.width > area.x + area.width && i > 0 {
239 x = area.x;
240 y += line_height + self.spacing;
241 line_height = 0.0;
242 }
243 rects[i] = Rect::new(x, y, child.width, child.height);
244 x += child.width + self.spacing;
245 line_height = line_height.max(child.height);
246 }
247 }
248 Align::Column => {
249 let mut x = area.x;
250 let mut y = area.y;
251 let mut col_width: f32 = 0.0;
252
253 for (i, child) in children.iter().enumerate() {
254 if y + child.height > area.y + area.height && i > 0 {
255 y = area.y;
256 x += col_width + self.spacing;
257 col_width = 0.0;
258 }
259 rects[i] = Rect::new(x, y, child.width, child.height);
260 y += child.height + self.spacing;
261 col_width = col_width.max(child.width);
262 }
263 }
264 }
265 rects
266 }
267}