diff options
Diffstat (limited to 'library/src/layout/container.rs')
| -rw-r--r-- | library/src/layout/container.rs | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs new file mode 100644 index 00000000..d65b78b6 --- /dev/null +++ b/library/src/layout/container.rs @@ -0,0 +1,80 @@ +use crate::prelude::*; + +/// An inline-level container that sizes content. +#[derive(Debug, Clone, 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(_: &mut 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>, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult<Vec<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 frames = self.child.layout_inline(world, &pod, styles)?; + + // Ensure frame size matches regions size if expansion is on. + let frame = &mut frames[0]; + let target = regions.expand.select(regions.first, frame.size()); + frame.resize(target, Align::LEFT_TOP); + + Ok(frames) + } +} + +/// A block-level container that places content into a separate flow. +#[derive(Debug, Clone, Hash)] +pub struct BlockNode(pub Content); + +#[node(LayoutBlock)] +impl BlockNode { + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { + Ok(Self(args.eat()?.unwrap_or_default()).pack()) + } +} + +impl LayoutBlock for BlockNode { + fn layout_block( + &self, + world: Tracked<dyn World>, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult<Vec<Frame>> { + self.0.layout_block(world, regions, styles) + } +} |
