summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/layout
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-09-11 14:40:22 +0200
committerLaurenz <laurmaedje@gmail.com>2023-09-11 14:40:22 +0200
commitb471ac7d590abd2398ce25193b4e4df373bf2e9c (patch)
treeb5f7a6fdc807ee3340a4f42b0ad3cc563fe45429 /crates/typst-library/src/layout
parent8f36fca68447a5d42a3d54b5fac7e5546ee244be (diff)
First-class types
Makes types first-class values.
Diffstat (limited to 'crates/typst-library/src/layout')
-rw-r--r--crates/typst-library/src/layout/align.rs48
-rw-r--r--crates/typst-library/src/layout/columns.rs18
-rw-r--r--crates/typst-library/src/layout/container.rs68
-rw-r--r--crates/typst-library/src/layout/enum.rs49
-rw-r--r--crates/typst-library/src/layout/flow.rs55
-rw-r--r--crates/typst-library/src/layout/grid.rs7
-rw-r--r--crates/typst-library/src/layout/hide.rs7
-rw-r--r--crates/typst-library/src/layout/list.rs36
-rw-r--r--crates/typst-library/src/layout/measure.rs25
-rw-r--r--crates/typst-library/src/layout/mod.rs79
-rw-r--r--crates/typst-library/src/layout/pad.rs10
-rw-r--r--crates/typst-library/src/layout/page.rs68
-rw-r--r--crates/typst-library/src/layout/par.rs54
-rw-r--r--crates/typst-library/src/layout/place.rs26
-rw-r--r--crates/typst-library/src/layout/repeat.rs11
-rw-r--r--crates/typst-library/src/layout/spacing.rs16
-rw-r--r--crates/typst-library/src/layout/stack.rs25
-rw-r--r--crates/typst-library/src/layout/table.rs54
-rw-r--r--crates/typst-library/src/layout/terms.rs34
-rw-r--r--crates/typst-library/src/layout/transform.rs45
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));