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 child_flex<'b>(child: &ElementBox) -> Option<f32> {
36 child.metadata::<FlexParentData>().map(|data| data.flex)
37 }
38}
39
40impl Extend<ElementBox> for Flex {
41 fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
42 self.children.extend(children);
43 }
44}
45
46impl Element for Flex {
47 type LayoutState = ();
48 type PaintState = ();
49
50 fn layout(
51 &mut self,
52 constraint: SizeConstraint,
53 cx: &mut LayoutContext,
54 ) -> (Vector2F, Self::LayoutState) {
55 let mut total_flex = 0.0;
56 let mut fixed_space = 0.0;
57
58 let cross_axis = self.axis.invert();
59 let mut cross_axis_max: f32 = 0.0;
60 for child in &mut self.children {
61 if let Some(flex) = Self::child_flex(&child) {
62 total_flex += flex;
63 } else {
64 let child_constraint = match self.axis {
65 Axis::Horizontal => SizeConstraint::new(
66 vec2f(0.0, constraint.min.y()),
67 vec2f(INFINITY, constraint.max.y()),
68 ),
69 Axis::Vertical => SizeConstraint::new(
70 vec2f(constraint.min.x(), 0.0),
71 vec2f(constraint.max.x(), INFINITY),
72 ),
73 };
74 let size = child.layout(child_constraint, cx);
75 fixed_space += size.along(self.axis);
76 cross_axis_max = cross_axis_max.max(size.along(cross_axis));
77 }
78 }
79
80 let mut size = if total_flex > 0.0 {
81 if constraint.max_along(self.axis).is_infinite() {
82 panic!("flex contains flexible children but has an infinite constraint along the flex axis");
83 }
84
85 let mut remaining_space = constraint.max_along(self.axis) - fixed_space;
86 let mut remaining_flex = total_flex;
87 for child in &mut self.children {
88 if let Some(flex) = Self::child_flex(&child) {
89 let child_max = if remaining_flex == 0.0 {
90 remaining_space
91 } else {
92 let space_per_flex = remaining_space / remaining_flex;
93 space_per_flex * flex
94 };
95 let child_constraint = match self.axis {
96 Axis::Horizontal => SizeConstraint::new(
97 vec2f(0.0, constraint.min.y()),
98 vec2f(child_max, constraint.max.y()),
99 ),
100 Axis::Vertical => SizeConstraint::new(
101 vec2f(constraint.min.x(), 0.0),
102 vec2f(constraint.max.x(), child_max),
103 ),
104 };
105 let child_size = child.layout(child_constraint, cx);
106 remaining_space -= child_size.along(self.axis);
107 remaining_flex -= flex;
108 cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
109 }
110 }
111
112 match self.axis {
113 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
114 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
115 }
116 } else {
117 match self.axis {
118 Axis::Horizontal => vec2f(fixed_space, cross_axis_max),
119 Axis::Vertical => vec2f(cross_axis_max, fixed_space),
120 }
121 };
122
123 if constraint.min.x().is_finite() {
124 size.set_x(size.x().max(constraint.min.x()));
125 }
126
127 if constraint.min.y().is_finite() {
128 size.set_y(size.y().max(constraint.min.y()));
129 }
130
131 (size, ())
132 }
133
134 fn paint(
135 &mut self,
136 bounds: RectF,
137 visible_bounds: RectF,
138 _: &mut Self::LayoutState,
139 cx: &mut PaintContext,
140 ) -> Self::PaintState {
141 let mut child_origin = bounds.origin();
142 for child in &mut self.children {
143 child.paint(child_origin, visible_bounds, cx);
144 match self.axis {
145 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
146 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
147 }
148 }
149 }
150
151 fn dispatch_event(
152 &mut self,
153 event: &Event,
154 _: RectF,
155 _: &mut Self::LayoutState,
156 _: &mut Self::PaintState,
157 cx: &mut EventContext,
158 ) -> bool {
159 let mut handled = false;
160 for child in &mut self.children {
161 handled = child.dispatch_event(event, cx) || handled;
162 }
163 handled
164 }
165
166 fn debug(
167 &self,
168 bounds: RectF,
169 _: &Self::LayoutState,
170 _: &Self::PaintState,
171 cx: &DebugContext,
172 ) -> json::Value {
173 json!({
174 "type": "Flex",
175 "bounds": bounds.to_json(),
176 "axis": self.axis.to_json(),
177 "children": self.children.iter().map(|child| child.debug(cx)).collect::<Vec<json::Value>>()
178 })
179 }
180}
181
182struct FlexParentData {
183 flex: f32,
184}
185
186pub struct Expanded {
187 metadata: FlexParentData,
188 child: ElementBox,
189}
190
191impl Expanded {
192 pub fn new(flex: f32, child: ElementBox) -> Self {
193 Expanded {
194 metadata: FlexParentData { flex },
195 child,
196 }
197 }
198}
199
200impl Element for Expanded {
201 type LayoutState = ();
202 type PaintState = ();
203
204 fn layout(
205 &mut self,
206 constraint: SizeConstraint,
207 cx: &mut LayoutContext,
208 ) -> (Vector2F, Self::LayoutState) {
209 let size = self.child.layout(constraint, cx);
210 (size, ())
211 }
212
213 fn paint(
214 &mut self,
215 bounds: RectF,
216 visible_bounds: RectF,
217 _: &mut Self::LayoutState,
218 cx: &mut PaintContext,
219 ) -> Self::PaintState {
220 self.child.paint(bounds.origin(), visible_bounds, cx)
221 }
222
223 fn dispatch_event(
224 &mut self,
225 event: &Event,
226 _: RectF,
227 _: &mut Self::LayoutState,
228 _: &mut Self::PaintState,
229 cx: &mut EventContext,
230 ) -> bool {
231 self.child.dispatch_event(event, cx)
232 }
233
234 fn metadata(&self) -> Option<&dyn Any> {
235 Some(&self.metadata)
236 }
237
238 fn debug(
239 &self,
240 _: RectF,
241 _: &Self::LayoutState,
242 _: &Self::PaintState,
243 cx: &DebugContext,
244 ) -> Value {
245 json!({
246 "type": "Expanded",
247 "flex": self.metadata.flex,
248 "child": self.child.debug(cx)
249 })
250 }
251}