typst/library/src/layout/container.rs
2022-11-25 12:16:13 +01:00

100 lines
3.0 KiB
Rust

use super::VNode;
use crate::prelude::*;
/// An inline-level container that sizes content.
#[derive(Debug, Hash)]
pub struct BoxNode {
/// How to size the content horizontally and vertically.
pub sizing: Axes<Option<Rel<Length>>>,
/// The content to be sized.
pub child: Content,
}
#[node(LayoutInline)]
impl BoxNode {
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
let width = args.named("width")?;
let height = args.named("height")?;
let body = args.eat::<Content>()?.unwrap_or_default();
Ok(body.boxed(Axes::new(width, height)))
}
}
impl LayoutInline for BoxNode {
fn layout_inline(
&self,
world: Tracked<dyn World>,
styles: StyleChain,
regions: &Regions,
) -> SourceResult<Frame> {
// 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
.resolve(styles)
.zip(regions.base)
.map(|(s, b)| s.map(|v| v.relative_to(b)))
.unwrap_or(regions.first);
// Select the appropriate base and expansion for the child depending
// on whether it is automatically or relatively sized.
let is_auto = self.sizing.as_ref().map(Option::is_none);
let base = is_auto.select(regions.base, size);
let expand = regions.expand | !is_auto;
Regions::one(size, base, expand)
};
// Layout the child.
let mut frame = self.child.layout_inline(world, styles, &pod)?;
// Ensure frame size matches regions size if expansion is on.
let target = regions.expand.select(regions.first, frame.size());
frame.resize(target, Align::LEFT_TOP);
Ok(frame)
}
}
/// A block-level container that places content into a separate flow.
#[derive(Debug, Hash)]
pub struct BlockNode(pub Content);
#[node(LayoutBlock)]
impl BlockNode {
/// The spacing between the previous and this block.
#[property(skip)]
pub const ABOVE: VNode = VNode::block_spacing(Em::new(1.2).into());
/// The spacing between this and the following block.
#[property(skip)]
pub const BELOW: VNode = VNode::block_spacing(Em::new(1.2).into());
fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
Ok(Self(args.eat()?.unwrap_or_default()).pack())
}
fn set(...) {
let spacing = args.named("spacing")?.map(VNode::block_spacing);
styles.set_opt(
Self::ABOVE,
args.named("above")?.map(VNode::block_around).or(spacing),
);
styles.set_opt(
Self::BELOW,
args.named("below")?.map(VNode::block_around).or(spacing),
);
}
}
impl LayoutBlock for BlockNode {
fn layout_block(
&self,
world: Tracked<dyn World>,
styles: StyleChain,
regions: &Regions,
) -> SourceResult<Vec<Frame>> {
self.0.layout_block(world, styles, regions)
}
}