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