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.extend(last.any());
|
||||||
children.push(par);
|
children.push(par);
|
||||||
}
|
}
|
||||||
StackNode { dirs, children }
|
StackNode { dirs, aspect: None, children }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,6 @@ pub struct FixedNode {
|
|||||||
pub width: Option<Linear>,
|
pub width: Option<Linear>,
|
||||||
/// The fixed height, if any.
|
/// The fixed height, if any.
|
||||||
pub height: Option<Linear>,
|
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.
|
/// The child node whose size to fix.
|
||||||
pub child: AnyNode,
|
pub child: AnyNode,
|
||||||
}
|
}
|
||||||
@ -18,21 +14,13 @@ pub struct FixedNode {
|
|||||||
impl Layout for FixedNode {
|
impl Layout for FixedNode {
|
||||||
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Frame> {
|
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Frame> {
|
||||||
let Areas { current, full, .. } = areas;
|
let Areas { current, full, .. } = areas;
|
||||||
|
let size = Size::new(
|
||||||
let full = Size::new(
|
self.width.map_or(current.width, |w| w.resolve(full.width)),
|
||||||
self.width.map(|w| w.resolve(full.width)).unwrap_or(current.width),
|
self.height.map_or(current.height, |h| h.resolve(full.height)),
|
||||||
self.height.map(|h| h.resolve(full.height)).unwrap_or(current.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 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)
|
self.child.layout(ctx, &areas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,10 +155,6 @@ pub struct Areas {
|
|||||||
///
|
///
|
||||||
/// If this is false, the frame will shrink to fit its content.
|
/// If this is false, the frame will shrink to fit its content.
|
||||||
pub fixed: Spec<bool>,
|
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 {
|
impl Areas {
|
||||||
@ -170,7 +166,6 @@ impl Areas {
|
|||||||
backlog: vec![],
|
backlog: vec![],
|
||||||
last: None,
|
last: None,
|
||||||
fixed,
|
fixed,
|
||||||
aspect: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,16 +177,9 @@ impl Areas {
|
|||||||
backlog: vec![],
|
backlog: vec![],
|
||||||
last: Some(size),
|
last: Some(size),
|
||||||
fixed,
|
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.
|
/// Map all areas.
|
||||||
pub fn map<F>(&self, mut f: F) -> Self
|
pub fn map<F>(&self, mut f: F) -> Self
|
||||||
where
|
where
|
||||||
@ -203,7 +191,6 @@ impl Areas {
|
|||||||
backlog: self.backlog.iter().copied().map(|s| f(s)).collect(),
|
backlog: self.backlog.iter().copied().map(|s| f(s)).collect(),
|
||||||
last: self.last.map(f),
|
last: self.last.map(f),
|
||||||
fixed: self.fixed,
|
fixed: self.fixed,
|
||||||
aspect: self.aspect,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,4 +211,10 @@ impl Areas {
|
|||||||
self.current.is_nan() || size.is_nan() || self.current == size
|
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`
|
/// The children are stacked along the `main` direction. The `cross`
|
||||||
/// direction is required for aligning the children.
|
/// direction is required for aligning the children.
|
||||||
pub dirs: Gen<Dir>,
|
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.
|
/// The nodes to be stacked.
|
||||||
pub children: Vec<StackChild>,
|
pub children: Vec<StackChild>,
|
||||||
}
|
}
|
||||||
@ -23,7 +27,7 @@ pub enum StackChild {
|
|||||||
|
|
||||||
impl Layout for StackNode {
|
impl Layout for StackNode {
|
||||||
fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Frame> {
|
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 {
|
for child in &self.children {
|
||||||
match *child {
|
match *child {
|
||||||
StackChild::Spacing(amount) => layouter.push_spacing(amount),
|
StackChild::Spacing(amount) => layouter.push_spacing(amount),
|
||||||
@ -52,6 +56,7 @@ impl From<StackNode> for AnyNode {
|
|||||||
|
|
||||||
struct StackLayouter {
|
struct StackLayouter {
|
||||||
dirs: Gen<Dir>,
|
dirs: Gen<Dir>,
|
||||||
|
aspect: Option<f64>,
|
||||||
main: SpecAxis,
|
main: SpecAxis,
|
||||||
areas: Areas,
|
areas: Areas,
|
||||||
finished: Vec<Frame>,
|
finished: Vec<Frame>,
|
||||||
@ -61,9 +66,14 @@ struct StackLayouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
Self {
|
||||||
dirs,
|
dirs,
|
||||||
|
aspect,
|
||||||
main: dirs.main.axis(),
|
main: dirs.main.axis(),
|
||||||
areas,
|
areas,
|
||||||
finished: vec![],
|
finished: vec![],
|
||||||
@ -112,7 +122,7 @@ impl StackLayouter {
|
|||||||
if fixed.vertical { full.height } else { used.height },
|
if fixed.vertical { full.height } else { used.height },
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(aspect) = self.areas.aspect {
|
if let Some(aspect) = self.aspect {
|
||||||
let width = size
|
let width = size
|
||||||
.width
|
.width
|
||||||
.max(aspect * size.height)
|
.max(aspect * size.height)
|
||||||
@ -162,6 +172,10 @@ impl StackLayouter {
|
|||||||
self.areas.next();
|
self.areas.next();
|
||||||
self.ruler = Align::Start;
|
self.ruler = Align::Start;
|
||||||
self.size = Gen::ZERO;
|
self.size = Gen::ZERO;
|
||||||
|
|
||||||
|
if let Some(aspect) = self.aspect {
|
||||||
|
self.areas.apply_aspect_ratio(aspect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(mut self) -> Vec<Frame> {
|
fn finish(mut self) -> Vec<Frame> {
|
||||||
|
@ -59,17 +59,19 @@ fn rect_impl(
|
|||||||
body: TemplateValue,
|
body: TemplateValue,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
Value::template(name, move |ctx| {
|
Value::template(name, move |ctx| {
|
||||||
let child = ctx.exec_group(&body).into();
|
let mut stack = ctx.exec_group(&body);
|
||||||
let node = FixedNode { width, height, aspect, child };
|
stack.aspect = aspect;
|
||||||
|
|
||||||
|
let fixed = FixedNode { width, height, child: stack.into() };
|
||||||
|
|
||||||
if let Some(color) = fill {
|
if let Some(color) = fill {
|
||||||
ctx.push(BackgroundNode {
|
ctx.push(BackgroundNode {
|
||||||
shape: BackgroundShape::Rect,
|
shape: BackgroundShape::Rect,
|
||||||
fill: Fill::Color(color),
|
fill: Fill::Color(color),
|
||||||
child: node.into(),
|
child: fixed.into(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ctx.push(node);
|
ctx.push(fixed);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -133,14 +135,15 @@ fn ellipse_impl(
|
|||||||
// perfectly into the ellipse.
|
// perfectly into the ellipse.
|
||||||
const PAD: f64 = 0.5 - SQRT_2 / 4.0;
|
const PAD: f64 = 0.5 - SQRT_2 / 4.0;
|
||||||
|
|
||||||
let child = ctx.exec_group(&body).into();
|
let mut stack = ctx.exec_group(&body);
|
||||||
let node = FixedNode {
|
stack.aspect = aspect;
|
||||||
|
|
||||||
|
let fixed = FixedNode {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
aspect,
|
|
||||||
child: PadNode {
|
child: PadNode {
|
||||||
padding: Sides::uniform(Relative::new(PAD).into()),
|
padding: Sides::uniform(Relative::new(PAD).into()),
|
||||||
child,
|
child: stack.into(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
};
|
};
|
||||||
@ -149,10 +152,10 @@ fn ellipse_impl(
|
|||||||
ctx.push(BackgroundNode {
|
ctx.push(BackgroundNode {
|
||||||
shape: BackgroundShape::Ellipse,
|
shape: BackgroundShape::Ellipse,
|
||||||
fill: Fill::Color(color),
|
fill: Fill::Color(color),
|
||||||
child: node.into(),
|
child: fixed.into(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ctx.push(node);
|
ctx.push(fixed);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user