Move aspect ratio into stack

This commit is contained in:
Laurenz 2021-05-17 15:23:04 +02:00
parent 1003d320d4
commit 24c4a746bc
5 changed files with 41 additions and 43 deletions

View File

@ -197,7 +197,7 @@ impl StackBuilder {
children.extend(last.any());
children.push(par);
}
StackNode { dirs, children }
StackNode { dirs, aspect: None, children }
}
}

View File

@ -7,10 +7,6 @@ pub struct FixedNode {
pub width: Option<Linear>,
/// The fixed height, if any.
pub height: Option<Linear>,
/// The fixed aspect ratio between width and height, if any.
///
/// The resulting frame will satisfy `width = aspect * height`.
pub aspect: Option<f64>,
/// 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<Frame> {
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)
}
}

View File

@ -155,10 +155,6 @@ pub struct Areas {
///
/// If this is false, the frame will shrink to fit its content.
pub fixed: Spec<bool>,
/// The aspect ratio the resulting frame should respect.
///
/// This property is only handled by the stack layouter.
pub aspect: Option<f64>,
}
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<f64>) -> Self {
self.aspect = aspect;
self
}
/// Map all areas.
pub fn map<F>(&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));
}
}

View File

@ -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<Dir>,
/// The fixed aspect ratio between width and height, if any.
///
/// The resulting frames will satisfy `width = aspect * height`.
pub aspect: Option<f64>,
/// The nodes to be stacked.
pub children: Vec<StackChild>,
}
@ -23,7 +27,7 @@ pub enum StackChild {
impl Layout for StackNode {
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Frame> {
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<StackNode> for AnyNode {
struct StackLayouter {
dirs: Gen<Dir>,
aspect: Option<f64>,
main: SpecAxis,
areas: Areas,
finished: Vec<Frame>,
@ -61,9 +66,14 @@ struct StackLayouter {
}
impl StackLayouter {
fn new(dirs: Gen<Dir>, areas: Areas) -> Self {
fn new(dirs: Gen<Dir>, aspect: Option<f64>, 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<Frame> {

View File

@ -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);
}
})
}