feather_ui/layout/
list.rs1use super::{
5 Concrete, Desc, Layout, Renderable, Staged, base, check_unsized_abs, map_unsized_area,
6 nuetralize_unsized,
7};
8use crate::{PxDim, PxPoint, PxRect, RowDirection, SourceID, rtree};
9use std::rc::Rc;
10
11pub trait Prop: base::Area + base::Limits + base::Direction {}
12
13crate::gen_from_to_dyn!(Prop);
14
15pub trait Child: base::RLimits + base::Margin + base::Order {}
16
17crate::gen_from_to_dyn!(Child);
18
19impl Desc for dyn Prop {
20 type Props = dyn Prop;
21 type Child = dyn Child;
22 type Children = im::Vector<Option<Box<dyn Layout<Self::Child>>>>;
24
25 fn stage<'a>(
26 props: &Self::Props,
27 outer_area: PxRect,
28 outer_limits: crate::PxLimits,
29 children: &Self::Children,
30 id: std::sync::Weak<SourceID>,
31 renderable: Option<Rc<dyn Renderable>>,
32 window: &mut crate::component::window::WindowState,
33 ) -> Box<dyn Staged + 'a> {
34 use super::Swappable;
35 let limits = outer_limits + props.limits().resolve(window.dpi);
38 let myarea = props.area().resolve(window.dpi);
39 let dir = props.direction();
41 let xaxis = match dir {
43 RowDirection::LeftToRight | RowDirection::RightToLeft => true,
44 RowDirection::TopToBottom | RowDirection::BottomToTop => false,
45 };
46
47 let inner_dim = super::limit_dim(super::eval_dim(myarea, outer_area.dim()), limits);
49 let outer_safe = nuetralize_unsized(outer_area);
50 let (main_limit, _) = inner_dim.min(limits.max()).swap_axis(xaxis);
52
53 let mut aux_margins: im::Vector<f32> = im::Vector::new();
55 let mut areas: im::Vector<Option<(PxRect, f32)>> = im::Vector::new();
56
57 let area = {
58 let mut cur = PxPoint::zero();
59 let mut max_main = 0.0;
60 let mut max_aux: f32 = 0.0;
61 let mut prev_margin = f32::NAN;
62 let mut aux_margin: f32 = 0.0;
63 let mut aux_margin_bottom = f32::NAN;
64 let mut prev_aux_margin = f32::NAN;
65 let inner_area = PxRect::from(inner_dim);
66
67 for child in children.iter() {
68 let child_props = child.as_ref().unwrap().get_props();
69 let child_limit = super::apply_limit(inner_dim, limits, *child_props.rlimits());
70 let child_margin = child_props
71 .margin()
72 .resolve(window.dpi)
73 .to_perimeter(outer_safe);
74
75 let stage = child
76 .as_ref()
77 .unwrap()
78 .stage(inner_area, child_limit, window);
79 let area = stage.get_area();
80
81 let (margin_main, child_margin_aux) = child_margin.topleft().swap_axis(xaxis);
82 let (main, aux) = area.dim().swap_axis(xaxis);
83 let mut margin = super::merge_margin(prev_margin, margin_main);
84 areas.push_back(Some((area, margin)));
86
87 if !prev_margin.is_nan() && cur.x + main + margin > main_limit {
88 max_main = cur.x.max(max_main);
89 cur.x = 0.0;
90 let aux_merge = super::merge_margin(prev_aux_margin, aux_margin);
91 aux_margins.push_back(aux_merge);
92 cur.y += max_aux + aux_merge;
93 max_aux = 0.0;
94 margin = 0.0;
95 aux_margin = 0.0;
96 prev_aux_margin = aux_margin_bottom;
97 aux_margin_bottom = f32::NAN;
98 }
99
100 cur.x += main + margin;
101 aux_margin = aux_margin.max(child_margin_aux);
102 max_aux = max_aux.max(aux);
103
104 let (margin, child_margin_aux) = child_margin.bottomright().swap_axis(xaxis);
105 prev_margin = margin;
106 aux_margin_bottom = aux_margin_bottom.max(child_margin_aux);
107 }
108
109 max_main = cur.x.max(max_main);
111 let aux_merge = super::merge_margin(prev_aux_margin, aux_margin);
112 aux_margins.push_back(aux_merge);
113 cur.y += max_aux + aux_margin;
114 let (bounds_x, bounds_y) = super::swap_pair(xaxis, (max_main, cur.y));
115 map_unsized_area(myarea, PxDim::new(bounds_x, bounds_y))
116 };
117
118 let evaluated_area = super::limit_area(area * outer_safe, limits);
120
121 let mut staging: im::Vector<Option<Box<dyn Staged>>> = im::Vector::new();
122 let mut nodes: im::Vector<Option<Rc<rtree::Node>>> = im::Vector::new();
123
124 let (unsized_x, unsized_y) = check_unsized_abs(outer_area.bottomright());
127 if (unsized_x && xaxis) || (unsized_y && !xaxis) {
128 return Box::new(Concrete {
129 area: evaluated_area,
130 renderable: None,
131 rtree: rtree::Node::new(evaluated_area.to_untyped(), None, nodes, id, window),
132 children: staging,
133 layer: None,
134 });
135 }
136
137 let evaluated_dim = evaluated_area.dim();
138 let mut cur = match dir {
139 RowDirection::LeftToRight | RowDirection::TopToBottom => PxPoint::zero(),
140 RowDirection::RightToLeft => PxPoint::new(evaluated_dim.width, 0.0),
141 RowDirection::BottomToTop => PxPoint::new(0.0, evaluated_dim.height),
142 };
143 let mut maxaux: f32 = 0.0;
144 aux_margins.pop_front();
145 let mut aux_margin = aux_margins.pop_front().unwrap_or_default();
146
147 for (i, child) in children.iter().enumerate() {
148 let child = child.as_ref().unwrap();
149 let (area, margin) = areas[i].unwrap();
150 let dim = area.dim();
151 let (_, aux) = dim.swap_axis(xaxis);
152
153 match dir {
154 RowDirection::RightToLeft => {
155 if cur.x - dim.width - margin < 0.0 {
156 cur.y += maxaux + aux_margin;
157 aux_margin = aux_margins.pop_front().unwrap_or_default();
158 maxaux = 0.0;
159 cur.x = evaluated_dim.width - dim.width;
160 } else {
161 cur.x -= dim.width + margin
162 }
163 }
164 RowDirection::BottomToTop => {
165 if cur.y - dim.height - margin < 0.0 {
166 cur.x += maxaux + aux_margin;
167 aux_margin = aux_margins.pop_front().unwrap_or_default();
168 maxaux = 0.0;
169 cur.y = evaluated_dim.height - dim.height;
170 } else {
171 cur.y -= dim.height + margin
172 }
173 }
174 RowDirection::LeftToRight => {
175 if cur.x + dim.width + margin > evaluated_dim.width {
176 cur.y += maxaux + aux_margin;
177 aux_margin = aux_margins.pop_front().unwrap_or_default();
178 maxaux = 0.0;
179 cur.x = 0.0;
180 } else {
181 cur.x += margin;
182 }
183 }
184 RowDirection::TopToBottom => {
185 if cur.y + dim.height + margin > evaluated_dim.height {
186 cur.x += maxaux + aux_margin;
187 aux_margin = aux_margins.pop_front().unwrap_or_default();
188 maxaux = 0.0;
189 cur.y = 0.0;
190 } else {
191 cur.y += margin;
192 }
193 }
194 };
195
196 let child_area = area + cur;
197
198 match dir {
199 RowDirection::LeftToRight => cur.x += dim.width,
200 RowDirection::TopToBottom => cur.y += dim.height,
201 _ => (),
202 };
203 maxaux = maxaux.max(aux);
204
205 let child_limit = *child.get_props().rlimits() * evaluated_dim;
206
207 let stage = child.stage(child_area, child_limit, window);
208 if let Some(node) = stage.get_rtree().upgrade() {
209 nodes.push_back(Some(node));
210 }
211 staging.push_back(Some(stage));
212 }
213
214 Box::new(Concrete {
215 area: evaluated_area,
216 renderable,
217 rtree: rtree::Node::new(evaluated_area.to_untyped(), None, nodes, id, window),
218 children: staging,
219 layer: None,
220 })
221 }
222}