flex.rs

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