flex.rs

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