typst/src/library/sized.rs
2021-12-05 12:54:03 +01:00

71 lines
2.4 KiB
Rust

use super::prelude::*;
/// `box`: Size content and place it into a paragraph.
pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let width = args.named("width")?;
let height = args.named("height")?;
let body: Node = args.find().unwrap_or_default();
Ok(Value::inline(
body.into_block().sized(Spec::new(width, height)),
))
}
/// `block`: Place content into the flow.
pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let body: Node = args.find().unwrap_or_default();
Ok(Value::block(body.into_block()))
}
/// A node that sizes its child.
#[derive(Debug, Hash)]
pub struct SizedNode {
/// How to size the node horizontally and vertically.
pub sizing: Spec<Option<Linear>>,
/// The node to be sized.
pub child: PackedNode,
}
impl Layout for SizedNode {
fn layout(
&self,
ctx: &mut LayoutContext,
regions: &Regions,
) -> Vec<Constrained<Rc<Frame>>> {
let is_auto = self.sizing.map_is_none();
let is_rel = self.sizing.map(|s| s.map_or(false, Linear::is_relative));
// The "pod" is the region into which the child will be layouted.
let pod = {
// Resolve the sizing to a concrete size.
let size = self
.sizing
.zip(regions.base)
.map(|(s, b)| s.map(|v| v.resolve(b)))
.unwrap_or(regions.current);
// Select the appropriate base and expansion for the child depending
// on whether it is automatically or linearly sized.
let base = is_auto.select(regions.base, size);
let expand = regions.expand | !is_auto;
Regions::one(size, base, expand)
};
let mut frames = self.child.layout(ctx, &pod);
let Constrained { item: frame, cts } = &mut frames[0];
// Ensure frame size matches regions size if expansion is on.
let target = regions.expand.select(regions.current, frame.size);
Rc::make_mut(frame).resize(target, Align::LEFT_TOP);
// Set base & exact constraints if the child is automatically sized
// since we don't know what the child might have done. Also set base if
// our sizing is relative.
*cts = Constraints::new(regions.expand);
cts.exact = regions.current.filter(regions.expand | is_auto);
cts.base = regions.base.filter(is_rel | is_auto);
frames
}
}