1use std::{any::Any, f32::INFINITY};
2
3use crate::{
4 json::{self, ToJson, Value},
5 Axis, DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, PaintContext,
6 SizeConstraint, Vector2FExt,
7};
8use pathfinder_geometry::{
9 rect::RectF,
10 vector::{vec2f, Vector2F},
11};
12use serde_json::json;
13
14pub struct Flex {
15 axis: Axis,
16 children: Vec<ElementBox>,
17}
18
19impl Flex {
20 pub fn new(axis: Axis) -> Self {
21 Self {
22 axis,
23 children: Default::default(),
24 }
25 }
26
27 pub fn row() -> Self {
28 Self::new(Axis::Horizontal)
29 }
30
31 pub fn column() -> Self {
32 Self::new(Axis::Vertical)
33 }
34
35 fn layout_flex_children(
36 &mut self,
37 expanded: bool,
38 constraint: SizeConstraint,
39 remaining_space: &mut f32,
40 remaining_flex: &mut f32,
41 cross_axis_max: &mut f32,
42 cx: &mut LayoutContext,
43 ) {
44 let cross_axis = self.axis.invert();
45 for child in &mut self.children {
46 if let Some(metadata) = child.metadata::<FlexParentData>() {
47 if metadata.expanded != expanded {
48 continue;
49 }
50
51 let flex = metadata.flex;
52 let child_max = if *remaining_flex == 0.0 {
53 *remaining_space
54 } else {
55 let space_per_flex = *remaining_space / *remaining_flex;
56 space_per_flex * flex
57 };
58 let child_min = if expanded { child_max } else { 0. };
59 let child_constraint = match self.axis {
60 Axis::Horizontal => SizeConstraint::new(
61 vec2f(child_min, constraint.min.y()),
62 vec2f(child_max, constraint.max.y()),
63 ),
64 Axis::Vertical => SizeConstraint::new(
65 vec2f(constraint.min.x(), child_min),
66 vec2f(constraint.max.x(), child_max),
67 ),
68 };
69 let child_size = child.layout(child_constraint, cx);
70 *remaining_space -= child_size.along(self.axis);
71 *remaining_flex -= flex;
72 *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
73 }
74 }
75 }
76}
77
78impl Extend<ElementBox> for Flex {
79 fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
80 self.children.extend(children);
81 }
82}
83
84impl Element for Flex {
85 type LayoutState = ();
86 type PaintState = ();
87
88 fn layout(
89 &mut self,
90 constraint: SizeConstraint,
91 cx: &mut LayoutContext,
92 ) -> (Vector2F, Self::LayoutState) {
93 let mut total_flex = 0.0;
94 let mut fixed_space = 0.0;
95
96 let cross_axis = self.axis.invert();
97 let mut cross_axis_max: f32 = 0.0;
98 for child in &mut self.children {
99 if let Some(metadata) = child.metadata::<FlexParentData>() {
100 total_flex += metadata.flex;
101 } else {
102 let child_constraint = match self.axis {
103 Axis::Horizontal => SizeConstraint::new(
104 vec2f(0.0, constraint.min.y()),
105 vec2f(INFINITY, constraint.max.y()),
106 ),
107 Axis::Vertical => SizeConstraint::new(
108 vec2f(constraint.min.x(), 0.0),
109 vec2f(constraint.max.x(), INFINITY),
110 ),
111 };
112 let size = child.layout(child_constraint, cx);
113 fixed_space += size.along(self.axis);
114 cross_axis_max = cross_axis_max.max(size.along(cross_axis));
115 }
116 }
117
118 let mut size = if total_flex > 0.0 {
119 if constraint.max_along(self.axis).is_infinite() {
120 panic!("flex contains flexible children but has an infinite constraint along the flex axis");
121 }
122
123 let mut remaining_space = constraint.max_along(self.axis) - fixed_space;
124 let mut remaining_flex = total_flex;
125 self.layout_flex_children(
126 false,
127 constraint,
128 &mut remaining_space,
129 &mut remaining_flex,
130 &mut cross_axis_max,
131 cx,
132 );
133 self.layout_flex_children(
134 true,
135 constraint,
136 &mut remaining_space,
137 &mut remaining_flex,
138 &mut cross_axis_max,
139 cx,
140 );
141
142 match self.axis {
143 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
144 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
145 }
146 } else {
147 match self.axis {
148 Axis::Horizontal => vec2f(fixed_space, cross_axis_max),
149 Axis::Vertical => vec2f(cross_axis_max, fixed_space),
150 }
151 };
152
153 if constraint.min.x().is_finite() {
154 size.set_x(size.x().max(constraint.min.x()));
155 }
156
157 if constraint.min.y().is_finite() {
158 size.set_y(size.y().max(constraint.min.y()));
159 }
160
161 (size, ())
162 }
163
164 fn paint(
165 &mut self,
166 bounds: RectF,
167 visible_bounds: RectF,
168 _: &mut Self::LayoutState,
169 cx: &mut PaintContext,
170 ) -> Self::PaintState {
171 let mut child_origin = bounds.origin();
172 for child in &mut self.children {
173 child.paint(child_origin, visible_bounds, cx);
174 match self.axis {
175 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
176 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
177 }
178 }
179 }
180
181 fn dispatch_event(
182 &mut self,
183 event: &Event,
184 _: RectF,
185 _: &mut Self::LayoutState,
186 _: &mut Self::PaintState,
187 cx: &mut EventContext,
188 ) -> bool {
189 let mut handled = false;
190 for child in &mut self.children {
191 handled = child.dispatch_event(event, cx) || handled;
192 }
193 handled
194 }
195
196 fn debug(
197 &self,
198 bounds: RectF,
199 _: &Self::LayoutState,
200 _: &Self::PaintState,
201 cx: &DebugContext,
202 ) -> json::Value {
203 json!({
204 "type": "Flex",
205 "bounds": bounds.to_json(),
206 "axis": self.axis.to_json(),
207 "children": self.children.iter().map(|child| child.debug(cx)).collect::<Vec<json::Value>>()
208 })
209 }
210}
211
212struct FlexParentData {
213 flex: f32,
214 expanded: bool,
215}
216
217pub struct Expanded {
218 metadata: FlexParentData,
219 child: ElementBox,
220}
221
222impl Expanded {
223 pub fn new(flex: f32, child: ElementBox) -> Self {
224 Expanded {
225 metadata: FlexParentData {
226 flex,
227 expanded: true,
228 },
229 child,
230 }
231 }
232}
233
234impl Element for Expanded {
235 type LayoutState = ();
236 type PaintState = ();
237
238 fn layout(
239 &mut self,
240 constraint: SizeConstraint,
241 cx: &mut LayoutContext,
242 ) -> (Vector2F, Self::LayoutState) {
243 let size = self.child.layout(constraint, cx);
244 (size, ())
245 }
246
247 fn paint(
248 &mut self,
249 bounds: RectF,
250 visible_bounds: RectF,
251 _: &mut Self::LayoutState,
252 cx: &mut PaintContext,
253 ) -> Self::PaintState {
254 self.child.paint(bounds.origin(), visible_bounds, cx)
255 }
256
257 fn dispatch_event(
258 &mut self,
259 event: &Event,
260 _: RectF,
261 _: &mut Self::LayoutState,
262 _: &mut Self::PaintState,
263 cx: &mut EventContext,
264 ) -> bool {
265 self.child.dispatch_event(event, cx)
266 }
267
268 fn metadata(&self) -> Option<&dyn Any> {
269 Some(&self.metadata)
270 }
271
272 fn debug(
273 &self,
274 _: RectF,
275 _: &Self::LayoutState,
276 _: &Self::PaintState,
277 cx: &DebugContext,
278 ) -> Value {
279 json!({
280 "type": "Expanded",
281 "flex": self.metadata.flex,
282 "child": self.child.debug(cx)
283 })
284 }
285}
286
287pub struct Flexible {
288 metadata: FlexParentData,
289 child: ElementBox,
290}
291
292impl Flexible {
293 pub fn new(flex: f32, child: ElementBox) -> Self {
294 Flexible {
295 metadata: FlexParentData {
296 flex,
297 expanded: false,
298 },
299 child,
300 }
301 }
302}
303
304impl Element for Flexible {
305 type LayoutState = ();
306 type PaintState = ();
307
308 fn layout(
309 &mut self,
310 constraint: SizeConstraint,
311 cx: &mut LayoutContext,
312 ) -> (Vector2F, Self::LayoutState) {
313 let size = self.child.layout(constraint, cx);
314 (size, ())
315 }
316
317 fn paint(
318 &mut self,
319 bounds: RectF,
320 visible_bounds: RectF,
321 _: &mut Self::LayoutState,
322 cx: &mut PaintContext,
323 ) -> Self::PaintState {
324 self.child.paint(bounds.origin(), visible_bounds, cx)
325 }
326
327 fn dispatch_event(
328 &mut self,
329 event: &Event,
330 _: RectF,
331 _: &mut Self::LayoutState,
332 _: &mut Self::PaintState,
333 cx: &mut EventContext,
334 ) -> bool {
335 self.child.dispatch_event(event, cx)
336 }
337
338 fn metadata(&self) -> Option<&dyn Any> {
339 Some(&self.metadata)
340 }
341
342 fn debug(
343 &self,
344 _: RectF,
345 _: &Self::LayoutState,
346 _: &Self::PaintState,
347 cx: &DebugContext,
348 ) -> Value {
349 json!({
350 "type": "Flexible",
351 "flex": self.metadata.flex,
352 "child": self.child.debug(cx)
353 })
354 }
355}