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 = bool;
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 = None;
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.get_or_insert(0.) += 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 let Some(mut remaining_flex) = total_flex {
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 self.layout_flex_children(
125 false,
126 constraint,
127 &mut remaining_space,
128 &mut remaining_flex,
129 &mut cross_axis_max,
130 cx,
131 );
132 self.layout_flex_children(
133 true,
134 constraint,
135 &mut remaining_space,
136 &mut remaining_flex,
137 &mut cross_axis_max,
138 cx,
139 );
140
141 match self.axis {
142 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
143 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
144 }
145 } else {
146 match self.axis {
147 Axis::Horizontal => vec2f(fixed_space, cross_axis_max),
148 Axis::Vertical => vec2f(cross_axis_max, fixed_space),
149 }
150 };
151
152 if constraint.min.x().is_finite() {
153 size.set_x(size.x().max(constraint.min.x()));
154 }
155 if constraint.min.y().is_finite() {
156 size.set_y(size.y().max(constraint.min.y()));
157 }
158
159 let mut overflowing = false;
160 if size.x() > constraint.max.x() {
161 size.set_x(constraint.max.x());
162 overflowing = true;
163 }
164 if size.y() > constraint.max.y() {
165 size.set_y(constraint.max.y());
166 overflowing = true;
167 }
168
169 (size, overflowing)
170 }
171
172 fn paint(
173 &mut self,
174 bounds: RectF,
175 visible_bounds: RectF,
176 overflowing: &mut Self::LayoutState,
177 cx: &mut PaintContext,
178 ) -> Self::PaintState {
179 if *overflowing {
180 cx.scene.push_layer(Some(bounds));
181 }
182 let mut child_origin = bounds.origin();
183 for child in &mut self.children {
184 child.paint(child_origin, visible_bounds, cx);
185 match self.axis {
186 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
187 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
188 }
189 }
190 if *overflowing {
191 cx.scene.pop_layer();
192 }
193 }
194
195 fn dispatch_event(
196 &mut self,
197 event: &Event,
198 _: RectF,
199 _: &mut Self::LayoutState,
200 _: &mut Self::PaintState,
201 cx: &mut EventContext,
202 ) -> bool {
203 let mut handled = false;
204 for child in &mut self.children {
205 handled = child.dispatch_event(event, cx) || handled;
206 }
207 handled
208 }
209
210 fn debug(
211 &self,
212 bounds: RectF,
213 _: &Self::LayoutState,
214 _: &Self::PaintState,
215 cx: &DebugContext,
216 ) -> json::Value {
217 json!({
218 "type": "Flex",
219 "bounds": bounds.to_json(),
220 "axis": self.axis.to_json(),
221 "children": self.children.iter().map(|child| child.debug(cx)).collect::<Vec<json::Value>>()
222 })
223 }
224}
225
226struct FlexParentData {
227 flex: f32,
228 expanded: bool,
229}
230
231pub struct Expanded {
232 metadata: FlexParentData,
233 child: ElementBox,
234}
235
236impl Expanded {
237 pub fn new(flex: f32, child: ElementBox) -> Self {
238 Expanded {
239 metadata: FlexParentData {
240 flex,
241 expanded: true,
242 },
243 child,
244 }
245 }
246}
247
248impl Element for Expanded {
249 type LayoutState = ();
250 type PaintState = ();
251
252 fn layout(
253 &mut self,
254 constraint: SizeConstraint,
255 cx: &mut LayoutContext,
256 ) -> (Vector2F, Self::LayoutState) {
257 let size = self.child.layout(constraint, cx);
258 (size, ())
259 }
260
261 fn paint(
262 &mut self,
263 bounds: RectF,
264 visible_bounds: RectF,
265 _: &mut Self::LayoutState,
266 cx: &mut PaintContext,
267 ) -> Self::PaintState {
268 self.child.paint(bounds.origin(), visible_bounds, cx)
269 }
270
271 fn dispatch_event(
272 &mut self,
273 event: &Event,
274 _: RectF,
275 _: &mut Self::LayoutState,
276 _: &mut Self::PaintState,
277 cx: &mut EventContext,
278 ) -> bool {
279 self.child.dispatch_event(event, cx)
280 }
281
282 fn metadata(&self) -> Option<&dyn Any> {
283 Some(&self.metadata)
284 }
285
286 fn debug(
287 &self,
288 _: RectF,
289 _: &Self::LayoutState,
290 _: &Self::PaintState,
291 cx: &DebugContext,
292 ) -> Value {
293 json!({
294 "type": "Expanded",
295 "flex": self.metadata.flex,
296 "child": self.child.debug(cx)
297 })
298 }
299}
300
301pub struct Flexible {
302 metadata: FlexParentData,
303 child: ElementBox,
304}
305
306impl Flexible {
307 pub fn new(flex: f32, child: ElementBox) -> Self {
308 Flexible {
309 metadata: FlexParentData {
310 flex,
311 expanded: false,
312 },
313 child,
314 }
315 }
316}
317
318impl Element for Flexible {
319 type LayoutState = ();
320 type PaintState = ();
321
322 fn layout(
323 &mut self,
324 constraint: SizeConstraint,
325 cx: &mut LayoutContext,
326 ) -> (Vector2F, Self::LayoutState) {
327 let size = self.child.layout(constraint, cx);
328 (size, ())
329 }
330
331 fn paint(
332 &mut self,
333 bounds: RectF,
334 visible_bounds: RectF,
335 _: &mut Self::LayoutState,
336 cx: &mut PaintContext,
337 ) -> Self::PaintState {
338 self.child.paint(bounds.origin(), visible_bounds, cx)
339 }
340
341 fn dispatch_event(
342 &mut self,
343 event: &Event,
344 _: RectF,
345 _: &mut Self::LayoutState,
346 _: &mut Self::PaintState,
347 cx: &mut EventContext,
348 ) -> bool {
349 self.child.dispatch_event(event, cx)
350 }
351
352 fn metadata(&self) -> Option<&dyn Any> {
353 Some(&self.metadata)
354 }
355
356 fn debug(
357 &self,
358 _: RectF,
359 _: &Self::LayoutState,
360 _: &Self::PaintState,
361 cx: &DebugContext,
362 ) -> Value {
363 json!({
364 "type": "Flexible",
365 "flex": self.metadata.flex,
366 "child": self.child.debug(cx)
367 })
368 }
369}