flex.rs

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