1use std::any::Any;
2
3use crate::{
4 AfterLayoutContext, Axis, Element, ElementBox, Event, EventContext, LayoutContext,
5 PaintContext, SizeConstraint, Vector2FExt,
6};
7use pathfinder_geometry::vector::{vec2f, Vector2F};
8
9pub struct Flex {
10 axis: Axis,
11 children: Vec<ElementBox>,
12}
13
14impl Flex {
15 pub fn new(axis: Axis) -> Self {
16 Self {
17 axis,
18 children: Default::default(),
19 }
20 }
21
22 pub fn row() -> Self {
23 Self::new(Axis::Horizontal)
24 }
25
26 pub fn column() -> Self {
27 Self::new(Axis::Vertical)
28 }
29
30 fn child_flex<'b>(child: &ElementBox) -> Option<f32> {
31 child
32 .metadata()
33 .and_then(|d| d.downcast_ref::<FlexParentData>())
34 .map(|data| data.flex)
35 }
36}
37
38impl Extend<ElementBox> for Flex {
39 fn extend<T: IntoIterator<Item = ElementBox>>(&mut self, children: T) {
40 self.children.extend(children);
41 }
42}
43
44impl Element for Flex {
45 type LayoutState = ();
46 type PaintState = ();
47
48 fn layout(
49 &mut self,
50 constraint: SizeConstraint,
51 ctx: &mut LayoutContext,
52 ) -> (Vector2F, Self::LayoutState) {
53 let mut total_flex = 0.0;
54 let mut fixed_space = 0.0;
55
56 let cross_axis = self.axis.invert();
57 let mut cross_axis_max: f32 = 0.0;
58 for child in &mut self.children {
59 if let Some(flex) = Self::child_flex(&child) {
60 total_flex += flex;
61 } else {
62 let child_constraint =
63 SizeConstraint::strict_along(cross_axis, constraint.max_along(cross_axis));
64 let size = child.layout(child_constraint, ctx);
65 fixed_space += size.along(self.axis);
66 cross_axis_max = cross_axis_max.max(size.along(cross_axis));
67 }
68 }
69
70 let mut size = if total_flex > 0.0 {
71 if constraint.max_along(self.axis).is_infinite() {
72 panic!("flex contains flexible children but has an infinite constraint along the flex axis");
73 }
74
75 let mut remaining_space = constraint.max_along(self.axis) - fixed_space;
76 let mut remaining_flex = total_flex;
77 for child in &mut self.children {
78 let space_per_flex = remaining_space / remaining_flex;
79 if let Some(flex) = Self::child_flex(&child) {
80 let child_max = space_per_flex * flex;
81 let child_constraint = match self.axis {
82 Axis::Horizontal => SizeConstraint::new(
83 vec2f(0.0, constraint.max.y()),
84 vec2f(child_max, constraint.max.y()),
85 ),
86 Axis::Vertical => SizeConstraint::new(
87 vec2f(constraint.max.x(), 0.0),
88 vec2f(constraint.max.x(), child_max),
89 ),
90 };
91 let child_size = child.layout(child_constraint, ctx);
92 remaining_space -= child_size.along(self.axis);
93 remaining_flex -= flex;
94 cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
95 }
96 }
97
98 match self.axis {
99 Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
100 Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
101 }
102 } else {
103 match self.axis {
104 Axis::Horizontal => vec2f(fixed_space, cross_axis_max),
105 Axis::Vertical => vec2f(cross_axis_max, fixed_space),
106 }
107 };
108
109 if constraint.min.x().is_finite() {
110 size.set_x(size.x().max(constraint.min.x()));
111 }
112
113 if constraint.min.y().is_finite() {
114 size.set_y(size.y().max(constraint.min.y()));
115 }
116
117 (size, ())
118 }
119
120 fn after_layout(
121 &mut self,
122 _: Vector2F,
123 _: &mut Self::LayoutState,
124 ctx: &mut AfterLayoutContext,
125 ) {
126 for child in &mut self.children {
127 child.after_layout(ctx);
128 }
129 }
130
131 fn paint(
132 &mut self,
133 bounds: pathfinder_geometry::rect::RectF,
134 _: &mut Self::LayoutState,
135 ctx: &mut PaintContext,
136 ) -> Self::PaintState {
137 let mut child_origin = bounds.origin();
138 for child in &mut self.children {
139 child.paint(child_origin, ctx);
140 match self.axis {
141 Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
142 Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
143 }
144 }
145 }
146
147 fn dispatch_event(
148 &mut self,
149 event: &Event,
150 _: pathfinder_geometry::rect::RectF,
151 _: &mut Self::LayoutState,
152 _: &mut Self::PaintState,
153 ctx: &mut EventContext,
154 ) -> bool {
155 let mut handled = false;
156 for child in &mut self.children {
157 handled = child.dispatch_event(event, ctx) || handled;
158 }
159 handled
160 }
161}
162
163struct FlexParentData {
164 flex: f32,
165}
166
167pub struct Expanded {
168 metadata: FlexParentData,
169 child: ElementBox,
170}
171
172impl Expanded {
173 pub fn new(flex: f32, child: ElementBox) -> Self {
174 Expanded {
175 metadata: FlexParentData { flex },
176 child,
177 }
178 }
179}
180
181impl Element for Expanded {
182 type LayoutState = ();
183 type PaintState = ();
184
185 fn layout(
186 &mut self,
187 constraint: SizeConstraint,
188 ctx: &mut LayoutContext,
189 ) -> (Vector2F, Self::LayoutState) {
190 let size = self.child.layout(constraint, ctx);
191 (size, ())
192 }
193
194 fn after_layout(
195 &mut self,
196 _: Vector2F,
197 _: &mut Self::LayoutState,
198 ctx: &mut AfterLayoutContext,
199 ) {
200 self.child.after_layout(ctx);
201 }
202
203 fn paint(
204 &mut self,
205 bounds: pathfinder_geometry::rect::RectF,
206 _: &mut Self::LayoutState,
207 ctx: &mut PaintContext,
208 ) -> Self::PaintState {
209 self.child.paint(bounds.origin(), ctx)
210 }
211
212 fn dispatch_event(
213 &mut self,
214 event: &Event,
215 _: pathfinder_geometry::rect::RectF,
216 _: &mut Self::LayoutState,
217 _: &mut Self::PaintState,
218 ctx: &mut EventContext,
219 ) -> bool {
220 self.child.dispatch_event(event, ctx)
221 }
222
223 fn metadata(&self) -> Option<&dyn Any> {
224 Some(&self.metadata)
225 }
226}