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