mirror of
https://github.com/typst/typst
synced 2025-05-17 02:25:27 +08:00
Move aspect ratio into stack
This commit is contained in:
parent
1003d320d4
commit
24c4a746bc
@ -197,7 +197,7 @@ impl StackBuilder {
|
||||
children.extend(last.any());
|
||||
children.push(par);
|
||||
}
|
||||
StackNode { dirs, children }
|
||||
StackNode { dirs, aspect: None, children }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user