flex.rs

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