diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-10-27 19:04:55 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-27 18:04:55 +0000 |
| commit | be7cfc85d08c545abfac08098b7b33b4bd71f37e (patch) | |
| tree | f4137fa2aaa57babae1f7603a9b2ed7e688f43d8 /crates/typst-library/src/layout/layout.rs | |
| parent | b8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff) | |
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-library/src/layout/layout.rs')
| -rw-r--r-- | crates/typst-library/src/layout/layout.rs | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/crates/typst-library/src/layout/layout.rs b/crates/typst-library/src/layout/layout.rs new file mode 100644 index 00000000..c3d112e1 --- /dev/null +++ b/crates/typst-library/src/layout/layout.rs @@ -0,0 +1,107 @@ +use comemo::Track; +use typst_syntax::Span; + +use crate::diag::SourceResult; +use crate::engine::Engine; +use crate::foundations::{ + dict, elem, func, Content, Context, Func, NativeElement, Packed, Show, StyleChain, +}; +use crate::introspection::Locatable; +use crate::layout::{BlockElem, Size}; + +/// Provides access to the current outer container's (or page's, if none) +/// dimensions (width and height). +/// +/// Accepts a function that receives a single parameter, which is a dictionary +/// with keys `width` and `height`, both of type [`length`]. The function is +/// provided [context], meaning you don't need to use it in combination with the +/// `context` keyword. This is why [`measure`] can be called in the example +/// below. +/// +/// ```example +/// #let text = lorem(30) +/// #layout(size => [ +/// #let (height,) = measure( +/// block(width: size.width, text), +/// ) +/// This text is #height high with +/// the current page width: \ +/// #text +/// ]) +/// ``` +/// +/// Note that the `layout` function forces its contents into a [block]-level +/// container, so placement relative to the page or pagebreaks are not possible +/// within it. +/// +/// If the `layout` call is placed inside a box with a width of `{800pt}` and a +/// height of `{400pt}`, then the specified function will be given the argument +/// `{(width: 800pt, height: 400pt)}`. If it is placed directly into the page, it +/// receives the page's dimensions minus its margins. This is mostly useful in +/// combination with [measurement]($measure). +/// +/// You can also use this function to resolve [`ratio`] to fixed lengths. This +/// might come in handy if you're building your own layout abstractions. +/// +/// ```example +/// #layout(size => { +/// let half = 50% * size.width +/// [Half a page is #half wide.] +/// }) +/// ``` +/// +/// Note that the width or height provided by `layout` will be infinite if the +/// corresponding page dimension is set to `{auto}`. +#[func] +pub fn layout( + /// The call span of this function. + span: Span, + /// A function to call with the outer container's size. Its return value is + /// displayed in the document. + /// + /// The container's size is given as a [dictionary] with the keys `width` + /// and `height`. + /// + /// This function is called once for each time the content returned by + /// `layout` appears in the document. This makes it possible to generate + /// content that depends on the dimensions of its container. + func: Func, +) -> Content { + LayoutElem::new(func).pack().spanned(span) +} + +/// Executes a `layout` call. +#[elem(Locatable, Show)] +struct LayoutElem { + /// The function to call with the outer container's (or page's) size. + #[required] + func: Func, +} + +impl Show for Packed<LayoutElem> { + fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> { + Ok(BlockElem::multi_layouter( + self.clone(), + |elem, engine, locator, styles, regions| { + // Gets the current region's base size, which will be the size of the + // outer container, or of the page if there is no such container. + let Size { x, y } = regions.base(); + let loc = elem.location().unwrap(); + let context = Context::new(Some(loc), Some(styles)); + let result = elem + .func() + .call( + engine, + context.track(), + [dict! { "width" => x, "height" => y }], + )? + .display(); + (engine.routines.layout_fragment)( + engine, &result, locator, styles, regions, + ) + }, + ) + .pack() + .spanned(self.span())) + } +} |
