diff options
| author | Pg Biel <9021226+PgBiel@users.noreply.github.com> | 2023-04-06 10:36:36 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-06 15:36:36 +0200 |
| commit | ca71081d05e1d2fabf65f4d4576ef0ac095221f9 (patch) | |
| tree | a705ca338a6f172d68635281df5ef587872f376a /library/src/meta | |
| parent | 5cb226026e104c0ddb855c3ad6fee5212a796006 (diff) | |
Add 'layout' function to obtain the size of the outer container (#557)
Diffstat (limited to 'library/src/meta')
| -rw-r--r-- | library/src/meta/context.rs | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/library/src/meta/context.rs b/library/src/meta/context.rs index 7426d27d..3902f47a 100644 --- a/library/src/meta/context.rs +++ b/library/src/meta/context.rs @@ -121,3 +121,101 @@ impl Show for StyleElem { Ok(self.func().call_vt(vt, [styles.to_map().into()])?.display()) } } + +/// Provides access to the current outer container's (or page's, if none) size (width and height). +/// +/// The given function must accept a single parameter, `size`, which is a dictionary with keys +/// `width` and `height`, both having the type [`length`]($type/length). +/// +/// That is, if this `layout` call is done inside (for example) a box of size 800pt (width) +/// by 400pt (height), then the specified function will be given the parameter +/// `(width: 800pt, height: 400pt)`. +/// +/// If, however, this `layout` call is placed directly on the page, not inside any container, +/// then the function will be given `(width: page_width, height: page_height)`, where `page_width` +/// and `page_height` correspond to the current page's respective dimensions, minus its margins. +/// +/// This is useful, for example, to convert a [`ratio`]($type/ratio) value (such as `5%`, `100%` +/// etc.), which are usually based upon the outer container's dimensions (precisely what this +/// function gives), to a fixed length (in `pt`). +/// +/// This is also useful if you're trying to make content fit a certain box, and doing certain +/// arithmetic using `pt` (for example, comparing different lengths) is required. +/// +/// Please note: This function may provide a width or height of `infpt` if one of the page +/// dimensions is `auto`, under certain circumstances. This should not normally occur for +/// usual page sizes, however. +/// +/// ```example +/// layout(size => { +/// // work with the width and height of the container we're in +/// // using size.width and size.height +/// }) +/// +/// layout(size => { +/// // convert 49% (of page width) to 'pt' +/// // note that "ratio" values are always relative to a certain, possibly arbitrary length, +/// // but it's usually the current container's width or height (e.g., for table columns, +/// // 15% would be relative to the width, but, for rows, it would be relative to the height). +/// let percentage_of_width = (49% / 1%) * 0.01 * size.width +/// // ... use the converted value ... +/// }) +/// +/// // The following two boxes are equivalent, and will have rectangles sized 200pt and 40pt: +/// +/// #box(width: 200pt, height: 40pt, { +/// rect(width: 100%, height: 100%) +/// }) +/// +/// #box(width: 200pt, height: 40pt, layout(size => { +/// rect(width: size.width, height: size.height) +/// })) +/// ``` +/// +/// Display: Layout +/// Category: meta +/// Returns: content +#[func] +pub fn layout( + /// A function to call with the outer container's size. Its return value is displayed + /// in the document. + /// + /// This function is called once for each time the content returned by + /// `layout` appears in the document. That makes it possible to generate + /// content that depends on the size of the container it is inside. + func: Func, +) -> Value { + LayoutElem::new(func).pack().into() +} + +/// Executes a `layout` call. +/// +/// Display: Layout +/// Category: special +#[element(Layout)] +struct LayoutElem { + /// The function to call with the outer container's (or page's) size. + #[required] + func: Func, +} + +impl Layout for LayoutElem { + fn layout( + &self, + vt: &mut Vt, + styles: StyleChain, + regions: Regions, + ) -> SourceResult<Fragment> { + // 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 size_dict = dict! { "width" => x, "height" => y }.into(); + + let result = self + .func() + .call_vt(vt, [size_dict])? // calls func(size) + .display(); + + result.layout(vt, styles, regions) + } +} |
