diff options
Diffstat (limited to 'crates/typst-library/src/layout')
20 files changed, 309 insertions, 426 deletions
diff --git a/crates/typst-library/src/layout/align.rs b/crates/typst-library/src/layout/align.rs index 5f7e8bc0..f080f677 100644 --- a/crates/typst-library/src/layout/align.rs +++ b/crates/typst-library/src/layout/align.rs @@ -2,7 +2,7 @@ use crate::prelude::*; /// Aligns content horizontally and vertically. /// -/// ## Example { #example } +/// # Example /// ```example /// #set align(center) /// @@ -11,43 +11,9 @@ use crate::prelude::*; /// Not left nor right, it stands alone \ /// A work of art, a visual throne /// ``` -/// -/// Display: Align -/// Category: layout -#[element(Show)] +#[elem(Show)] pub struct AlignElem { - /// The alignment along both axes. - /// - /// Possible values for horizontal alignments are: - /// - `start` - /// - `end` - /// - `left` - /// - `center` - /// - `right` - /// - /// The `start` and `end` alignments are relative to the current [text - /// direction]($func/text.dir). - /// - /// Possible values for vertical alignments are: - /// - `top` - /// - `horizon` - /// - `bottom` - /// - /// You can use the `axis` method on a single-axis alignment to obtain - /// whether it is `{"horizontal"}` or `{"vertical"}`. You can also use the - /// `inv` method to obtain its inverse alignment. For example, - /// `{top.axis()}` is `{"vertical"}`, while `{top.inv()}` is equal to - /// `{bottom}`. - /// - /// To align along both axes at the same time, add the two alignments using - /// the `+` operator to get a `2d alignment`. For example, `top + right` - /// aligns the content to the top right corner. - /// - /// For 2d alignments, the `x` and `y` fields hold their horizontal and - /// vertical components, respectively. Additionally, you can use the `inv` - /// method to obtain a 2d alignment with both components inverted. For - /// instance, `{(top + right).x}` is `right`, `{(top + right).y}` is `top`, - /// and `{(top + right).inv()}` is equal to `bottom + left`. + /// The [alignment]($alignment) along both axes. /// /// ```example /// #set page(height: 6cm) @@ -61,8 +27,8 @@ pub struct AlignElem { /// ``` #[positional] #[fold] - #[default(Axes::new(GenAlign::Start, GenAlign::Specific(Align::Top)))] - pub alignment: Axes<Option<GenAlign>>, + #[default] + pub alignment: Align, /// The content to align. #[required] @@ -72,8 +38,6 @@ pub struct AlignElem { impl Show for AlignElem { #[tracing::instrument(name = "AlignElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self - .body() - .styled(Self::set_alignment(self.alignment(styles).map(Some)))) + Ok(self.body().styled(Self::set_alignment(self.alignment(styles)))) } } diff --git a/crates/typst-library/src/layout/columns.rs b/crates/typst-library/src/layout/columns.rs index 6645ba9e..961bedc5 100644 --- a/crates/typst-library/src/layout/columns.rs +++ b/crates/typst-library/src/layout/columns.rs @@ -10,9 +10,9 @@ use crate::text::TextElem; /// necessary. /// /// If you need to insert columns across your whole document, you can use the -/// [`{page}` function's `columns` parameter]($func/page.columns) instead. +/// [`{page}` function's `columns` parameter]($page.columns) instead. /// -/// ## Example { #example } +/// # Example /// ```example /// = Towards Advanced Deep Learning /// @@ -32,10 +32,7 @@ use crate::text::TextElem; /// increasingly been used to solve a /// variety of problems. /// ``` -/// -/// Display: Columns -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct ColumnsElem { /// The number of columns. #[positional] @@ -132,11 +129,11 @@ impl Layout for ColumnsElem { /// Forces a column break. /// -/// The function will behave like a [page break]($func/pagebreak) when used in a +/// The function will behave like a [page break]($pagebreak) when used in a /// single column layout or the last column on a page. Otherwise, content after /// the column break will be placed in the next column. /// -/// ## Example { #example } +/// # Example /// ```example /// #set page(columns: 2) /// Preliminary findings from our @@ -153,10 +150,7 @@ impl Layout for ColumnsElem { /// understanding of the fundamental /// laws of nature. /// ``` -/// -/// Display: Column Break -/// Category: layout -#[element(Behave)] +#[elem(title = "Column Break", Behave)] pub struct ColbreakElem { /// If `{true}`, the column break is skipped if the current column is /// already empty. diff --git a/crates/typst-library/src/layout/container.rs b/crates/typst-library/src/layout/container.rs index c79669d0..e966398f 100644 --- a/crates/typst-library/src/layout/container.rs +++ b/crates/typst-library/src/layout/container.rs @@ -11,7 +11,7 @@ use crate::prelude::*; /// elements into a paragraph. Boxes take the size of their contents by default /// but can also be sized explicitly. /// -/// ## Example { #example } +/// # Example /// ```example /// Refer to the docs /// #box( @@ -20,15 +20,12 @@ use crate::prelude::*; /// ) /// for more information. /// ``` -/// -/// Display: Box -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct BoxElem { /// The width of the box. /// - /// Boxes can have [fractional]($type/fraction) widths, as the example - /// below demonstrates. + /// Boxes can have [fractional]($fraction) widths, as the example below + /// demonstrates. /// /// _Note:_ Currently, only boxes and only their widths might be fractionally /// sized within paragraphs. Support for fractionally sized images, shapes, @@ -51,23 +48,29 @@ pub struct BoxElem { pub baseline: Rel<Length>, /// The box's background color. See the - /// [rectangle's documentation]($func/rect.fill) for more details. + /// [rectangle's documentation]($rect.fill) for more details. pub fill: Option<Paint>, /// The box's border color. See the - /// [rectangle's documentation]($func/rect.stroke) for more details. + /// [rectangle's documentation]($rect.stroke) for more details. #[resolve] #[fold] - pub stroke: Sides<Option<Option<PartialStroke>>>, + pub stroke: Sides<Option<Option<Stroke>>>, - /// How much to round the box's corners. See the [rectangle's - /// documentation]($func/rect.radius) for more details. + /// How much to round the box's corners. See the + /// [rectangle's documentation]($rect.radius) for more details. #[resolve] #[fold] pub radius: Corners<Option<Rel<Length>>>, - /// How much to pad the box's content. See the [rectangle's - /// documentation]($func/rect.inset) for more details. + /// How much to pad the box's content. + /// + /// _Note:_ When the box contains text, its exact size depends on the + /// current [text edges]($text.top-edge). + /// + /// ```example + /// #rect(inset: 0pt)[Tight] + /// ``` #[resolve] #[fold] pub inset: Sides<Option<Rel<Length>>>, @@ -76,7 +79,7 @@ pub struct BoxElem { /// /// This is useful to prevent padding from affecting line layout. For a /// generalized version of the example below, see the documentation for the - /// [raw text's block parameter]($func/raw.block). + /// [raw text's block parameter]($raw.block). /// /// ```example /// An inline @@ -119,8 +122,7 @@ impl Layout for BoxElem { let expand = sizing.as_ref().map(Smart::is_custom); let size = sizing .resolve(styles) - .zip(regions.base()) - .map(|(s, b)| s.map(|v| v.relative_to(b))) + .zip_map(regions.base(), |s, b| s.map(|v| v.relative_to(b))) .unwrap_or(regions.base()); // Apply inset. @@ -151,7 +153,7 @@ impl Layout for BoxElem { // Prepare fill and stroke. let fill = self.fill(styles); - let stroke = self.stroke(styles).map(|s| s.map(PartialStroke::unwrap_or_default)); + let stroke = self.stroke(styles).map(|s| s.map(Stroke::unwrap_or_default)); // Add fill and/or stroke. if fill.is_some() || stroke.iter().any(Option::is_some) { @@ -172,7 +174,7 @@ impl Layout for BoxElem { /// Such a container can be used to separate content, size it, and give it a /// background or border. /// -/// ## Examples { #examples } +/// # Examples /// With a block, you can give a background to content while still allowing it /// to break across multiple pages. /// ```example @@ -196,10 +198,7 @@ impl Layout for BoxElem { /// = Blocky /// More text. /// ``` -/// -/// Display: Block -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct BlockElem { /// The block's width. /// @@ -215,7 +214,7 @@ pub struct BlockElem { pub width: Smart<Rel<Length>>, /// The block's height. When the height is larger than the remaining space - /// on a page and [`breakable`]($func/block.breakable) is `{true}`, the + /// on a page and [`breakable`]($block.breakable) is `{true}`, the /// block will continue on the next page with the remaining height. /// /// ```example @@ -244,29 +243,29 @@ pub struct BlockElem { pub breakable: bool, /// The block's background color. See the - /// [rectangle's documentation]($func/rect.fill) for more details. + /// [rectangle's documentation]($rect.fill) for more details. pub fill: Option<Paint>, /// The block's border color. See the - /// [rectangle's documentation]($func/rect.stroke) for more details. + /// [rectangle's documentation]($rect.stroke) for more details. #[resolve] #[fold] - pub stroke: Sides<Option<Option<PartialStroke>>>, + pub stroke: Sides<Option<Option<Stroke>>>, - /// How much to round the block's corners. See the [rectangle's - /// documentation]($func/rect.radius) for more details. + /// How much to round the block's corners. See the + /// [rectangle's documentation]($rect.radius) for more details. #[resolve] #[fold] pub radius: Corners<Option<Rel<Length>>>, - /// How much to pad the block's content. See the [rectangle's - /// documentation]($func/rect.inset) for more details. + /// How much to pad the block's content. See the + /// [box's documentation]($box.inset) for more details. #[resolve] #[fold] pub inset: Sides<Option<Rel<Length>>>, /// How much to expand the block's size without affecting the layout. See - /// the [rectangle's documentation]($func/rect.outset) for more details. + /// the [box's documentation]($box.outset) for more details. #[resolve] #[fold] pub outset: Sides<Option<Rel<Length>>>, @@ -352,8 +351,7 @@ impl Layout for BlockElem { let mut expand = sizing.as_ref().map(Smart::is_custom); let mut size = sizing .resolve(styles) - .zip(regions.base()) - .map(|(s, b)| s.map(|v| v.relative_to(b))) + .zip_map(regions.base(), |s, b| s.map(|v| v.relative_to(b))) .unwrap_or(regions.base()); // Layout the child. @@ -418,7 +416,7 @@ impl Layout for BlockElem { // Prepare fill and stroke. let fill = self.fill(styles); - let stroke = self.stroke(styles).map(|s| s.map(PartialStroke::unwrap_or_default)); + let stroke = self.stroke(styles).map(|s| s.map(Stroke::unwrap_or_default)); // Add fill and/or stroke. if fill.is_some() || stroke.iter().any(Option::is_some) { diff --git a/crates/typst-library/src/layout/enum.rs b/crates/typst-library/src/layout/enum.rs index 8f23b6dc..c505f189 100644 --- a/crates/typst-library/src/layout/enum.rs +++ b/crates/typst-library/src/layout/enum.rs @@ -11,7 +11,7 @@ use super::GridLayouter; /// /// Displays a sequence of items vertically and numbers them consecutively. /// -/// ## Example { #example } +/// # Example /// ```example /// Automatically numbered: /// + Preparations @@ -41,8 +41,8 @@ use super::GridLayouter; /// + Don't forget step two /// ``` /// -/// You can also use [`enum.item`]($func/enum.item) to programmatically -/// customize the number of each item in the enumeration: +/// You can also use [`enum.item`]($enum.item) to programmatically customize the +/// number of each item in the enumeration: /// /// ```example /// #enum( @@ -52,7 +52,7 @@ use super::GridLayouter; /// ) /// ``` /// -/// ## Syntax { #syntax } +/// # Syntax /// This functions also has dedicated syntax: /// /// - Starting a line with a plus sign creates an automatically numbered @@ -63,18 +63,11 @@ use super::GridLayouter; /// Enumeration items can contain multiple paragraphs and other block-level /// content. All content that is indented more than an item's marker becomes /// part of that item. -/// -/// Display: Numbered List -/// Category: layout -#[element(Layout)] -#[scope( - scope.define("item", EnumItem::func()); - scope -)] +#[elem(scope, title = "Numbered List", Layout)] pub struct EnumElem { /// If this is `{false}`, the items are spaced apart with - /// [enum spacing]($func/enum.spacing). If it is `{true}`, they use normal - /// [leading]($func/par.leading) instead. This makes the enumeration more + /// [enum spacing]($enum.spacing). If it is `{true}`, they use normal + /// [leading]($par.leading) instead. This makes the enumeration more /// compact, which can look better if the items are short. /// /// In markup mode, the value of this parameter is determined based on @@ -95,7 +88,7 @@ pub struct EnumElem { pub tight: bool, /// How to number the enumeration. Accepts a - /// [numbering pattern or function]($func/numbering). + /// [numbering pattern or function]($numbering). /// /// If the numbering pattern contains multiple counting symbols, they apply /// to nested enums. If given a function, the function receives one argument @@ -153,7 +146,7 @@ pub struct EnumElem { /// The spacing between the items of a wide (non-tight) enumeration. /// - /// If set to `{auto}`, uses the spacing [below blocks]($func/block.below). + /// If set to `{auto}`, uses the spacing [below blocks]($block.below). pub spacing: Smart<Spacing>, /// The horizontal alignment that enum numbers should have. @@ -177,8 +170,8 @@ pub struct EnumElem { /// 16. Sixteen /// 32. Thirty two /// ```` - #[default(HorizontalAlign(GenAlign::End))] - pub number_align: HorizontalAlign, + #[default(HAlign::End)] + pub number_align: HAlign, /// The numbered list's items. /// @@ -201,6 +194,12 @@ pub struct EnumElem { parents: Parent, } +#[scope] +impl EnumElem { + #[elem] + type EnumItem; +} + impl Layout for EnumElem { #[tracing::instrument(name = "EnumElem::layout", skip_all)] fn layout( @@ -225,11 +224,10 @@ impl Layout for EnumElem { let full = self.full(styles); // Horizontally align based on the given respective parameter. - // Vertically align to the top to avoid inheriting 'horizon' or - // 'bottom' alignment from the context and having the number be - // displaced in relation to the item it refers to. - let number_align: Axes<Option<GenAlign>> = - Axes::new(self.number_align(styles).into(), Align::Top.into()).map(Some); + // Vertically align to the top to avoid inheriting `horizon` or `bottom` + // alignment from the context and having the number be displaced in + // relation to the item it refers to. + let number_align = self.number_align(styles) + VAlign::Top; for item in self.children() { number = item.number(styles).unwrap_or(number); @@ -278,10 +276,7 @@ impl Layout for EnumElem { } /// An enumeration item. -/// -/// Display: Numbered List Item -/// Category: layout -#[element] +#[elem(name = "item", title = "Numbered List Item")] pub struct EnumItem { /// The item's number. #[positional] diff --git a/crates/typst-library/src/layout/flow.rs b/crates/typst-library/src/layout/flow.rs index 4ce78c94..fe6e9398 100644 --- a/crates/typst-library/src/layout/flow.rs +++ b/crates/typst-library/src/layout/flow.rs @@ -14,10 +14,7 @@ use crate::visualize::{ /// /// This element is responsible for layouting both the top-level content flow /// and the contents of boxes. -/// -/// Display: Flow -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct FlowElem { /// The children that will be arranges into a flow. #[variadic] @@ -62,7 +59,7 @@ impl Layout for FlowElem { frame.meta(styles, true); layouter.items.push(FlowItem::Frame { frame, - aligns: Axes::new(Align::Top, Align::Left), + align: Axes::splat(FixedAlign::Start), sticky: true, movable: false, }); @@ -128,12 +125,12 @@ enum FlowItem { /// A frame for a layouted block, how to align it, whether it sticks to the /// item after it (for orphan prevention), and whether it is movable /// (to keep it together with its footnotes). - Frame { frame: Frame, aligns: Axes<Align>, sticky: bool, movable: bool }, + Frame { frame: Frame, align: Axes<FixedAlign>, sticky: bool, movable: bool }, /// An absolutely placed frame. Placed { frame: Frame, - x_align: Align, - y_align: Smart<Option<Align>>, + x_align: FixedAlign, + y_align: Smart<Option<FixedAlign>>, delta: Axes<Rel<Abs>>, float: bool, clearance: Abs, @@ -209,7 +206,7 @@ impl<'a> FlowLayouter<'a> { par: &ParElem, styles: StyleChain, ) -> SourceResult<()> { - let aligns = AlignElem::alignment_in(styles).resolve(styles); + let align = AlignElem::alignment_in(styles).resolve(styles); let leading = ParElem::leading_in(styles); let consecutive = self.last_was_par; let lines = par @@ -242,7 +239,7 @@ impl<'a> FlowLayouter<'a> { self.layout_item( vt, - FlowItem::Frame { frame, aligns, sticky: false, movable: true }, + FlowItem::Frame { frame, align, sticky: false, movable: true }, )?; } @@ -258,11 +255,11 @@ impl<'a> FlowLayouter<'a> { content: &dyn Layout, styles: StyleChain, ) -> SourceResult<()> { - let aligns = AlignElem::alignment_in(styles).resolve(styles); + let align = AlignElem::alignment_in(styles).resolve(styles); let sticky = BlockElem::sticky_in(styles); let pod = Regions::one(self.regions.base(), Axes::splat(false)); let frame = content.layout(vt, styles, pod)?.into_frame(); - self.layout_item(vt, FlowItem::Frame { frame, aligns, sticky, movable: true })?; + self.layout_item(vt, FlowItem::Frame { frame, align, sticky, movable: true })?; self.last_was_par = false; Ok(()) } @@ -278,10 +275,10 @@ impl<'a> FlowLayouter<'a> { let clearance = placed.clearance(styles); let alignment = placed.alignment(styles); let delta = Axes::new(placed.dx(styles), placed.dy(styles)).resolve(styles); - let x_align = alignment.map_or(Align::Center, |aligns| { - aligns.x.unwrap_or(GenAlign::Start).resolve(styles) + let x_align = alignment.map_or(FixedAlign::Center, |align| { + align.x().unwrap_or_default().resolve(styles) }); - let y_align = alignment.map(|align| align.y.resolve(styles)); + let y_align = alignment.map(|align| align.y().map(VAlign::fix)); let frame = placed.layout(vt, styles, self.regions)?.into_frame(); let item = FlowItem::Placed { frame, x_align, y_align, delta, float, clearance }; self.layout_item(vt, item) @@ -309,7 +306,7 @@ impl<'a> FlowLayouter<'a> { } // How to align the block. - let aligns = if let Some(align) = block.to::<AlignElem>() { + let align = if let Some(align) = block.to::<AlignElem>() { align.alignment(styles) } else if let Some((_, local)) = block.to_styled() { AlignElem::alignment_in(styles.chain(local)) @@ -332,7 +329,7 @@ impl<'a> FlowLayouter<'a> { self.finish_region(vt)?; } - let item = FlowItem::Frame { frame, aligns, sticky, movable: false }; + let item = FlowItem::Frame { frame, align, sticky, movable: false }; self.layout_item(vt, item)?; } @@ -404,14 +401,14 @@ impl<'a> FlowLayouter<'a> { - (frame.height() + clearance) / 2.0) / self.regions.full; let better_align = - if ratio <= 0.5 { Align::Bottom } else { Align::Top }; + if ratio <= 0.5 { FixedAlign::End } else { FixedAlign::Start }; *y_align = Smart::Custom(Some(better_align)); } // Add some clearance so that the float doesn't touch the main // content. frame.size_mut().y += clearance; - if *y_align == Smart::Custom(Some(Align::Bottom)) { + if *y_align == Smart::Custom(Some(FixedAlign::End)) { frame.translate(Point::with_y(clearance)); } @@ -459,8 +456,10 @@ impl<'a> FlowLayouter<'a> { } FlowItem::Placed { float: false, .. } => {} FlowItem::Placed { frame, float: true, y_align, .. } => match y_align { - Smart::Custom(Some(Align::Top)) => float_top_height += frame.height(), - Smart::Custom(Some(Align::Bottom)) => { + Smart::Custom(Some(FixedAlign::Start)) => { + float_top_height += frame.height() + } + Smart::Custom(Some(FixedAlign::End)) => { float_bottom_height += frame.height() } _ => {} @@ -486,7 +485,7 @@ impl<'a> FlowLayouter<'a> { } let mut output = Frame::new(size); - let mut ruler = Align::Top; + let mut ruler = FixedAlign::Start; let mut float_top_offset = Abs::zero(); let mut offset = float_top_height; let mut float_bottom_offset = Abs::zero(); @@ -502,9 +501,9 @@ impl<'a> FlowLayouter<'a> { let remaining = self.initial.y - used.y; offset += v.share(fr, remaining); } - FlowItem::Frame { frame, aligns, .. } => { - ruler = ruler.max(aligns.y); - let x = aligns.x.position(size.x - frame.width()); + FlowItem::Frame { frame, align, .. } => { + ruler = ruler.max(align.y); + let x = align.x.position(size.x - frame.width()); let y = offset + ruler.position(size.y - used.y); let pos = Point::new(x, y); offset += frame.height(); @@ -514,12 +513,12 @@ impl<'a> FlowLayouter<'a> { let x = x_align.position(size.x - frame.width()); let y = if float { match y_align { - Smart::Custom(Some(Align::Top)) => { + Smart::Custom(Some(FixedAlign::Start)) => { let y = float_top_offset; float_top_offset += frame.height(); y } - Smart::Custom(Some(Align::Bottom)) => { + Smart::Custom(Some(FixedAlign::End)) => { let y = size.y - footnote_height - float_bottom_height + float_bottom_offset; float_bottom_offset += frame.height(); @@ -537,7 +536,7 @@ impl<'a> FlowLayouter<'a> { }; let pos = Point::new(x, y) - + delta.zip(size).map(|(d, s)| d.relative_to(s)).to_point(); + + delta.zip_map(size, Rel::relative_to).to_point(); output.push_frame(pos, frame); } diff --git a/crates/typst-library/src/layout/grid.rs b/crates/typst-library/src/layout/grid.rs index 4f5175e9..06962524 100644 --- a/crates/typst-library/src/layout/grid.rs +++ b/crates/typst-library/src/layout/grid.rs @@ -34,7 +34,7 @@ use super::Sizing; /// instead of an array. For example, `columns:` `{3}` is equivalent to /// `columns:` `{(auto, auto, auto)}`. /// -/// ## Example { #example } +/// # Example /// ```example /// #set text(10pt, style: "italic") /// #let cell = rect.with( @@ -58,10 +58,7 @@ use super::Sizing; /// cell[One more thing...], /// ) /// ``` -/// -/// Display: Grid -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct GridElem { /// The column sizes. /// diff --git a/crates/typst-library/src/layout/hide.rs b/crates/typst-library/src/layout/hide.rs index c6e83e0c..7f17a7d7 100644 --- a/crates/typst-library/src/layout/hide.rs +++ b/crates/typst-library/src/layout/hide.rs @@ -7,15 +7,12 @@ use crate::prelude::*; /// content. It may also be useful to redact content because its arguments are /// not included in the output. /// -/// ## Example { #example } +/// # Example /// ```example /// Hello Jane \ /// #hide[Hello] Joe /// ``` -/// -/// Display: Hide -/// Category: layout -#[element(Show)] +#[elem(Show)] pub struct HideElem { /// The content to hide. #[required] diff --git a/crates/typst-library/src/layout/list.rs b/crates/typst-library/src/layout/list.rs index 8bb8744b..a9dad85b 100644 --- a/crates/typst-library/src/layout/list.rs +++ b/crates/typst-library/src/layout/list.rs @@ -9,7 +9,7 @@ use super::GridLayouter; /// Displays a sequence of items vertically, with each item introduced by a /// marker. /// -/// ## Example { #example } +/// # Example /// ```example /// Normal list. /// - Text @@ -30,24 +30,17 @@ use super::GridLayouter; /// ) /// ``` /// -/// ## Syntax { #syntax } +/// # Syntax /// This functions also has dedicated syntax: Start a line with a hyphen, /// followed by a space to create a list item. A list item can contain multiple /// paragraphs and other block-level content. All content that is indented /// more than an item's marker becomes part of that item. -/// -/// Display: Bullet List -/// Category: layout -#[element(Layout)] -#[scope( - scope.define("item", ListItem::func()); - scope -)] +#[elem(scope, title = "Bullet List", Layout)] pub struct ListElem { - /// If this is `{false}`, the items are spaced apart with [list - /// spacing]($func/list.spacing). If it is `{true}`, they use normal - /// [leading]($func/par.leading) instead. This makes the list more compact, - /// which can look better if the items are short. + /// If this is `{false}`, the items are spaced apart with + /// [list spacing]($list.spacing). If it is `{true}`, they use normal + /// [leading]($par.leading) instead. This makes the list more compact, which + /// can look better if the items are short. /// /// In markup mode, the value of this parameter is determined based on /// whether items are separated with a blank line. If items directly follow @@ -98,7 +91,7 @@ pub struct ListElem { /// The spacing between the items of a wide (non-tight) list. /// - /// If set to `{auto}`, uses the spacing [below blocks]($func/block.below). + /// If set to `{auto}`, uses the spacing [below blocks]($block.below). pub spacing: Smart<Spacing>, /// The bullet list's children. @@ -120,6 +113,12 @@ pub struct ListElem { depth: Depth, } +#[scope] +impl ListElem { + #[elem] + type ListItem; +} + impl Layout for ListElem { #[tracing::instrument(name = "ListElem::layout", skip_all)] fn layout( @@ -142,7 +141,7 @@ impl Layout for ListElem { .marker(styles) .resolve(vt, depth)? // avoid '#set align' interference with the list - .aligned(Align::LEFT_TOP.into()); + .aligned(HAlign::Start + VAlign::Top); let mut cells = vec![]; for item in self.children() { @@ -170,10 +169,7 @@ impl Layout for ListElem { } /// A bullet list item. -/// -/// Display: Bullet List Item -/// Category: layout -#[element] +#[elem(name = "item", title = "Bullet List Item")] pub struct ListItem { /// The item's body. #[required] diff --git a/crates/typst-library/src/layout/measure.rs b/crates/typst-library/src/layout/measure.rs index eb8e509e..d41b7f95 100644 --- a/crates/typst-library/src/layout/measure.rs +++ b/crates/typst-library/src/layout/measure.rs @@ -2,13 +2,13 @@ use crate::prelude::*; /// Measures the layouted size of content. /// -/// The `measure` function lets you determine the layouted size of content. -/// Note that an infinite space is assumed, therefore the measured height/width -/// may not necessarily match the final height/width of the measured content. -/// If you want to measure in the current layout dimensions, you can combined -/// `measure` and [`layout`]($func/layout). +/// The `measure` function lets you determine the layouted size of content. Note +/// that an infinite space is assumed, therefore the measured height/width may +/// not necessarily match the final height/width of the measured content. If you +/// want to measure in the current layout dimensions, you can combine `measure` +/// and [`layout`]($layout). /// -/// # Example { #example } +/// # Example /// The same content can have a different size depending on the styles that /// are active when it is layouted. For example, in the example below /// `[#content]` is of course bigger when we increase the font size. @@ -21,8 +21,8 @@ use crate::prelude::*; /// ``` /// /// To do a meaningful measurement, you therefore first need to retrieve the -/// active styles with the [`style`]($func/style) function. You can then pass -/// them to the `measure` function. +/// active styles with the [`style`]($style) function. You can then pass them to +/// the `measure` function. /// /// ```example /// #let thing(body) = style(styles => { @@ -35,18 +35,15 @@ use crate::prelude::*; /// ``` /// /// The measure function returns a dictionary with the entries `width` and -/// `height`, both of type [`length`]($type/length). -/// -/// Display: Measure -/// Category: layout +/// `height`, both of type [`length`]($length). #[func] pub fn measure( + /// The virtual machine. + vm: &mut Vm, /// The content whose size to measure. content: Content, /// The styles with which to layout the content. styles: Styles, - /// The virtual machine. - vm: &mut Vm, ) -> SourceResult<Dict> { let pod = Regions::one(Axes::splat(Abs::inf()), Axes::splat(false)); let styles = StyleChain::new(&styles); diff --git a/crates/typst-library/src/layout/mod.rs b/crates/typst-library/src/layout/mod.rs index 3334d5aa..ace5cd6e 100644 --- a/crates/typst-library/src/layout/mod.rs +++ b/crates/typst-library/src/layout/mod.rs @@ -10,7 +10,8 @@ mod fragment; mod grid; mod hide; mod list; -mod measure; +#[path = "measure.rs"] +mod measure_; mod pad; mod page; mod par; @@ -32,7 +33,7 @@ pub use self::fragment::*; pub use self::grid::*; pub use self::hide::*; pub use self::list::*; -pub use self::measure::*; +pub use self::measure_::*; pub use self::pad::*; pub use self::page::*; pub use self::par::*; @@ -57,7 +58,7 @@ use crate::math::{EquationElem, LayoutMath}; use crate::meta::DocumentElem; use crate::prelude::*; use crate::shared::BehavedBuilder; -use crate::text::{LinebreakElem, SmartQuoteElem, SpaceElem, TextElem}; +use crate::text::{LinebreakElem, SmartquoteElem, SpaceElem, TextElem}; use crate::visualize::{ CircleElem, EllipseElem, ImageElem, LineElem, PathElem, PolygonElem, RectElem, SquareElem, @@ -65,43 +66,39 @@ use crate::visualize::{ /// Hook up all layout definitions. pub(super) fn define(global: &mut Scope) { - global.define("page", PageElem::func()); - global.define("pagebreak", PagebreakElem::func()); - global.define("v", VElem::func()); - global.define("par", ParElem::func()); - global.define("parbreak", ParbreakElem::func()); - global.define("h", HElem::func()); - global.define("box", BoxElem::func()); - global.define("block", BlockElem::func()); - global.define("list", ListElem::func()); - global.define("enum", EnumElem::func()); - global.define("terms", TermsElem::func()); - global.define("table", TableElem::func()); - global.define("stack", StackElem::func()); - global.define("grid", GridElem::func()); - global.define("columns", ColumnsElem::func()); - global.define("colbreak", ColbreakElem::func()); - global.define("place", PlaceElem::func()); - global.define("align", AlignElem::func()); - global.define("pad", PadElem::func()); - global.define("repeat", RepeatElem::func()); - global.define("move", MoveElem::func()); - global.define("scale", ScaleElem::func()); - global.define("rotate", RotateElem::func()); - global.define("hide", HideElem::func()); - global.define("measure", measure_func()); - global.define("ltr", Dir::LTR); - global.define("rtl", Dir::RTL); - global.define("ttb", Dir::TTB); - global.define("btt", Dir::BTT); - global.define("start", GenAlign::Start); - global.define("end", GenAlign::End); - global.define("left", GenAlign::Specific(Align::Left)); - global.define("center", GenAlign::Specific(Align::Center)); - global.define("right", GenAlign::Specific(Align::Right)); - global.define("top", GenAlign::Specific(Align::Top)); - global.define("horizon", GenAlign::Specific(Align::Horizon)); - global.define("bottom", GenAlign::Specific(Align::Bottom)); + global.category("layout"); + global.define_type::<Length>(); + global.define_type::<Angle>(); + global.define_type::<Ratio>(); + global.define_type::<Rel<Length>>(); + global.define_type::<Fr>(); + global.define_type::<Dir>(); + global.define_type::<Align>(); + global.define_elem::<PageElem>(); + global.define_elem::<PagebreakElem>(); + global.define_elem::<VElem>(); + global.define_elem::<ParElem>(); + global.define_elem::<ParbreakElem>(); + global.define_elem::<HElem>(); + global.define_elem::<BoxElem>(); + global.define_elem::<BlockElem>(); + global.define_elem::<ListElem>(); + global.define_elem::<EnumElem>(); + global.define_elem::<TermsElem>(); + global.define_elem::<TableElem>(); + global.define_elem::<StackElem>(); + global.define_elem::<GridElem>(); + global.define_elem::<ColumnsElem>(); + global.define_elem::<ColbreakElem>(); + global.define_elem::<PlaceElem>(); + global.define_elem::<AlignElem>(); + global.define_elem::<PadElem>(); + global.define_elem::<RepeatElem>(); + global.define_elem::<MoveElem>(); + global.define_elem::<ScaleElem>(); + global.define_elem::<RotateElem>(); + global.define_elem::<HideElem>(); + global.define_func::<measure>(); } /// Root-level layout. @@ -598,7 +595,7 @@ impl<'a> ParBuilder<'a> { || content.is::<TextElem>() || content.is::<HElem>() || content.is::<LinebreakElem>() - || content.is::<SmartQuoteElem>() + || content.is::<SmartquoteElem>() || content.to::<EquationElem>().map_or(false, |elem| !elem.block(styles)) || content.is::<BoxElem>() { diff --git a/crates/typst-library/src/layout/pad.rs b/crates/typst-library/src/layout/pad.rs index a3d5646b..d1b0cb1f 100644 --- a/crates/typst-library/src/layout/pad.rs +++ b/crates/typst-library/src/layout/pad.rs @@ -5,7 +5,7 @@ use crate::prelude::*; /// The spacing can be specified for each side individually, or for all sides at /// once by specifying a positional argument. /// -/// ## Example { #example } +/// # Example /// ```example /// #set align(center) /// @@ -13,10 +13,7 @@ use crate::prelude::*; /// _Typing speeds can be /// measured in words per minute._ /// ``` -/// -/// Display: Padding -/// Category: layout -#[element(Layout)] +#[elem(title = "Padding", Layout)] pub struct PadElem { /// The padding at the left side. #[parse( @@ -120,6 +117,5 @@ fn shrink(size: Size, padding: Sides<Rel<Abs>>) -> Size { /// <=> (1 - p.rel) * w = s + p.abs /// <=> w = (s + p.abs) / (1 - p.rel) fn grow(size: Size, padding: Sides<Rel<Abs>>) -> Size { - size.zip(padding.sum_by_axis()) - .map(|(s, p)| (s + p.abs).safe_div(1.0 - p.rel.get())) + size.zip_map(padding.sum_by_axis(), |s, p| (s + p.abs).safe_div(1.0 - p.rel.get())) } diff --git a/crates/typst-library/src/layout/page.rs b/crates/typst-library/src/layout/page.rs index 4ef90753..d182a417 100644 --- a/crates/typst-library/src/layout/page.rs +++ b/crates/typst-library/src/layout/page.rs @@ -1,6 +1,8 @@ use std::ptr; use std::str::FromStr; +use typst::eval::AutoValue; + use super::{AlignElem, ColumnsElem}; use crate::meta::{Counter, CounterKey, Numbering}; use crate::prelude::*; @@ -18,17 +20,14 @@ use crate::text::TextElem; /// The [Guide for Page Setup]($guides/page-setup-guide) explains how to use /// this and related functions to set up a document with many examples. /// -/// ## Example { #example } +/// # Example /// ```example /// >>> #set page(margin: auto) /// #set page("us-letter") /// /// There you go, US friends! /// ``` -/// -/// Display: Page -/// Category: layout -#[element] +#[elem] pub struct PageElem { /// A standard paper size to set width and height. #[external] @@ -59,9 +58,9 @@ pub struct PageElem { /// The height of the page. /// /// If this is set to `{auto}`, page breaks can only be triggered manually - /// by inserting a [page break]($func/pagebreak). Most examples throughout - /// this documentation use `{auto}` for the height of the page to - /// dynamically grow and shrink to fit their content. + /// by inserting a [page break]($pagebreak). Most examples throughout this + /// documentation use `{auto}` for the height of the page to dynamically + /// grow and shrink to fit their content. #[resolve] #[parse( args.named("height")? @@ -103,9 +102,9 @@ pub struct PageElem { /// - `bottom`: The bottom margin. /// - `left`: The left margin. /// - `inside`: The margin at the inner side of the page (where the - /// [binding]($func/page.binding) is). + /// [binding]($page.binding) is). /// - `outside`: The margin at the outer side of the page (opposite to the - /// [binding]($func/page.binding)). + /// [binding]($page.binding)). /// - `x`: The horizontal margins. /// - `y`: The vertical margins. /// - `rest`: The margins on all sides except those for which the @@ -132,7 +131,7 @@ pub struct PageElem { /// On which side the pages will be bound. /// - /// - `{auto}`: Equivalent to `left` if the [text direction]($func/text.dir) + /// - `{auto}`: Equivalent to `left` if the [text direction]($text.dir) /// is left-to-right and `right` if it is right-to-left. /// - `left`: Bound on the left side. /// - `right`: Bound on the right side. @@ -144,7 +143,7 @@ pub struct PageElem { /// How many columns the page has. /// /// If you need to insert columns into a page or other container, you can - /// also use the [`columns` function]($func/columns). + /// also use the [`columns` function]($columns). /// /// ```example:single /// #set page(columns: 2, height: 4.8cm) @@ -175,7 +174,7 @@ pub struct PageElem { /// ``` pub fill: Option<Paint>, - /// How to [number]($func/numbering) the pages. + /// How to [number]($numbering) the pages. /// /// If an explicit `footer` (or `header` for top-aligned numbering) is /// given, the numbering is ignored. @@ -207,17 +206,17 @@ pub struct PageElem { /// /// #lorem(30) /// ``` - #[default(Align::Center.into())] + #[default(HAlign::Center + VAlign::Bottom)] #[parse({ - let spanned: Option<Spanned<Axes<_>>> = args.named("number-align")?; - if let Some(Spanned { v, span }) = spanned { - if matches!(v.y, Some(GenAlign::Specific(Align::Horizon))) { + let option: Option<Spanned<Align>> = args.named("number-align")?; + if let Some(Spanned { v: align, span }) = option { + if align.y() == Some(VAlign::Horizon) { bail!(span, "page number cannot be `horizon`-aligned"); } } - spanned.map(|s| s.v) + option.map(|spanned| spanned.v) })] - pub number_align: Axes<Option<GenAlign>>, + pub number_align: Align, /// The page's header. Fills the top margin of each page. /// @@ -245,7 +244,7 @@ pub struct PageElem { /// /// For just a page number, the `numbering` property, typically suffices. If /// you want to create a custom footer, but still display the page number, - /// you can directly access the [page counter]($func/counter). + /// you can directly access the [page counter]($counter). /// /// ```example /// #set par(justify: true) @@ -406,14 +405,14 @@ impl PageElem { // We interpret the Y alignment as selecting header or footer // and then ignore it for aligning the actual number. - if let Some(x) = number_align.x { - counter = counter.aligned(Axes::with_x(Some(x))); + if let Some(x) = number_align.x() { + counter = counter.aligned(x.into()); } counter }); - if matches!(number_align.y, Some(GenAlign::Specific(Align::Top))) { + if matches!(number_align.y(), Some(VAlign::Top)) { header = header.or(numbering_marginal); } else { footer = footer.or(numbering_marginal); @@ -461,16 +460,16 @@ impl PageElem { let ascent = header_ascent.relative_to(margin.top); pos = Point::with_x(margin.left); area = Size::new(pw, margin.top - ascent); - align = Align::Bottom.into(); + align = Align::BOTTOM; } else if ptr::eq(marginal, &footer) { let descent = footer_descent.relative_to(margin.bottom); pos = Point::new(margin.left, size.y - margin.bottom + descent); area = Size::new(pw, margin.bottom - descent); - align = Align::Top.into(); + align = Align::TOP; } else { pos = Point::zero(); area = size; - align = Align::CENTER_HORIZON.into(); + align = HAlign::Center + VAlign::Horizon; }; let pod = Regions::one(area, Axes::splat(true)); @@ -626,12 +625,12 @@ impl Binding { cast! { Binding, self => match self { - Self::Left => GenAlign::Specific(Align::Left).into_value(), - Self::Right => GenAlign::Specific(Align::Right).into_value(), + Self::Left => Align::LEFT.into_value(), + Self::Right => Align::RIGHT.into_value(), }, - v: GenAlign => match v { - GenAlign::Specific(Align::Left) => Self::Left, - GenAlign::Specific(Align::Right) => Self::Right, + v: Align => match v { + Align::LEFT => Self::Left, + Align::RIGHT => Self::Right, _ => bail!("must be `left` or `right`"), }, } @@ -669,7 +668,7 @@ cast! { /// /// Must not be used inside any containers. /// -/// ## Example { #example } +/// # Example /// ```example /// The next page contains /// more details on compound theory. @@ -678,10 +677,7 @@ cast! { /// == Compound Theory /// In 1984, the first ... /// ``` -/// -/// Display: Page Break -/// Category: layout -#[element] +#[elem(title = "Page Break")] pub struct PagebreakElem { /// If `{true}`, the page break is skipped if the current page is already /// empty. diff --git a/crates/typst-library/src/layout/par.rs b/crates/typst-library/src/layout/par.rs index 39689477..e28e661c 100644 --- a/crates/typst-library/src/layout/par.rs +++ b/crates/typst-library/src/layout/par.rs @@ -16,7 +16,7 @@ use crate::layout::AlignElem; use crate::math::EquationElem; use crate::prelude::*; use crate::text::{ - is_gb_style, shape, LinebreakElem, Quoter, Quotes, ShapedText, SmartQuoteElem, + is_gb_style, shape, LinebreakElem, Quoter, Quotes, ShapedText, SmartquoteElem, SpaceElem, TextElem, }; @@ -26,7 +26,7 @@ use crate::text::{ /// properties, it can also be used to explicitly render its argument onto a /// paragraph of its own. /// -/// ## Example { #example } +/// # Example /// ```example /// #show par: set block(spacing: 0.65em) /// #set par( @@ -45,10 +45,7 @@ use crate::text::{ /// let $a$ be the smallest of the /// three integers. Then, we ... /// ``` -/// -/// Display: Paragraph -/// Category: layout -#[element(Construct)] +#[elem(title = "Paragraph", Construct)] pub struct ParElem { /// The spacing between lines. #[resolve] @@ -57,13 +54,13 @@ pub struct ParElem { /// Whether to justify text in its line. /// - /// Hyphenation will be enabled for justified paragraphs if the [text - /// property hyphenate]($func/text.hyphenate) is set to `{auto}` and the - /// current language is known. + /// Hyphenation will be enabled for justified paragraphs if the + /// [text function's `hyphenate` property]($text.hyphenate) is set to + /// `{auto}` and the current language is known. /// - /// Note that the current [alignment]($func/align) still has an effect on - /// the placement of the last line except if it ends with a [justified line - /// break]($func/linebreak.justify). + /// Note that the current [alignment]($align) still has an effect on the + /// placement of the last line except if it ends with a + /// [justified line break]($linebreak.justify). #[default(false)] pub justify: bool, @@ -88,7 +85,6 @@ pub struct ParElem { /// challenging to break in a visually /// pleasing way. /// ``` - #[default] pub linebreaks: Smart<Linebreaks>, /// The indent the first line of a paragraph should have. @@ -98,7 +94,7 @@ pub struct ParElem { /// /// By typographic convention, paragraph breaks are indicated either by some /// space between paragraphs or by indented first lines. Consider reducing - /// the [paragraph spacing]($func/block.spacing) to the [`leading`] when + /// the [paragraph spacing]($block.spacing) to the [`leading`] when /// using this property (e.g. using /// `[#show par: set block(spacing: 0.65em)]`). pub first_line_indent: Length, @@ -219,7 +215,7 @@ pub enum Linebreaks { /// [for loops]($scripting/#loops). Multiple consecutive /// paragraph breaks collapse into a single one. /// -/// ## Example { #example } +/// # Example /// ```example /// #for i in range(3) { /// [Blind text #i: ] @@ -228,13 +224,10 @@ pub enum Linebreaks { /// } /// ``` /// -/// ## Syntax { #syntax } +/// # Syntax /// Instead of calling this function, you can insert a blank line into your /// markup to create a paragraph break. -/// -/// Display: Paragraph Break -/// Category: layout -#[element(Unlabellable)] +#[elem(title = "Paragraph Break", Unlabellable)] pub struct ParbreakElem {} impl Unlabellable for ParbreakElem {} @@ -266,8 +259,8 @@ struct Preparation<'a> { hyphenate: Option<bool>, /// The text language if it's the same for all children. lang: Option<Lang>, - /// The paragraph's resolved alignment. - align: Align, + /// The paragraph's resolved horizontal alignment. + align: FixedAlign, /// Whether to justify the paragraph. justify: bool, /// The paragraph's hanging indent. @@ -550,7 +543,7 @@ fn collect<'a>( let first_line_indent = ParElem::first_line_indent_in(*styles); if !first_line_indent.is_zero() && consecutive - && AlignElem::alignment_in(*styles).x.resolve(*styles) + && AlignElem::alignment_in(*styles).resolve(*styles).x == TextElem::dir_in(*styles).start().into() { full.push(SPACING_REPLACE); @@ -593,15 +586,15 @@ fn collect<'a>( let c = if elem.justify(styles) { '\u{2028}' } else { '\n' }; full.push(c); Segment::Text(c.len_utf8()) - } else if let Some(elem) = child.to::<SmartQuoteElem>() { + } else if let Some(elem) = child.to::<SmartquoteElem>() { let prev = full.len(); - if SmartQuoteElem::enabled_in(styles) { + if SmartquoteElem::enabled_in(styles) { let lang = TextElem::lang_in(styles); let region = TextElem::region_in(styles); let quotes = Quotes::from_lang( lang, region, - SmartQuoteElem::alternative_in(styles), + SmartquoteElem::alternative_in(styles), ); let peeked = iter.peek().and_then(|child| { let child = if let Some((child, _)) = child.to_styled() { @@ -611,7 +604,7 @@ fn collect<'a>( }; if let Some(elem) = child.to::<TextElem>() { elem.text().chars().next() - } else if child.is::<SmartQuoteElem>() { + } else if child.is::<SmartquoteElem>() { Some('"') } else if child.is::<SpaceElem>() || child.is::<HElem>() @@ -642,7 +635,7 @@ fn collect<'a>( }; if let Some(last) = full.chars().last() { - quoter.last(last, child.is::<SmartQuoteElem>()); + quoter.last(last, child.is::<SmartquoteElem>()); } spans.push(segment.len(), child.span()); @@ -673,9 +666,10 @@ fn prepare<'a>( styles: StyleChain<'a>, region: Size, ) -> SourceResult<Preparation<'a>> { + let dir = TextElem::dir_in(styles); let bidi = BidiInfo::new( text, - match TextElem::dir_in(styles) { + match dir { Dir::LTR => Some(BidiLevel::ltr()), Dir::RTL => Some(BidiLevel::rtl()), _ => None, @@ -734,7 +728,7 @@ fn prepare<'a>( styles, hyphenate: shared_get(styles, children, TextElem::hyphenate_in), lang: shared_get(styles, children, TextElem::lang_in), - align: AlignElem::alignment_in(styles).x.resolve(styles), + align: AlignElem::alignment_in(styles).resolve(styles).x, justify: ParElem::justify_in(styles), hang: ParElem::hanging_indent_in(styles), }) diff --git a/crates/typst-library/src/layout/place.rs b/crates/typst-library/src/layout/place.rs index 95c042ff..39a38b16 100644 --- a/crates/typst-library/src/layout/place.rs +++ b/crates/typst-library/src/layout/place.rs @@ -7,7 +7,7 @@ use crate::prelude::*; /// other content in the container. Page margins will be respected. /// /// -/// ## Example { #example } +/// # Example /// ```example /// #set page(height: 60pt) /// Hello, world! @@ -20,10 +20,7 @@ use crate::prelude::*; /// ), /// ) /// ``` -/// -/// Display: Place -/// Category: layout -#[element(Layout, Behave)] +#[elem(Layout, Behave)] pub struct PlaceElem { /// Relative to which position in the parent container to place the content. /// @@ -34,8 +31,8 @@ pub struct PlaceElem { /// that axis will be ignored, instead, the item will be placed in the /// origin of the axis. #[positional] - #[default(Smart::Custom(Axes::with_x(Some(GenAlign::Start))))] - pub alignment: Smart<Axes<Option<GenAlign>>>, + #[default(Smart::Custom(Align::START))] + pub alignment: Smart<Align>, /// Whether the placed element has floating layout. /// @@ -98,16 +95,7 @@ impl Layout for PlaceElem { let float = self.float(styles); let alignment = self.alignment(styles); - if float - && !matches!( - alignment, - Smart::Auto - | Smart::Custom(Axes { - y: Some(GenAlign::Specific(Align::Top | Align::Bottom)), - .. - }) - ) - { + if float && alignment.map_or(false, |align| align.y() == Some(VAlign::Horizon)) { bail!(self.span(), "floating placement must be `auto`, `top`, or `bottom`"); } else if !float && alignment.is_auto() { return Err("automatic positioning is only available for floating placement") @@ -115,9 +103,7 @@ impl Layout for PlaceElem { .at(self.span()); } - let child = self.body().aligned( - alignment.unwrap_or_else(|| Axes::with_x(Some(Align::Center.into()))), - ); + let child = self.body().aligned(alignment.unwrap_or_else(|| Align::CENTER)); let pod = Regions::one(base, Axes::splat(false)); let frame = child.layout(vt, styles, pod)?.into_frame(); diff --git a/crates/typst-library/src/layout/repeat.rs b/crates/typst-library/src/layout/repeat.rs index 646eb991..41dede51 100644 --- a/crates/typst-library/src/layout/repeat.rs +++ b/crates/typst-library/src/layout/repeat.rs @@ -12,7 +12,7 @@ use super::AlignElem; /// Errors if there no bounds on the available space, as it would create /// infinite content. /// -/// ## Example { #example } +/// # Example /// ```example /// Sign on the dotted line: /// #box(width: 1fr, repeat[.]) @@ -23,10 +23,7 @@ use super::AlignElem; /// Berlin, the 22nd of December, 2022 /// ] /// ``` -/// -/// Display: Repeat -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct RepeatElem { /// The content to repeat. #[required] @@ -43,7 +40,7 @@ impl Layout for RepeatElem { ) -> SourceResult<Fragment> { let pod = Regions::one(regions.size, Axes::new(false, false)); let piece = self.body().layout(vt, styles, pod)?.into_frame(); - let align = AlignElem::alignment_in(styles).x.resolve(styles); + let align = AlignElem::alignment_in(styles).resolve(styles); let fill = regions.size.x; let width = piece.width(); @@ -64,7 +61,7 @@ impl Layout for RepeatElem { let mut offset = Abs::zero(); if count == 1.0 { - offset += align.position(remaining); + offset += align.x.position(remaining); } if width > Abs::zero() { diff --git a/crates/typst-library/src/layout/spacing.rs b/crates/typst-library/src/layout/spacing.rs index 69a5d952..868b3d50 100644 --- a/crates/typst-library/src/layout/spacing.rs +++ b/crates/typst-library/src/layout/spacing.rs @@ -8,20 +8,17 @@ use crate::prelude::*; /// remaining space on the line is distributed among all fractional spacings /// according to their relative fractions. /// -/// ## Example { #example } +/// # Example /// ```example /// First #h(1cm) Second \ /// First #h(30%) Second \ /// First #h(2fr) Second #h(1fr) Third /// ``` /// -/// ## Mathematical Spacing { #math-spacing } +/// # Mathematical Spacing { #math-spacing } /// In [mathematical formulas]($category/math), you can additionally use these /// constants to add spacing between elements: `thin`, `med`, `thick`, `quad`. -/// -/// Display: Spacing (H) -/// Category: layout -#[element(Behave)] +#[elem(title = "Spacing (H)", Behave)] pub struct HElem { /// How much spacing to insert. #[required] @@ -79,7 +76,7 @@ impl Behave for HElem { /// the remaining space on the page is distributed among all fractional spacings /// according to their relative fractions. /// -/// ## Example { #example } +/// # Example /// ```example /// #grid( /// rows: 3cm, @@ -93,10 +90,7 @@ impl Behave for HElem { /// [A #v(1fr) B], /// ) /// ``` -/// -/// Display: Spacing (V) -/// Category: layout -#[element(Behave)] +#[elem(title = "Spacing (V)", Behave)] pub struct VElem { /// How much spacing to insert. #[required] diff --git a/crates/typst-library/src/layout/stack.rs b/crates/typst-library/src/layout/stack.rs index 52a2f289..d3fcba8d 100644 --- a/crates/typst-library/src/layout/stack.rs +++ b/crates/typst-library/src/layout/stack.rs @@ -6,7 +6,7 @@ use crate::prelude::*; /// The stack places a list of items along an axis, with optional spacing /// between each item. /// -/// ## Example { #example } +/// # Example /// ```example /// #stack( /// dir: ttb, @@ -15,10 +15,7 @@ use crate::prelude::*; /// rect(width: 90pt), /// ) /// ``` -/// -/// Display: Stack -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct StackElem { /// The direction along which the items are stacked. Possible values are: /// @@ -27,7 +24,7 @@ pub struct StackElem { /// - `{ttb}`: Top to bottom. /// - `{btt}`: Bottom to top. /// - /// You cab use the `start` and `end` methods to obtain the initial and + /// You can use the `start` and `end` methods to obtain the initial and /// final points (respectively) of a direction, as `alignment`. You can also /// use the `axis` method to determine whether a direction is /// `{"horizontal"}` or `{"vertical"}`. The `inv` method returns a @@ -141,7 +138,7 @@ enum StackItem { /// Fractional spacing between other items. Fractional(Fr), /// A frame for a layouted block. - Frame(Frame, Axes<Align>), + Frame(Frame, Axes<FixedAlign>), } impl<'a> StackLayouter<'a> { @@ -204,7 +201,7 @@ impl<'a> StackLayouter<'a> { } // Block-axis alignment of the `AlignElement` is respected by stacks. - let aligns = if let Some(align) = block.to::<AlignElem>() { + let align = if let Some(align) = block.to::<AlignElem>() { align.alignment(styles) } else if let Some((_, local)) = block.to_styled() { AlignElem::alignment_in(styles.chain(local)) @@ -230,7 +227,7 @@ impl<'a> StackLayouter<'a> { self.used.main += gen.main; self.used.cross.set_max(gen.cross); - self.items.push(StackItem::Frame(frame, aligns)); + self.items.push(StackItem::Frame(frame, align)); if i + 1 < len { self.finish_region(); @@ -259,18 +256,18 @@ impl<'a> StackLayouter<'a> { let mut output = Frame::new(size); let mut cursor = Abs::zero(); - let mut ruler: Align = self.dir.start().into(); + let mut ruler: FixedAlign = self.dir.start().into(); // Place all frames. for item in self.items.drain(..) { match item { StackItem::Absolute(v) => cursor += v, StackItem::Fractional(v) => cursor += v.share(self.fr, remaining), - StackItem::Frame(frame, aligns) => { + StackItem::Frame(frame, align) => { if self.dir.is_positive() { - ruler = ruler.max(aligns.get(self.axis)); + ruler = ruler.max(align.get(self.axis)); } else { - ruler = ruler.min(aligns.get(self.axis)); + ruler = ruler.min(align.get(self.axis)); } // Align along the main axis. @@ -285,7 +282,7 @@ impl<'a> StackLayouter<'a> { // Align along the cross axis. let other = self.axis.other(); - let cross = aligns + let cross = align .get(other) .position(size.get(other) - frame.size().get(other)); diff --git a/crates/typst-library/src/layout/table.rs b/crates/typst-library/src/layout/table.rs index a7bc8a0e..4bbe79a6 100644 --- a/crates/typst-library/src/layout/table.rs +++ b/crates/typst-library/src/layout/table.rs @@ -9,13 +9,13 @@ use crate::prelude::*; /// Tables are used to arrange content in cells. Cells can contain arbitrary /// content, including multiple paragraphs and are specified in row-major order. /// Because tables are just grids with configurable cell properties, refer to -/// the [grid documentation]($func/grid) for more information on how to size the +/// the [grid documentation]($grid) for more information on how to size the /// table tracks. /// -/// To give a table a caption and make it [referenceable]($func/ref), put it -/// into a [figure]($func/figure). +/// To give a table a caption and make it [referenceable]($ref), put it into a +/// [figure]($figure). /// -/// ## Example { #example } +/// # Example /// ```example /// #table( /// columns: (1fr, auto, auto), @@ -34,34 +34,31 @@ use crate::prelude::*; /// [$a$: edge length] /// ) /// ``` -/// -/// Display: Table -/// Category: layout -#[element(Layout, LocalName, Figurable)] +#[elem(Layout, LocalName, Figurable)] pub struct TableElem { - /// The column sizes. See the [grid documentation]($func/grid) for more + /// The column sizes. See the [grid documentation]($grid) for more /// information on track sizing. pub columns: TrackSizings, - /// The row sizes. See the [grid documentation]($func/grid) for more - /// information on track sizing. + /// The row sizes. See the [grid documentation]($grid) for more information + /// on track sizing. pub rows: TrackSizings, - /// The gaps between rows & columns. See the [grid - /// documentation]($func/grid) for more information on gutters. + /// The gaps between rows & columns. See the [grid documentation]($grid) for + /// more information on gutters. #[external] pub gutter: TrackSizings, - /// The gaps between columns. Takes precedence over `gutter`. See the [grid - /// documentation]($func/grid) for more information on gutters. + /// The gaps between columns. Takes precedence over `gutter`. See the + /// [grid documentation]($grid) for more information on gutters. #[parse( let gutter = args.named("gutter")?; args.named("column-gutter")?.or_else(|| gutter.clone()) )] pub column_gutter: TrackSizings, - /// The gaps between rows. Takes precedence over `gutter`. See the [grid - /// documentation]($func/grid) for more information on gutters. + /// The gaps between rows. Takes precedence over `gutter`. See the + /// [grid documentation]($grid) for more information on gutters. #[parse(args.named("row-gutter")?.or_else(|| gutter.clone()))] pub row_gutter: TrackSizings, @@ -102,20 +99,19 @@ pub struct TableElem { /// [A], [B], [C], /// ) /// ``` - pub align: Celled<Smart<Axes<Option<GenAlign>>>>, + pub align: Celled<Smart<Align>>, - /// How to stroke the cells. + /// How to [stroke]($stroke) the cells. /// - /// See the [line's documentation]($func/line.stroke) for more details. /// Strokes can be disabled by setting this to `{none}`. /// /// _Note:_ Richer stroke customization for individual cells is not yet - /// implemented, but will be in the future. In the meantime, you can use - /// the third-party [tablex library](https://github.com/PgBiel/typst-tablex/). + /// implemented, but will be in the future. In the meantime, you can use the + /// third-party [tablex library](https://github.com/PgBiel/typst-tablex/). #[resolve] #[fold] - #[default(Some(PartialStroke::default()))] - pub stroke: Option<PartialStroke>, + #[default(Some(Stroke::default()))] + pub stroke: Option<Stroke>, /// How much to pad the cells' content. #[default(Abs::pt(5.0).into())] @@ -158,7 +154,7 @@ impl Layout for TableElem { .collect::<SourceResult<_>>()?; let fill = self.fill(styles); - let stroke = self.stroke(styles).map(PartialStroke::unwrap_or_default); + let stroke = self.stroke(styles).map(Stroke::unwrap_or_default); // Prepare grid layout by unifying content and gutter tracks. let layouter = GridLayouter::new( @@ -268,8 +264,12 @@ impl<T: Default> Default for Celled<T> { } impl<T: Reflect> Reflect for Celled<T> { - fn describe() -> CastInfo { - T::describe() + Array::describe() + Func::describe() + fn input() -> CastInfo { + T::input() + Array::input() + Func::input() + } + + fn output() -> CastInfo { + T::output() + Array::output() + Func::output() } fn castable(value: &Value) -> bool { diff --git a/crates/typst-library/src/layout/terms.rs b/crates/typst-library/src/layout/terms.rs index d693f100..07f17bb0 100644 --- a/crates/typst-library/src/layout/terms.rs +++ b/crates/typst-library/src/layout/terms.rs @@ -8,29 +8,22 @@ use crate::prelude::*; /// descriptions span over multiple lines, they use hanging indent to /// communicate the visual hierarchy. /// -/// ## Example { #example } +/// # Example /// ```example /// / Ligature: A merged glyph. /// / Kerning: A spacing adjustment /// between two adjacent letters. /// ``` /// -/// ## Syntax { #syntax } +/// # Syntax /// This function also has dedicated syntax: Starting a line with a slash, /// followed by a term, a colon and a description creates a term list item. -/// -/// Display: Term List -/// Category: layout -#[element(Layout)] -#[scope( - scope.define("item", TermItem::func()); - scope -)] +#[elem(scope, title = "Term List", Layout)] pub struct TermsElem { - /// If this is `{false}`, the items are spaced apart with [term list - /// spacing]($func/terms.spacing). If it is `{true}`, they use normal - /// [leading]($func/par.leading) instead. This makes the term list more - /// compact, which can look better if the items are short. + /// If this is `{false}`, the items are spaced apart with + /// [term list spacing]($terms.spacing). If it is `{true}`, they use normal + /// [leading]($par.leading) instead. This makes the term list more compact, + /// which can look better if the items are short. /// /// In markup mode, the value of this parameter is determined based on /// whether items are separated with a blank line. If items directly follow @@ -81,7 +74,7 @@ pub struct TermsElem { /// The spacing between the items of a wide (non-tight) term list. /// - /// If set to `{auto}`, uses the spacing [below blocks]($func/block.below). + /// If set to `{auto}`, uses the spacing [below blocks]($block.below). pub spacing: Smart<Spacing>, /// The term list's children. @@ -100,6 +93,12 @@ pub struct TermsElem { pub children: Vec<TermItem>, } +#[scope] +impl TermsElem { + #[elem] + type TermItem; +} + impl Layout for TermsElem { #[tracing::instrument(name = "TermsElem::layout", skip_all)] fn layout( @@ -138,10 +137,7 @@ impl Layout for TermsElem { } /// A term list item. -/// -/// Display: Term List Item -/// Category: layout -#[element] +#[elem(name = "item", title = "Term List Item")] pub struct TermItem { /// The term described by the list item. #[required] diff --git a/crates/typst-library/src/layout/transform.rs b/crates/typst-library/src/layout/transform.rs index a57a5edc..012a146d 100644 --- a/crates/typst-library/src/layout/transform.rs +++ b/crates/typst-library/src/layout/transform.rs @@ -8,7 +8,7 @@ use crate::prelude::*; /// it at the original positions. Containers will still be sized as if the /// content was not moved. /// -/// ## Example { #example } +/// # Example /// ```example /// #rect(inset: 0pt, move( /// dx: 6pt, dy: 6pt, @@ -20,10 +20,7 @@ use crate::prelude::*; /// ) /// )) /// ``` -/// -/// Display: Move -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct MoveElem { /// The horizontal displacement of the content. pub dx: Rel<Length>, @@ -47,7 +44,7 @@ impl Layout for MoveElem { let pod = Regions::one(regions.base(), Axes::splat(false)); let mut frame = self.body().layout(vt, styles, pod)?.into_frame(); let delta = Axes::new(self.dx(styles), self.dy(styles)).resolve(styles); - let delta = delta.zip(regions.base()).map(|(d, s)| d.relative_to(s)); + let delta = delta.zip_map(regions.base(), Rel::relative_to); frame.translate(delta.to_point()); Ok(Fragment::frame(frame)) } @@ -58,7 +55,7 @@ impl Layout for MoveElem { /// Rotates an element by a given angle. The layout will act as if the element /// was not rotated. /// -/// ## Example { #example } +/// # Example /// ```example /// #stack( /// dir: ltr, @@ -67,10 +64,7 @@ impl Layout for MoveElem { /// .map(i => rotate(24deg * i)[X]), /// ) /// ``` -/// -/// Display: Rotate -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct RotateElem { /// The amount of rotation. /// @@ -96,10 +90,9 @@ pub struct RotateElem { /// #box(rotate(30deg, origin: top + left, square())) /// #box(rotate(30deg, origin: bottom + right, square())) /// ``` - #[resolve] #[fold] - #[default(Align::CENTER_HORIZON)] - pub origin: Axes<Option<GenAlign>>, + #[default(HAlign::Center + VAlign::Horizon)] + pub origin: Align, /// The content to rotate. #[required] @@ -116,8 +109,10 @@ impl Layout for RotateElem { ) -> SourceResult<Fragment> { let pod = Regions::one(regions.base(), Axes::splat(false)); let mut frame = self.body().layout(vt, styles, pod)?.into_frame(); - let Axes { x, y } = - self.origin(styles).zip(frame.size()).map(|(o, s)| o.position(s)); + let Axes { x, y } = self + .origin(styles) + .resolve(styles) + .zip_map(frame.size(), FixedAlign::position); let ts = Transform::translate(x, y) .pre_concat(Transform::rotate(self.angle(styles))) .pre_concat(Transform::translate(-x, -y)); @@ -130,15 +125,12 @@ impl Layout for RotateElem { /// /// Lets you mirror content by specifying a negative scale on a single axis. /// -/// ## Example { #example } +/// # Example /// ```example /// #set align(center) /// #scale(x: -100%)[This is mirrored.] /// ``` -/// -/// Display: Scale -/// Category: layout -#[element(Layout)] +#[elem(Layout)] pub struct ScaleElem { /// The horizontal scaling factor. /// @@ -163,10 +155,9 @@ pub struct ScaleElem { /// A#box(scale(75%)[A])A \ /// B#box(scale(75%, origin: bottom + left)[B])B /// ``` - #[resolve] #[fold] - #[default(Align::CENTER_HORIZON)] - pub origin: Axes<Option<GenAlign>>, + #[default(HAlign::Center + VAlign::Horizon)] + pub origin: Align, /// The content to scale. #[required] @@ -183,8 +174,10 @@ impl Layout for ScaleElem { ) -> SourceResult<Fragment> { let pod = Regions::one(regions.base(), Axes::splat(false)); let mut frame = self.body().layout(vt, styles, pod)?.into_frame(); - let Axes { x, y } = - self.origin(styles).zip(frame.size()).map(|(o, s)| o.position(s)); + let Axes { x, y } = self + .origin(styles) + .resolve(styles) + .zip_map(frame.size(), FixedAlign::position); let transform = Transform::translate(x, y) .pre_concat(Transform::scale(self.x(styles), self.y(styles))) .pre_concat(Transform::translate(-x, -y)); |
