diff --git a/src/exec/context.rs b/src/exec/context.rs index d33d62ef4..7dc517966 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -197,7 +197,7 @@ impl StackBuilder { children.extend(last.any()); children.push(par); } - StackNode { dirs, children } + StackNode { dirs, aspect: None, children } } } diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs index 5905fa952..a42eab5ac 100644 --- a/src/layout/fixed.rs +++ b/src/layout/fixed.rs @@ -7,10 +7,6 @@ pub struct FixedNode { pub width: Option, /// The fixed height, if any. pub height: Option, - /// The fixed aspect ratio between width and height, if any. - /// - /// The resulting frame will satisfy `width = aspect * height`. - pub aspect: Option, /// The child node whose size to fix. pub child: AnyNode, } @@ -18,21 +14,13 @@ pub struct FixedNode { impl Layout for FixedNode { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec { let Areas { current, full, .. } = areas; - - let full = Size::new( - self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width), - self.height.map(|h| h.resolve(full.height)).unwrap_or(current.height), + let size = Size::new( + self.width.map_or(current.width, |w| w.resolve(full.width)), + self.height.map_or(current.height, |h| h.resolve(full.height)), ); - let mut size = full; - if let Some(aspect) = self.aspect { - // Shrink the size to ensure that the aspect ratio can be satisfied. - let width = size.width.min(aspect * size.height); - size = Size::new(width, width / aspect); - } - let fixed = Spec::new(self.width.is_some(), self.height.is_some()); - let areas = Areas::once(size, full, fixed).with_aspect(self.aspect); + let areas = Areas::once(size, size, fixed); self.child.layout(ctx, &areas) } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index dad378f5c..cb138753d 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -155,10 +155,6 @@ pub struct Areas { /// /// If this is false, the frame will shrink to fit its content. pub fixed: Spec, - /// The aspect ratio the resulting frame should respect. - /// - /// This property is only handled by the stack layouter. - pub aspect: Option, } impl Areas { @@ -170,7 +166,6 @@ impl Areas { backlog: vec![], last: None, fixed, - aspect: None, } } @@ -182,16 +177,9 @@ impl Areas { backlog: vec![], last: Some(size), fixed, - aspect: None, } } - /// Builder-style method for setting the aspect ratio. - pub fn with_aspect(mut self, aspect: Option) -> Self { - self.aspect = aspect; - self - } - /// Map all areas. pub fn map(&self, mut f: F) -> Self where @@ -203,7 +191,6 @@ impl Areas { backlog: self.backlog.iter().copied().map(|s| f(s)).collect(), last: self.last.map(f), fixed: self.fixed, - aspect: self.aspect, } } @@ -224,4 +211,10 @@ impl Areas { self.current.is_nan() || size.is_nan() || self.current == size }) } + + /// Shrink `current` to ensure that the aspect ratio can be satisfied. + pub fn apply_aspect_ratio(&mut self, ratio: f64) { + let Size { width, height } = self.current; + self.current = Size::new(width.min(ratio * height), height.min(width / ratio)); + } } diff --git a/src/layout/stack.rs b/src/layout/stack.rs index a73abeeb9..463f38a73 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -8,6 +8,10 @@ pub struct StackNode { /// The children are stacked along the `main` direction. The `cross` /// direction is required for aligning the children. pub dirs: Gen, + /// The fixed aspect ratio between width and height, if any. + /// + /// The resulting frames will satisfy `width = aspect * height`. + pub aspect: Option, /// The nodes to be stacked. pub children: Vec, } @@ -23,7 +27,7 @@ pub enum StackChild { impl Layout for StackNode { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec { - let mut layouter = StackLayouter::new(self.dirs, areas.clone()); + let mut layouter = StackLayouter::new(self.dirs, self.aspect, areas.clone()); for child in &self.children { match *child { StackChild::Spacing(amount) => layouter.push_spacing(amount), @@ -52,6 +56,7 @@ impl From for AnyNode { struct StackLayouter { dirs: Gen, + aspect: Option, main: SpecAxis, areas: Areas, finished: Vec, @@ -61,9 +66,14 @@ struct StackLayouter { } impl StackLayouter { - fn new(dirs: Gen, areas: Areas) -> Self { + fn new(dirs: Gen, aspect: Option, mut areas: Areas) -> Self { + if let Some(aspect) = aspect { + areas.apply_aspect_ratio(aspect); + } + Self { dirs, + aspect, main: dirs.main.axis(), areas, finished: vec![], @@ -112,7 +122,7 @@ impl StackLayouter { if fixed.vertical { full.height } else { used.height }, ); - if let Some(aspect) = self.areas.aspect { + if let Some(aspect) = self.aspect { let width = size .width .max(aspect * size.height) @@ -162,6 +172,10 @@ impl StackLayouter { self.areas.next(); self.ruler = Align::Start; self.size = Gen::ZERO; + + if let Some(aspect) = self.aspect { + self.areas.apply_aspect_ratio(aspect); + } } fn finish(mut self) -> Vec { diff --git a/src/library/shapes.rs b/src/library/shapes.rs index 6c6a2f0b3..c87a0ac3d 100644 --- a/src/library/shapes.rs +++ b/src/library/shapes.rs @@ -59,17 +59,19 @@ fn rect_impl( body: TemplateValue, ) -> Value { Value::template(name, move |ctx| { - let child = ctx.exec_group(&body).into(); - let node = FixedNode { width, height, aspect, child }; + let mut stack = ctx.exec_group(&body); + stack.aspect = aspect; + + let fixed = FixedNode { width, height, child: stack.into() }; if let Some(color) = fill { ctx.push(BackgroundNode { shape: BackgroundShape::Rect, fill: Fill::Color(color), - child: node.into(), + child: fixed.into(), }); } else { - ctx.push(node); + ctx.push(fixed); } }) } @@ -133,14 +135,15 @@ fn ellipse_impl( // perfectly into the ellipse. const PAD: f64 = 0.5 - SQRT_2 / 4.0; - let child = ctx.exec_group(&body).into(); - let node = FixedNode { + let mut stack = ctx.exec_group(&body); + stack.aspect = aspect; + + let fixed = FixedNode { width, height, - aspect, child: PadNode { padding: Sides::uniform(Relative::new(PAD).into()), - child, + child: stack.into(), } .into(), }; @@ -149,10 +152,10 @@ fn ellipse_impl( ctx.push(BackgroundNode { shape: BackgroundShape::Ellipse, fill: Fill::Color(color), - child: node.into(), + child: fixed.into(), }); } else { - ctx.push(node); + ctx.push(fixed); } }) }