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 layout_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 let Some((flex, expanded)) = metadata.flex {
48 if expanded != layout_expanded {
49 continue;
50 }
51
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}
78
79impl Extend<ElementBox> for Flex {
80 fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
81 self.children.extend(children);
82 }
83}
84
85impl Element for Flex {
86 type LayoutState = f32;
87 type PaintState = ();
88
89 fn layout(
90 &mut self,
91 constraint: SizeConstraint,
92 cx: &mut LayoutContext,
93 ) -> (Vector2F, Self::LayoutState) {
94 let mut total_flex = None;
95 let mut fixed_space = 0.0;
96
97 let cross_axis = self.axis.invert();
98 let mut cross_axis_max: f32 = 0.0;
99 for child in &mut self.children {
100 if let Some(flex) = child
101 .metadata::<FlexParentData>()
102 .and_then(|metadata| metadata.flex.map(|(flex, _)| flex))
103 {
104 *total_flex.get_or_insert(0.) += flex;
105 } else {
106 let child_constraint = match self.axis {
107 Axis::Horizontal => SizeConstraint::new(
108 vec2f(0.0, constraint.min.y()),
109 vec2f(INFINITY, constraint.max.y()),
110 ),
111 Axis::Vertical => SizeConstraint::new(
112 vec2f(constraint.min.x(), 0.0),
113 vec2f(constraint.max.x(), INFINITY),
114 ),
115 };
116 let size = child.layout(child_constraint, cx);
117 fixed_space += size.along(self.axis);
118 cross_axis_max = cross_axis_max.max(size.along(cross_axis));
119 }
120 }
121
122 let mut remaining_space = constraint.max_along(self.axis) - fixed_space;
123 let mut size = if let Some(mut remaining_flex) = total_flex {
124 if remaining_space.is_infinite() {
125 panic!("flex contains flexible children but has an infinite constraint along the flex axis");
126 }
127
128 self.layout_flex_children(
129 false,
130 constraint,
131 &mut remaining_space,
132 &mut remaining_flex,
133 &mut cross_axis_max,
134 cx,
135 );
136 self.layout_flex_children(
137 true,
138 constraint,
139 &mut remaining_space,
140 &mut remaining_flex,
141 &mut cross_axis_max,
142 cx,
143 );
144
145 match self.axis {
146 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
147 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
148 }
149 } else {
150 match self.axis {
151 Axis::Horizontal => vec2f(fixed_space, cross_axis_max),
152 Axis::Vertical => vec2f(cross_axis_max, fixed_space),
153 }
154 };
155
156 if constraint.min.x().is_finite() {
157 size.set_x(size.x().max(constraint.min.x()));
158 }
159 if constraint.min.y().is_finite() {
160 size.set_y(size.y().max(constraint.min.y()));
161 }
162
163 if size.x() > constraint.max.x() {
164 size.set_x(constraint.max.x());
165 }
166 if size.y() > constraint.max.y() {
167 size.set_y(constraint.max.y());
168 }
169
170 (size, remaining_space)
171 }
172
173 fn paint(
174 &mut self,
175 bounds: RectF,
176 visible_bounds: RectF,
177 remaining_space: &mut Self::LayoutState,
178 cx: &mut PaintContext,
179 ) -> Self::PaintState {
180 let overflowing = *remaining_space < 0.;
181 if overflowing {
182 cx.scene.push_layer(Some(bounds));
183 }
184 let mut child_origin = bounds.origin();
185 for child in &mut self.children {
186 if *remaining_space > 0. {
187 if let Some(metadata) = child.metadata::<FlexParentData>() {
188 if metadata.float {
189 match self.axis {
190 Axis::Horizontal => child_origin += vec2f(*remaining_space, 0.0),
191 Axis::Vertical => child_origin += vec2f(0.0, *remaining_space),
192 }
193 *remaining_space = 0.;
194 }
195 }
196 }
197 child.paint(child_origin, visible_bounds, cx);
198 match self.axis {
199 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
200 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
201 }
202 }
203 if overflowing {
204 cx.scene.pop_layer();
205 }
206 }
207
208 fn dispatch_event(
209 &mut self,
210 event: &Event,
211 _: RectF,
212 _: &mut Self::LayoutState,
213 _: &mut Self::PaintState,
214 cx: &mut EventContext,
215 ) -> bool {
216 let mut handled = false;
217 for child in &mut self.children {
218 handled = child.dispatch_event(event, cx) || handled;
219 }
220 handled
221 }
222
223 fn debug(
224 &self,
225 bounds: RectF,
226 _: &Self::LayoutState,
227 _: &Self::PaintState,
228 cx: &DebugContext,
229 ) -> json::Value {
230 json!({
231 "type": "Flex",
232 "bounds": bounds.to_json(),
233 "axis": self.axis.to_json(),
234 "children": self.children.iter().map(|child| child.debug(cx)).collect::<Vec<json::Value>>()
235 })
236 }
237}
238
239struct FlexParentData {
240 flex: Option<(f32, bool)>,
241 float: bool,
242}
243
244pub struct FlexItem {
245 metadata: FlexParentData,
246 child: ElementBox,
247}
248
249impl FlexItem {
250 pub fn new(child: ElementBox) -> Self {
251 FlexItem {
252 metadata: FlexParentData {
253 flex: None,
254 float: false,
255 },
256 child,
257 }
258 }
259
260 pub fn flex(mut self, flex: f32, expanded: bool) -> Self {
261 self.metadata.flex = Some((flex, expanded));
262 self
263 }
264
265 pub fn float(mut self) -> Self {
266 self.metadata.float = true;
267 self
268 }
269}
270
271impl Element for FlexItem {
272 type LayoutState = ();
273 type PaintState = ();
274
275 fn layout(
276 &mut self,
277 constraint: SizeConstraint,
278 cx: &mut LayoutContext,
279 ) -> (Vector2F, Self::LayoutState) {
280 let size = self.child.layout(constraint, cx);
281 (size, ())
282 }
283
284 fn paint(
285 &mut self,
286 bounds: RectF,
287 visible_bounds: RectF,
288 _: &mut Self::LayoutState,
289 cx: &mut PaintContext,
290 ) -> Self::PaintState {
291 self.child.paint(bounds.origin(), visible_bounds, cx)
292 }
293
294 fn dispatch_event(
295 &mut self,
296 event: &Event,
297 _: RectF,
298 _: &mut Self::LayoutState,
299 _: &mut Self::PaintState,
300 cx: &mut EventContext,
301 ) -> bool {
302 self.child.dispatch_event(event, cx)
303 }
304
305 fn metadata(&self) -> Option<&dyn Any> {
306 Some(&self.metadata)
307 }
308
309 fn debug(
310 &self,
311 _: RectF,
312 _: &Self::LayoutState,
313 _: &Self::PaintState,
314 cx: &DebugContext,
315 ) -> Value {
316 json!({
317 "type": "Flexible",
318 "flex": self.metadata.flex,
319 "child": self.child.debug(cx)
320 })
321 }
322}