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 = None;
 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.get_or_insert(0.) += 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 let Some(mut remaining_flex) = total_flex {
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            self.layout_flex_children(
125                false,
126                constraint,
127                &mut remaining_space,
128                &mut remaining_flex,
129                &mut cross_axis_max,
130                cx,
131            );
132            self.layout_flex_children(
133                true,
134                constraint,
135                &mut remaining_space,
136                &mut remaining_flex,
137                &mut cross_axis_max,
138                cx,
139            );
140
141            match self.axis {
142                Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
143                Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space),
144            }
145        } else {
146            match self.axis {
147                Axis::Horizontal => vec2f(fixed_space, cross_axis_max),
148                Axis::Vertical => vec2f(cross_axis_max, fixed_space),
149            }
150        };
151
152        if constraint.min.x().is_finite() {
153            size.set_x(size.x().max(constraint.min.x()));
154        }
155        if constraint.min.y().is_finite() {
156            size.set_y(size.y().max(constraint.min.y()));
157        }
158
159        let mut overflowing = false;
160        if size.x() > constraint.max.x() {
161            size.set_x(constraint.max.x());
162            overflowing = true;
163        }
164        if size.y() > constraint.max.y() {
165            size.set_y(constraint.max.y());
166            overflowing = true;
167        }
168
169        (size, overflowing)
170    }
171
172    fn paint(
173        &mut self,
174        bounds: RectF,
175        visible_bounds: RectF,
176        overflowing: &mut Self::LayoutState,
177        cx: &mut PaintContext,
178    ) -> Self::PaintState {
179        if *overflowing {
180            cx.scene.push_layer(Some(bounds));
181        }
182        let mut child_origin = bounds.origin();
183        for child in &mut self.children {
184            child.paint(child_origin, visible_bounds, cx);
185            match self.axis {
186                Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0),
187                Axis::Vertical => child_origin += vec2f(0.0, child.size().y()),
188            }
189        }
190        if *overflowing {
191            cx.scene.pop_layer();
192        }
193    }
194
195    fn dispatch_event(
196        &mut self,
197        event: &Event,
198        _: RectF,
199        _: &mut Self::LayoutState,
200        _: &mut Self::PaintState,
201        cx: &mut EventContext,
202    ) -> bool {
203        let mut handled = false;
204        for child in &mut self.children {
205            handled = child.dispatch_event(event, cx) || handled;
206        }
207        handled
208    }
209
210    fn debug(
211        &self,
212        bounds: RectF,
213        _: &Self::LayoutState,
214        _: &Self::PaintState,
215        cx: &DebugContext,
216    ) -> json::Value {
217        json!({
218            "type": "Flex",
219            "bounds": bounds.to_json(),
220            "axis": self.axis.to_json(),
221            "children": self.children.iter().map(|child| child.debug(cx)).collect::<Vec<json::Value>>()
222        })
223    }
224}
225
226struct FlexParentData {
227    flex: f32,
228    expanded: bool,
229}
230
231pub struct Expanded {
232    metadata: FlexParentData,
233    child: ElementBox,
234}
235
236impl Expanded {
237    pub fn new(flex: f32, child: ElementBox) -> Self {
238        Expanded {
239            metadata: FlexParentData {
240                flex,
241                expanded: true,
242            },
243            child,
244        }
245    }
246}
247
248impl Element for Expanded {
249    type LayoutState = ();
250    type PaintState = ();
251
252    fn layout(
253        &mut self,
254        constraint: SizeConstraint,
255        cx: &mut LayoutContext,
256    ) -> (Vector2F, Self::LayoutState) {
257        let size = self.child.layout(constraint, cx);
258        (size, ())
259    }
260
261    fn paint(
262        &mut self,
263        bounds: RectF,
264        visible_bounds: RectF,
265        _: &mut Self::LayoutState,
266        cx: &mut PaintContext,
267    ) -> Self::PaintState {
268        self.child.paint(bounds.origin(), visible_bounds, cx)
269    }
270
271    fn dispatch_event(
272        &mut self,
273        event: &Event,
274        _: RectF,
275        _: &mut Self::LayoutState,
276        _: &mut Self::PaintState,
277        cx: &mut EventContext,
278    ) -> bool {
279        self.child.dispatch_event(event, cx)
280    }
281
282    fn metadata(&self) -> Option<&dyn Any> {
283        Some(&self.metadata)
284    }
285
286    fn debug(
287        &self,
288        _: RectF,
289        _: &Self::LayoutState,
290        _: &Self::PaintState,
291        cx: &DebugContext,
292    ) -> Value {
293        json!({
294            "type": "Expanded",
295            "flex": self.metadata.flex,
296            "child": self.child.debug(cx)
297        })
298    }
299}
300
301pub struct Flexible {
302    metadata: FlexParentData,
303    child: ElementBox,
304}
305
306impl Flexible {
307    pub fn new(flex: f32, child: ElementBox) -> Self {
308        Flexible {
309            metadata: FlexParentData {
310                flex,
311                expanded: false,
312            },
313            child,
314        }
315    }
316}
317
318impl Element for Flexible {
319    type LayoutState = ();
320    type PaintState = ();
321
322    fn layout(
323        &mut self,
324        constraint: SizeConstraint,
325        cx: &mut LayoutContext,
326    ) -> (Vector2F, Self::LayoutState) {
327        let size = self.child.layout(constraint, cx);
328        (size, ())
329    }
330
331    fn paint(
332        &mut self,
333        bounds: RectF,
334        visible_bounds: RectF,
335        _: &mut Self::LayoutState,
336        cx: &mut PaintContext,
337    ) -> Self::PaintState {
338        self.child.paint(bounds.origin(), visible_bounds, cx)
339    }
340
341    fn dispatch_event(
342        &mut self,
343        event: &Event,
344        _: RectF,
345        _: &mut Self::LayoutState,
346        _: &mut Self::PaintState,
347        cx: &mut EventContext,
348    ) -> bool {
349        self.child.dispatch_event(event, cx)
350    }
351
352    fn metadata(&self) -> Option<&dyn Any> {
353        Some(&self.metadata)
354    }
355
356    fn debug(
357        &self,
358        _: RectF,
359        _: &Self::LayoutState,
360        _: &Self::PaintState,
361        cx: &DebugContext,
362    ) -> Value {
363        json!({
364            "type": "Flexible",
365            "flex": self.metadata.flex,
366            "child": self.child.debug(cx)
367        })
368    }
369}