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