cosmic/widget/menu/
flex.rs

1// From iced_aw, license MIT
2
3use iced_core::widget::Tree;
4use iced_widget::core::{
5    Alignment, Element, Padding, Point, Size,
6    layout::{Limits, Node},
7    renderer,
8};
9
10/// The main axis of a flex layout.
11#[derive(Debug)]
12pub enum Axis {
13    /// The horizontal axis
14    Horizontal,
15
16    /// The vertical axis
17    #[allow(dead_code)]
18    Vertical,
19}
20
21impl Axis {
22    /// Gets the main Axis
23    fn main(&self, size: Size) -> f32 {
24        match self {
25            Self::Horizontal => size.width,
26            Self::Vertical => size.height,
27        }
28    }
29
30    /// Gets the cross Axis
31    fn cross(&self, size: Size) -> f32 {
32        match self {
33            Self::Horizontal => size.height,
34            Self::Vertical => size.width,
35        }
36    }
37
38    /// Returns a Packed axis
39    fn pack(&self, main: f32, cross: f32) -> (f32, f32) {
40        match self {
41            Self::Horizontal => (main, cross),
42            Self::Vertical => (cross, main),
43        }
44    }
45}
46
47/// Computes the flex layout with the given axis and limits, applying spacing,
48/// padding and alignment to the items as needed.
49///
50/// It returns a new layout [`Node`].
51pub fn resolve<'a, E, Message, Renderer>(
52    axis: &Axis,
53    renderer: &Renderer,
54    limits: &Limits,
55    padding: Padding,
56    spacing: f32,
57    align_items: Alignment,
58    items: &[E],
59    tree: &mut [&mut Tree],
60) -> Node
61where
62    E: std::borrow::Borrow<Element<'a, Message, crate::Theme, Renderer>>,
63    Renderer: renderer::Renderer,
64{
65    let limits = limits.shrink(padding);
66    let total_spacing = spacing * items.len().saturating_sub(1) as f32;
67    let max_cross = axis.cross(limits.max());
68
69    let mut fill_sum = 0;
70    let mut cross = axis.cross(limits.min()).max(axis.cross(Size::INFINITY));
71    let mut available = axis.main(limits.max()) - total_spacing;
72
73    let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
74    nodes.resize(items.len(), Node::default());
75
76    if align_items == Alignment::Center {
77        let mut fill_cross = axis.cross(limits.min());
78
79        for (child, tree) in items.iter().zip(tree.iter_mut()) {
80            let child = child.borrow();
81            let c_size = child.as_widget().size();
82            let cross_fill_factor = match axis {
83                Axis::Horizontal => c_size.height,
84                Axis::Vertical => c_size.width,
85            }
86            .fill_factor();
87
88            if cross_fill_factor == 0 {
89                let (max_width, max_height) = axis.pack(available, max_cross);
90
91                let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height));
92
93                let layout = child.as_widget().layout(tree, renderer, &child_limits);
94                let size = layout.size();
95
96                fill_cross = fill_cross.max(axis.cross(size));
97            }
98        }
99
100        cross = fill_cross;
101    }
102
103    for (i, (child, tree)) in items.iter().zip(tree.iter_mut()).enumerate() {
104        let child = child.borrow();
105        let c_size = child.as_widget().size();
106        let fill_factor = match axis {
107            Axis::Horizontal => c_size.width,
108            Axis::Vertical => c_size.height,
109        }
110        .fill_factor();
111
112        if fill_factor == 0 {
113            let (min_width, min_height) = if align_items == Alignment::Center {
114                axis.pack(0.0, cross)
115            } else {
116                axis.pack(0.0, 0.0)
117            };
118
119            let (max_width, max_height) = if align_items == Alignment::Center {
120                axis.pack(available, cross)
121            } else {
122                axis.pack(available, max_cross)
123            };
124
125            let child_limits = Limits::new(
126                Size::new(min_width, min_height),
127                Size::new(max_width, max_height),
128            );
129
130            let layout = child.as_widget().layout(tree, renderer, &child_limits);
131            let size = layout.size();
132
133            available -= axis.main(size);
134
135            if align_items != Alignment::Center {
136                cross = cross.max(axis.cross(size));
137            }
138
139            nodes[i] = layout;
140        } else {
141            fill_sum += fill_factor;
142        }
143    }
144
145    let remaining = available.max(0.0);
146
147    for (i, (child, tree)) in items.iter().zip(tree.iter_mut()).enumerate() {
148        let child = child.borrow();
149        let c_size = child.as_widget().size();
150        let fill_factor = match axis {
151            Axis::Horizontal => c_size.width,
152            Axis::Vertical => c_size.height,
153        }
154        .fill_factor();
155
156        if fill_factor != 0 {
157            let max_main = remaining * f32::from(fill_factor) / f32::from(fill_sum);
158            let min_main = if max_main.is_infinite() {
159                0.0
160            } else {
161                max_main
162            };
163
164            let (min_width, min_height) = if align_items == Alignment::Center {
165                axis.pack(min_main, cross)
166            } else {
167                axis.pack(min_main, axis.cross(limits.min()))
168            };
169
170            let (max_width, max_height) = if align_items == Alignment::Center {
171                axis.pack(max_main, cross)
172            } else {
173                axis.pack(max_main, max_cross)
174            };
175
176            let child_limits = Limits::new(
177                Size::new(min_width, min_height),
178                Size::new(max_width, max_height),
179            );
180
181            let layout = child.as_widget().layout(tree, renderer, &child_limits);
182
183            if align_items != Alignment::Center {
184                cross = cross.max(axis.cross(layout.size()));
185            }
186
187            nodes[i] = layout;
188        }
189    }
190
191    let pad = axis.pack(padding.left, padding.top);
192    let mut main = pad.0;
193
194    for (i, node) in nodes.iter_mut().enumerate() {
195        if i > 0 {
196            main += spacing;
197        }
198
199        let (x, y) = axis.pack(main, pad.1);
200
201        let node_ = node.clone().move_to(Point::new(x, y));
202
203        let node_ = match axis {
204            Axis::Horizontal => node_.align(Alignment::Start, align_items, Size::new(0.0, cross)),
205            Axis::Vertical => node_.align(align_items, Alignment::Start, Size::new(cross, 0.0)),
206        };
207
208        let size = node_.bounds().size();
209
210        *node = node_;
211
212        main += axis.main(size);
213    }
214
215    let (width, height) = axis.pack(main - pad.0, cross);
216    let size = limits.resolve(width, height, Size::new(width, height));
217
218    Node::with_children(size.expand(padding), nodes)
219}