flex.rs

  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}