cosmic/widget/menu/
flex.rs

1// From iced_aw, license MIT
2
3use iced_core::{Widget, widget::Tree};
4use iced_widget::core::{
5    Alignment, Element, Padding, Point, Size,
6    layout::{Limits, Node},
7    renderer,
8};
9
10use crate::widget::RcElementWrapper;
11
12/// The main axis of a flex layout.
13#[derive(Debug)]
14pub enum Axis {
15    /// The horizontal axis
16    Horizontal,
17
18    /// The vertical axis
19    #[allow(dead_code)]
20    Vertical,
21}
22
23impl Axis {
24    /// Gets the main Axis
25    fn main(&self, size: Size) -> f32 {
26        match self {
27            Self::Horizontal => size.width,
28            Self::Vertical => size.height,
29        }
30    }
31
32    /// Gets the cross Axis
33    fn cross(&self, size: Size) -> f32 {
34        match self {
35            Self::Horizontal => size.height,
36            Self::Vertical => size.width,
37        }
38    }
39
40    /// Returns a Packed axis
41    fn pack(&self, main: f32, cross: f32) -> (f32, f32) {
42        match self {
43            Self::Horizontal => (main, cross),
44            Self::Vertical => (cross, main),
45        }
46    }
47}
48
49/// Computes the flex layout with the given axis and limits, applying spacing,
50/// padding and alignment to the items as needed.
51///
52/// It returns a new layout [`Node`].
53pub fn resolve<'a, E, Message, Renderer>(
54    axis: &Axis,
55    renderer: &Renderer,
56    limits: &Limits,
57    padding: Padding,
58    spacing: f32,
59    align_items: Alignment,
60    items: &mut [E],
61    tree: &mut [&mut Tree],
62) -> Node
63where
64    E: std::borrow::BorrowMut<Element<'a, Message, crate::Theme, Renderer>>,
65    Renderer: renderer::Renderer,
66{
67    let limits = limits.shrink(padding);
68    let total_spacing = spacing * items.len().saturating_sub(1) as f32;
69    let max_cross = axis.cross(limits.max());
70
71    let mut fill_sum = 0;
72    let mut cross = axis.cross(limits.min()).max(axis.cross(Size::INFINITE));
73    let mut available = axis.main(limits.max()) - total_spacing;
74
75    let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
76    nodes.resize(items.len(), Node::default());
77
78    if align_items == Alignment::Center {
79        let mut fill_cross = axis.cross(limits.min());
80
81        for (child, tree) in items.iter_mut().zip(tree.iter_mut()) {
82            let child = child.borrow_mut();
83            let c_size = child.as_widget().size();
84            let cross_fill_factor = match axis {
85                Axis::Horizontal => c_size.height,
86                Axis::Vertical => c_size.width,
87            }
88            .fill_factor();
89
90            if cross_fill_factor == 0 {
91                let (max_width, max_height) = axis.pack(available, max_cross);
92
93                let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height));
94
95                let layout = child.as_widget_mut().layout(tree, renderer, &child_limits);
96                let size = layout.size();
97
98                fill_cross = fill_cross.max(axis.cross(size));
99            }
100        }
101
102        cross = fill_cross;
103    }
104
105    for (i, (child, tree)) in items.iter_mut().zip(tree.iter_mut()).enumerate() {
106        let child = child.borrow_mut();
107        let c_size = child.as_widget().size();
108        let fill_factor = match axis {
109            Axis::Horizontal => c_size.width,
110            Axis::Vertical => c_size.height,
111        }
112        .fill_factor();
113
114        if fill_factor == 0 {
115            let (min_width, min_height) = if align_items == Alignment::Center {
116                axis.pack(0.0, cross)
117            } else {
118                axis.pack(0.0, 0.0)
119            };
120
121            let (max_width, max_height) = if align_items == Alignment::Center {
122                axis.pack(available, cross)
123            } else {
124                axis.pack(available, max_cross)
125            };
126
127            let child_limits = Limits::new(
128                Size::new(min_width, min_height),
129                Size::new(max_width, max_height),
130            );
131
132            let layout = child.as_widget_mut().layout(tree, renderer, &child_limits);
133            let size = layout.size();
134
135            available -= axis.main(size);
136
137            if align_items != Alignment::Center {
138                cross = cross.max(axis.cross(size));
139            }
140
141            nodes[i] = layout;
142        } else {
143            fill_sum += fill_factor;
144        }
145    }
146
147    let remaining = available.max(0.0);
148
149    for (i, (child, tree)) in items.iter_mut().zip(tree.iter_mut()).enumerate() {
150        let child = child.borrow_mut();
151        let c_size = child.as_widget().size();
152        let fill_factor = match axis {
153            Axis::Horizontal => c_size.width,
154            Axis::Vertical => c_size.height,
155        }
156        .fill_factor();
157
158        if fill_factor != 0 {
159            let max_main = remaining * f32::from(fill_factor) / f32::from(fill_sum);
160            let min_main = if max_main.is_infinite() {
161                0.0
162            } else {
163                max_main
164            };
165
166            let (min_width, min_height) = if align_items == Alignment::Center {
167                axis.pack(min_main, cross)
168            } else {
169                axis.pack(min_main, axis.cross(limits.min()))
170            };
171
172            let (max_width, max_height) = if align_items == Alignment::Center {
173                axis.pack(max_main, cross)
174            } else {
175                axis.pack(max_main, max_cross)
176            };
177
178            let child_limits = Limits::new(
179                Size::new(min_width, min_height),
180                Size::new(max_width, max_height),
181            );
182
183            let layout = child.as_widget_mut().layout(tree, renderer, &child_limits);
184
185            if align_items != Alignment::Center {
186                cross = cross.max(axis.cross(layout.size()));
187            }
188
189            nodes[i] = layout;
190        }
191    }
192
193    let pad = axis.pack(padding.left, padding.top);
194    let mut main = pad.0;
195
196    for (i, node) in nodes.iter_mut().enumerate() {
197        if i > 0 {
198            main += spacing;
199        }
200
201        let (x, y) = axis.pack(main, pad.1);
202
203        node.move_to_mut(Point::new(x, y));
204
205        match axis {
206            Axis::Horizontal => {
207                node.align_mut(Alignment::Start, align_items, Size::new(0.0, cross))
208            }
209            Axis::Vertical => node.align_mut(align_items, Alignment::Start, Size::new(cross, 0.0)),
210        };
211
212        let size = node.bounds().size();
213
214        main += axis.main(size);
215    }
216
217    let (width, height) = axis.pack(main - pad.0, cross);
218    let size = limits.resolve(width, height, Size::new(width, height));
219
220    Node::with_children(size.expand(padding), nodes)
221}
222
223/// Computes the flex layout with the given axis and limits, applying spacing,
224/// padding and alignment to the items as needed.
225///
226/// It returns a new layout [`Node`].
227pub fn resolve_wrapper<'a, Message>(
228    axis: &Axis,
229    renderer: &crate::Renderer,
230    limits: &Limits,
231    padding: Padding,
232    spacing: f32,
233    align_items: Alignment,
234    items: &mut [&mut RcElementWrapper<Message>],
235    tree: &mut [&mut Tree],
236) -> Node {
237    let limits = limits.shrink(padding);
238    let total_spacing = spacing * items.len().saturating_sub(1) as f32;
239    let max_cross = axis.cross(limits.max());
240
241    let mut fill_sum = 0;
242    let mut cross = axis.cross(limits.min()).max(axis.cross(Size::INFINITE));
243    let mut available = axis.main(limits.max()) - total_spacing;
244
245    let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
246    nodes.resize(items.len(), Node::default());
247
248    if align_items == Alignment::Center {
249        let mut fill_cross = axis.cross(limits.min());
250
251        for (child, tree) in items.into_iter().zip(tree.iter_mut()) {
252            let c_size = child.size();
253            let cross_fill_factor = match axis {
254                Axis::Horizontal => c_size.height,
255                Axis::Vertical => c_size.width,
256            }
257            .fill_factor();
258
259            if cross_fill_factor == 0 {
260                let (max_width, max_height) = axis.pack(available, max_cross);
261
262                let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height));
263
264                let layout = child.layout(tree, renderer, &child_limits);
265                let size = layout.size();
266
267                fill_cross = fill_cross.max(axis.cross(size));
268            }
269        }
270
271        cross = fill_cross;
272    }
273
274    for (i, (child, tree)) in items.into_iter().zip(tree.iter_mut()).enumerate() {
275        let c_size = child.size();
276        let fill_factor = match axis {
277            Axis::Horizontal => c_size.width,
278            Axis::Vertical => c_size.height,
279        }
280        .fill_factor();
281
282        if fill_factor == 0 {
283            let (min_width, min_height) = if align_items == Alignment::Center {
284                axis.pack(0.0, cross)
285            } else {
286                axis.pack(0.0, 0.0)
287            };
288
289            let (max_width, max_height) = if align_items == Alignment::Center {
290                axis.pack(available, cross)
291            } else {
292                axis.pack(available, max_cross)
293            };
294
295            let child_limits = Limits::new(
296                Size::new(min_width, min_height),
297                Size::new(max_width, max_height),
298            );
299
300            let layout = child.layout(tree, renderer, &child_limits);
301            let size = layout.size();
302
303            available -= axis.main(size);
304
305            if align_items != Alignment::Center {
306                cross = cross.max(axis.cross(size));
307            }
308
309            nodes[i] = layout;
310        } else {
311            fill_sum += fill_factor;
312        }
313    }
314
315    let remaining = available.max(0.0);
316
317    for (i, (child, tree)) in items.into_iter().zip(tree.iter_mut()).enumerate() {
318        let c_size = child.size();
319        let fill_factor = match axis {
320            Axis::Horizontal => c_size.width,
321            Axis::Vertical => c_size.height,
322        }
323        .fill_factor();
324
325        if fill_factor != 0 {
326            let max_main = remaining * f32::from(fill_factor) / f32::from(fill_sum);
327            let min_main = if max_main.is_infinite() {
328                0.0
329            } else {
330                max_main
331            };
332
333            let (min_width, min_height) = if align_items == Alignment::Center {
334                axis.pack(min_main, cross)
335            } else {
336                axis.pack(min_main, axis.cross(limits.min()))
337            };
338
339            let (max_width, max_height) = if align_items == Alignment::Center {
340                axis.pack(max_main, cross)
341            } else {
342                axis.pack(max_main, max_cross)
343            };
344
345            let child_limits = Limits::new(
346                Size::new(min_width, min_height),
347                Size::new(max_width, max_height),
348            );
349
350            let layout = child.layout(tree, renderer, &child_limits);
351
352            if align_items != Alignment::Center {
353                cross = cross.max(axis.cross(layout.size()));
354            }
355
356            nodes[i] = layout;
357        }
358    }
359
360    let pad = axis.pack(padding.left, padding.top);
361    let mut main = pad.0;
362
363    for (i, node) in nodes.iter_mut().enumerate() {
364        if i > 0 {
365            main += spacing;
366        }
367
368        let (x, y) = axis.pack(main, pad.1);
369
370        node.move_to_mut(Point::new(x, y));
371
372        match axis {
373            Axis::Horizontal => {
374                node.align_mut(Alignment::Start, align_items, Size::new(0.0, cross))
375            }
376            Axis::Vertical => node.align_mut(align_items, Alignment::Start, Size::new(cross, 0.0)),
377        };
378
379        let size = node.bounds().size();
380
381        main += axis.main(size);
382    }
383
384    let (width, height) = axis.pack(main - pad.0, cross);
385    let size = limits.resolve(width, height, Size::new(width, height));
386
387    Node::with_children(size.expand(padding), nodes)
388}