diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-08-19 15:31:29 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-08-19 15:52:15 +0200 |
| commit | a6f260ca39f70f82617eca87855789413715f47d (patch) | |
| tree | 08141ae619bd21e0544d21433bce759aebc7ba83 /src/layout/fixed.rs | |
| parent | fdab7158c91c52a4ace211c804fdd8e9110f56de (diff) | |
Refactor layouting a bit
Notably:
- Handle aspect ratio in fixed node
- Inline constraint inflation into pad node
Diffstat (limited to 'src/layout/fixed.rs')
| -rw-r--r-- | src/layout/fixed.rs | 77 |
1 files changed, 62 insertions, 15 deletions
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs index 9fa2af8a..7f292f14 100644 --- a/src/layout/fixed.rs +++ b/src/layout/fixed.rs @@ -1,3 +1,5 @@ +use decorum::N64; + use super::*; /// A node that can fix its child's width and height. @@ -8,6 +10,10 @@ pub struct FixedNode { pub width: Option<Linear>, /// The fixed height, if any. pub height: Option<Linear>, + /// The fixed aspect ratio between width and height. + /// + /// The resulting frame will satisfy `width = aspect * height`. + pub aspect: Option<N64>, /// The child node whose size to fix. pub child: LayoutNode, } @@ -16,33 +22,74 @@ impl Layout for FixedNode { fn layout( &self, ctx: &mut LayoutContext, - regions: &Regions, + &Regions { current, base, expand, .. }: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - let Regions { current, base, .. } = regions; - let mut constraints = Constraints::new(regions.expand); - constraints.set_base_using_linears(Spec::new(self.width, self.height), ®ions); + // Fill in width or height if aspect ratio and the other is given. + let aspect = self.aspect.map(N64::into_inner); + let width = self.width.or(self.height.zip(aspect).map(|(h, a)| a * h)); + let height = self.height.or(self.width.zip(aspect).map(|(w, a)| w / a)); - let size = Size::new( - self.width.map_or(current.width, |w| w.resolve(base.width)), - self.height.map_or(current.height, |h| h.resolve(base.height)), - ); + // Prepare constraints. + let mut constraints = Constraints::new(expand); + constraints.set_base_if_linear(base, Spec::new(width, height)); - // If one dimension was not specified, the `current` size needs to remain static. - if self.width.is_none() { + // If the size for one axis isn't specified, the `current` size along + // that axis needs to remain the same for the result to be reusable. + if width.is_none() { constraints.exact.horizontal = Some(current.width); } - if self.height.is_none() { + + if height.is_none() { constraints.exact.vertical = Some(current.height); } - let expand = Spec::new(self.width.is_some(), self.height.is_some()); - let regions = Regions::one(size, expand); + // Resolve the linears based on the current width and height. + let mut size = Size::new( + width.map_or(current.width, |w| w.resolve(base.width)), + height.map_or(current.height, |h| h.resolve(base.height)), + ); + + // If width or height aren't set for an axis, the base should be + // inherited from the parent for that axis. + let base = Size::new( + width.map_or(base.width, |_| size.width), + height.map_or(base.height, |_| size.height), + ); + + // Handle the aspect ratio. + if let Some(aspect) = aspect { + constraints.exact = current.to_spec().map(Some); + constraints.min = Spec::splat(None); + constraints.max = Spec::splat(None); + + let width = size.width.min(aspect * size.height); + size = Size::new(width, width / aspect); + } + + // If width or height are fixed, the child should fill the available + // space along that axis. + let expand = Spec::new(width.is_some(), height.is_some()); + + // Layout the child. + let mut regions = Regions::one(size, base, expand); let mut frames = self.child.layout(ctx, ®ions); - if let Some(frame) = frames.first_mut() { - frame.constraints = constraints; + // If we have an aspect ratio and the child is content-sized, we need to + // relayout with expansion. + if let Some(aspect) = aspect { + if width.is_none() && height.is_none() { + let needed = frames[0].size.cap(size); + let width = needed.width.max(aspect * needed.height); + regions.current = Size::new(width, width / aspect); + regions.expand = Spec::splat(true); + frames = self.child.layout(ctx, ®ions); + } } + // Overwrite the child's constraints with ours. + frames[0].constraints = constraints; + assert_eq!(frames.len(), 1); + frames } } |
