diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-09 14:17:24 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-09 14:42:14 +0100 |
| commit | c38d72383d2068361d635d6c1c78ba97aa917801 (patch) | |
| tree | e758418a2d704d69dee88faf4a9a9c69b25b47ca /library/src/layout | |
| parent | d7a65fa26d131179d9d82226e5ee1b562084e48a (diff) | |
Make all optional fields settable
Diffstat (limited to 'library/src/layout')
| -rw-r--r-- | library/src/layout/align.rs | 11 | ||||
| -rw-r--r-- | library/src/layout/columns.rs | 20 | ||||
| -rw-r--r-- | library/src/layout/container.rs | 124 | ||||
| -rw-r--r-- | library/src/layout/enum.rs | 104 | ||||
| -rw-r--r-- | library/src/layout/flow.rs | 19 | ||||
| -rw-r--r-- | library/src/layout/grid.rs | 33 | ||||
| -rw-r--r-- | library/src/layout/list.rs | 49 | ||||
| -rw-r--r-- | library/src/layout/mod.rs | 30 | ||||
| -rw-r--r-- | library/src/layout/pad.rs | 67 | ||||
| -rw-r--r-- | library/src/layout/page.rs | 74 | ||||
| -rw-r--r-- | library/src/layout/par.rs | 44 | ||||
| -rw-r--r-- | library/src/layout/place.rs | 24 | ||||
| -rw-r--r-- | library/src/layout/spacing.rs | 28 | ||||
| -rw-r--r-- | library/src/layout/stack.rs | 28 | ||||
| -rw-r--r-- | library/src/layout/table.rs | 43 | ||||
| -rw-r--r-- | library/src/layout/terms.rs | 44 | ||||
| -rw-r--r-- | library/src/layout/transform.rs | 65 |
17 files changed, 317 insertions, 490 deletions
diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs index fdd4d0ea..88815dc9 100644 --- a/library/src/layout/align.rs +++ b/library/src/layout/align.rs @@ -15,9 +15,6 @@ use crate::prelude::*; /// Display: Align /// Category: layout #[node(Show)] -#[set({ - styles.set(Self::set_alignment(args.find()?.unwrap_or_default())); -})] pub struct AlignNode { /// The alignment along both axes. /// @@ -50,10 +47,8 @@ pub struct AlignNode { /// rect(inset: 12pt)[ركن] /// ) /// ``` - #[settable] #[positional] #[fold] - #[skip] #[default(Axes::new(GenAlign::Start, GenAlign::Specific(Align::Top)))] pub alignment: Axes<Option<GenAlign>>, @@ -64,7 +59,9 @@ pub struct AlignNode { } impl Show for AlignNode { - fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult<Content> { - Ok(self.body()) + fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> { + Ok(self + .body() + .styled(Self::set_alignment(self.alignment(styles).map(Some)))) } } diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs index 27339628..58b369c6 100644 --- a/library/src/layout/columns.rs +++ b/library/src/layout/columns.rs @@ -36,19 +36,18 @@ use crate::text::TextNode; pub struct ColumnsNode { /// The number of columns. #[positional] - #[required] + #[default(NonZeroUsize::new(2).unwrap())] pub count: NonZeroUsize, - /// The content that should be layouted into the columns. - #[positional] - #[required] - pub body: Content, - /// The size of the gutter space between each column. - #[settable] #[resolve] #[default(Ratio::new(0.04).into())] pub gutter: Rel<Length>, + + /// The content that should be layouted into the columns. + #[positional] + #[required] + pub body: Content, } impl Layout for ColumnsNode { @@ -67,8 +66,8 @@ impl Layout for ColumnsNode { } // Determine the width of the gutter and each column. - let columns = self.count().get(); - let gutter = Self::gutter_in(styles).relative_to(regions.base().x); + let columns = self.count(styles).get(); + let gutter = self.gutter(styles).relative_to(regions.base().x); let width = (regions.size.x - gutter * (columns - 1) as f64) / columns as f64; let backlog: Vec<_> = std::iter::once(®ions.size.y) @@ -157,14 +156,13 @@ impl Layout for ColumnsNode { pub struct ColbreakNode { /// If `{true}`, the column break is skipped if the current column is /// already empty. - #[named] #[default(false)] pub weak: bool, } impl Behave for ColbreakNode { fn behaviour(&self) -> Behaviour { - if self.weak() { + if self.weak(StyleChain::default()) { Behaviour::Weak(1) } else { Behaviour::Destructive diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs index bdce1922..31a80aa2 100644 --- a/library/src/layout/container.rs +++ b/library/src/layout/container.rs @@ -23,11 +23,6 @@ use crate::prelude::*; /// Category: layout #[node(Layout)] pub struct BoxNode { - /// The contents of the box. - #[positional] - #[default] - pub body: Option<Content>, - /// The width of the box. /// /// Boxes can have [fractional]($type/fraction) widths, as the example @@ -40,13 +35,9 @@ pub struct BoxNode { /// ```example /// Line in #box(width: 1fr, line(length: 100%)) between. /// ``` - #[named] - #[default] pub width: Sizing, /// The height of the box. - #[named] - #[default] pub height: Smart<Rel<Length>>, /// An amount to shift the box's baseline by. @@ -54,39 +45,29 @@ pub struct BoxNode { /// ```example /// Image: #box(baseline: 40%, image("tiger.jpg", width: 2cm)). /// ``` - #[settable] #[resolve] - #[default] pub baseline: Rel<Length>, /// The box's background color. See the /// [rectangle's documentation]($func/rect.fill) for more details. - #[settable] - #[default] pub fill: Option<Paint>, /// The box's border color. See the /// [rectangle's documentation]($func/rect.stroke) for more details. - #[settable] #[resolve] #[fold] - #[default] pub stroke: Sides<Option<Option<PartialStroke>>>, /// How much to round the box's corners. See the [rectangle's /// documentation]($func/rect.radius) for more details. - #[settable] #[resolve] #[fold] - #[default] 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. - #[settable] #[resolve] #[fold] - #[default] pub inset: Sides<Option<Rel<Length>>>, /// How much to expand the box's size without affecting the layout. @@ -103,11 +84,13 @@ pub struct BoxNode { /// outset: (y: 3pt), /// radius: 2pt, /// )[rectangle]. - #[settable] #[resolve] #[fold] - #[default] pub outset: Sides<Option<Rel<Length>>>, + + /// The contents of the box. + #[positional] + pub body: Option<Content>, } impl Layout for BoxNode { @@ -117,14 +100,14 @@ impl Layout for BoxNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let width = match self.width() { + let width = match self.width(styles) { Sizing::Auto => Smart::Auto, Sizing::Rel(rel) => Smart::Custom(rel), Sizing::Fr(_) => Smart::Custom(Ratio::one().into()), }; // Resolve the sizing to a concrete size. - let sizing = Axes::new(width, self.height()); + let sizing = Axes::new(width, self.height(styles)); let expand = sizing.as_ref().map(Smart::is_custom); let size = sizing .resolve(styles) @@ -133,8 +116,8 @@ impl Layout for BoxNode { .unwrap_or(regions.base()); // Apply inset. - let mut body = self.body().unwrap_or_default(); - let inset = Self::inset_in(styles); + let mut body = self.body(styles).unwrap_or_default(); + let inset = self.inset(styles); if inset.iter().any(|v| !v.is_zero()) { body = body.padded(inset.map(|side| side.map(Length::from))); } @@ -145,20 +128,19 @@ impl Layout for BoxNode { let mut frame = body.layout(vt, styles, pod)?.into_frame(); // Apply baseline shift. - let shift = Self::baseline_in(styles).relative_to(frame.height()); + let shift = self.baseline(styles).relative_to(frame.height()); if !shift.is_zero() { frame.set_baseline(frame.baseline() - shift); } // Prepare fill and stroke. - let fill = Self::fill_in(styles); - let stroke = - Self::stroke_in(styles).map(|s| s.map(PartialStroke::unwrap_or_default)); + let fill = self.fill(styles); + let stroke = self.stroke(styles).map(|s| s.map(PartialStroke::unwrap_or_default)); // Add fill and/or stroke. if fill.is_some() || stroke.iter().any(Option::is_some) { - let outset = Self::outset_in(styles); - let radius = Self::radius_in(styles); + let outset = self.outset(styles); + let radius = self.radius(styles); frame.fill_and_stroke(fill, stroke, outset, radius); } @@ -216,27 +198,7 @@ impl Layout for BoxNode { /// Display: Block /// Category: layout #[node(Layout)] -#[set({ - let spacing = args.named("spacing")?; - styles.set_opt( - args.named("above")? - .map(VNode::block_around) - .or_else(|| spacing.map(VNode::block_spacing)) - .map(Self::set_above), - ); - styles.set_opt( - args.named("below")? - .map(VNode::block_around) - .or_else(|| spacing.map(VNode::block_spacing)) - .map(Self::set_below), - ); -})] pub struct BlockNode { - /// The contents of the block. - #[positional] - #[default] - pub body: Option<Content>, - /// The block's width. /// /// ```example @@ -248,8 +210,6 @@ pub struct BlockNode { /// lorem(10), /// ) /// ``` - #[named] - #[default] pub width: Smart<Rel<Length>>, /// The block's height. When the height is larger than the remaining space on @@ -265,8 +225,6 @@ pub struct BlockNode { /// fill: aqua, /// ) /// ``` - #[named] - #[default] pub height: Smart<Rel<Length>>, /// Whether the block can be broken and continue on the next page. @@ -281,55 +239,48 @@ pub struct BlockNode { /// lorem(15), /// ) /// ``` - #[settable] #[default(true)] pub breakable: bool, /// The block's background color. See the /// [rectangle's documentation]($func/rect.fill) for more details. - #[settable] - #[default] pub fill: Option<Paint>, /// The block's border color. See the /// [rectangle's documentation]($func/rect.stroke) for more details. - #[settable] #[resolve] #[fold] - #[default] pub stroke: Sides<Option<Option<PartialStroke>>>, /// How much to round the block's corners. See the [rectangle's /// documentation]($func/rect.radius) for more details. - #[settable] #[resolve] #[fold] - #[default] 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. - #[settable] #[resolve] #[fold] - #[default] 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. - #[settable] #[resolve] #[fold] - #[default] pub outset: Sides<Option<Rel<Length>>>, - /// The spacing between this block and its predecessor. Takes precedence over - /// `spacing`. Can be used in combination with a show rule to adjust the - /// spacing around arbitrary block-level elements. + /// The spacing between this block and its predecessor. Takes precedence + /// over `spacing`. Can be used in combination with a show rule to adjust + /// the spacing around arbitrary block-level elements. /// /// The default value is `{1.2em}`. - #[settable] - #[skip] + #[parse( + let spacing = args.named("spacing")?; + args.named("above")? + .map(VNode::block_around) + .or_else(|| spacing.map(VNode::block_spacing)) + )] #[default(VNode::block_spacing(Em::new(1.2).into()))] pub above: VNode, @@ -337,16 +288,22 @@ pub struct BlockNode { /// over `spacing`. /// /// The default value is `{1.2em}`. - #[settable] - #[skip] + #[parse( + args.named("below")? + .map(VNode::block_around) + .or_else(|| spacing.map(VNode::block_spacing)) + )] #[default(VNode::block_spacing(Em::new(1.2).into()))] pub below: VNode, + /// The contents of the block. + #[positional] + pub body: Option<Content>, + /// Whether this block must stick to the following one. /// /// Use this to prevent page breaks between e.g. a heading and its body. - #[settable] - #[skip] + #[internal] #[default(false)] pub sticky: bool, } @@ -359,14 +316,14 @@ impl Layout for BlockNode { regions: Regions, ) -> SourceResult<Fragment> { // Apply inset. - let mut body = self.body().unwrap_or_default(); - let inset = Self::inset_in(styles); + let mut body = self.body(styles).unwrap_or_default(); + let inset = self.inset(styles); if inset.iter().any(|v| !v.is_zero()) { body = body.clone().padded(inset.map(|side| side.map(Length::from))); } // Resolve the sizing to a concrete size. - let sizing = Axes::new(self.width(), self.height()); + let sizing = Axes::new(self.width(styles), self.height(styles)); let mut expand = sizing.as_ref().map(Smart::is_custom); let mut size = sizing .resolve(styles) @@ -375,7 +332,7 @@ impl Layout for BlockNode { .unwrap_or(regions.base()); // Layout the child. - let mut frames = if Self::breakable_in(styles) { + let mut frames = if self.breakable(styles) { // Measure to ensure frames for all regions have the same width. if sizing.x == Smart::Auto { let pod = Regions::one(size, Axes::splat(false)); @@ -413,9 +370,8 @@ impl Layout for BlockNode { }; // Prepare fill and stroke. - let fill = Self::fill_in(styles); - let stroke = - Self::stroke_in(styles).map(|s| s.map(PartialStroke::unwrap_or_default)); + let fill = self.fill(styles); + let stroke = self.stroke(styles).map(|s| s.map(PartialStroke::unwrap_or_default)); // Add fill and/or stroke. if fill.is_some() || stroke.iter().any(Option::is_some) { @@ -424,8 +380,8 @@ impl Layout for BlockNode { skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty()); } - let outset = Self::outset_in(styles); - let radius = Self::radius_in(styles); + let outset = self.outset(styles); + let radius = self.radius(styles); for frame in frames.iter_mut().skip(skip as usize) { frame.fill_and_stroke(fill, stroke, outset, radius); } diff --git a/library/src/layout/enum.rs b/library/src/layout/enum.rs index 4876b616..ee09d339 100644 --- a/library/src/layout/enum.rs +++ b/library/src/layout/enum.rs @@ -48,37 +48,10 @@ use super::GridLayouter; /// content. All content that is indented more than an item's plus sign or dot /// becomes part of that item. /// -/// ## Parameters -/// - start: `NonZeroUsize` (named) -/// Which number to start the enumeration with. -/// -/// ```example -/// #enum( -/// start: 3, -/// [Skipping], -/// [Ahead], -/// ) -/// ``` -/// /// Display: Numbered List /// Category: layout -#[node(Construct, Layout)] +#[node(Layout)] pub struct EnumNode { - /// The numbered list's items. - /// - /// When using the enum syntax, adjacent items are automatically collected - /// into enumerations, even through constructs like for loops. - /// - /// ```example - /// #for phase in ( - /// "Launch", - /// "Orbit", - /// "Descent", - /// ) [+ #phase] - /// ``` - #[variadic] - pub children: Vec<EnumItem>, - /// 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 @@ -93,7 +66,6 @@ pub struct EnumNode { /// insert a blank line between the /// items. /// ``` - #[named] #[default(true)] pub tight: bool, @@ -116,10 +88,21 @@ pub struct EnumNode { /// + Superscript /// + Numbering! /// ``` - #[settable] #[default(Numbering::Pattern(NumberingPattern::from_str("1.").unwrap()))] pub numbering: Numbering, + /// Which number to start the enumeration with. + /// + /// ```example + /// #enum( + /// start: 3, + /// [Skipping], + /// [Ahead], + /// ) + /// ``` + #[default(NonZeroUsize::new(1).unwrap())] + pub start: NonZeroUsize, + /// Whether to display the full numbering, including the numbers of /// all parent enumerations. /// @@ -132,18 +115,14 @@ pub struct EnumNode { /// + Add integredients /// + Eat /// ``` - #[settable] #[default(false)] pub full: bool, /// The indentation of each item's label. - #[settable] #[resolve] - #[default] pub indent: Length, /// The space between the numbering and the body of each item. - #[settable] #[resolve] #[default(Em::new(0.5).into())] pub body_indent: Length, @@ -151,35 +130,29 @@ pub struct EnumNode { /// The spacing between the items of a wide (non-tight) enumeration. /// /// If set to `{auto}`, uses the spacing [below blocks]($func/block.below). - #[settable] - #[default] pub spacing: Smart<Spacing>, + /// The numbered list's items. + /// + /// When using the enum syntax, adjacent items are automatically collected + /// into enumerations, even through constructs like for loops. + /// + /// ```example + /// #for phase in ( + /// "Launch", + /// "Orbit", + /// "Descent", + /// ) [+ #phase] + /// ``` + #[variadic] + pub children: Vec<EnumItem>, + /// The numbers of parent items. - #[settable] + #[internal] #[fold] - #[skip] - #[default] parents: Parent, } -impl Construct for EnumNode { - fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { - let mut items = args.all::<EnumItem>()?; - if let Some(number) = args.named::<NonZeroUsize>("start")? { - if let Some(first) = items.first_mut() { - if first.number().is_none() { - *first = EnumItem::new(first.body()).with_number(Some(number)); - } - } - } - - Ok(Self::new(items) - .with_tight(args.named("tight")?.unwrap_or(true)) - .pack()) - } -} - impl Layout for EnumNode { fn layout( &self, @@ -187,23 +160,23 @@ impl Layout for EnumNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let numbering = Self::numbering_in(styles); - let indent = Self::indent_in(styles); - let body_indent = Self::body_indent_in(styles); - let gutter = if self.tight() { + let numbering = self.numbering(styles); + let indent = self.indent(styles); + let body_indent = self.body_indent(styles); + let gutter = if self.tight(styles) { ParNode::leading_in(styles).into() } else { - Self::spacing_in(styles) + self.spacing(styles) .unwrap_or_else(|| BlockNode::below_in(styles).amount()) }; let mut cells = vec![]; - let mut number = NonZeroUsize::new(1).unwrap(); - let mut parents = Self::parents_in(styles); - let full = Self::full_in(styles); + let mut number = self.start(styles); + let mut parents = self.parents(styles); + let full = self.full(styles); for item in self.children() { - number = item.number().unwrap_or(number); + number = item.number(styles).unwrap_or(number); let resolved = if full { parents.push(number); @@ -252,7 +225,6 @@ impl Layout for EnumNode { pub struct EnumItem { /// The item's number. #[positional] - #[default] pub number: Option<NonZeroUsize>, /// The item's body. diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs index 5d679570..7a063bce 100644 --- a/library/src/layout/flow.rs +++ b/library/src/layout/flow.rs @@ -1,4 +1,4 @@ -use typst::model::{Style, StyledNode}; +use typst::model::StyledNode; use super::{AlignNode, BlockNode, ColbreakNode, ParNode, PlaceNode, Spacing, VNode}; use crate::prelude::*; @@ -40,8 +40,6 @@ impl Layout for FlowNode { if let Some(node) = child.to::<VNode>() { layouter.layout_spacing(node, styles); } else if let Some(node) = child.to::<ParNode>() { - let barrier = Style::Barrier(child.id()); - let styles = styles.chain_one(&barrier); layouter.layout_par(vt, node, styles)?; } else if child.is::<RectNode>() || child.is::<SquareNode>() @@ -49,8 +47,6 @@ impl Layout for FlowNode { || child.is::<CircleNode>() || child.is::<ImageNode>() { - let barrier = Style::Barrier(child.id()); - let styles = styles.chain_one(&barrier); layouter.layout_single(vt, &child, styles)?; } else if child.has::<dyn Layout>() { layouter.layout_multiple(vt, &child, styles)?; @@ -121,7 +117,7 @@ impl<'a> FlowLayouter<'a> { self.layout_item(match node.amount() { Spacing::Rel(v) => FlowItem::Absolute( v.resolve(styles).relative_to(self.initial.y), - node.weakness() > 0, + node.weakness(styles) > 0, ), Spacing::Fr(v) => FlowItem::Fractional(v), }); @@ -200,7 +196,7 @@ impl<'a> FlowLayouter<'a> { // Placed nodes that are out of flow produce placed items which aren't // aligned later. if let Some(placed) = block.to::<PlaceNode>() { - if placed.out_of_flow() { + if placed.out_of_flow(styles) { let frame = block.layout(vt, styles, self.regions)?.into_frame(); self.layout_item(FlowItem::Placed(frame)); return Ok(()); @@ -208,7 +204,14 @@ impl<'a> FlowLayouter<'a> { } // How to align the block. - let aligns = AlignNode::alignment_in(styles).resolve(styles); + let aligns = if let Some(align) = block.to::<AlignNode>() { + align.alignment(styles) + } else if let Some(styled) = block.to::<StyledNode>() { + AlignNode::alignment_in(styles.chain(&styled.map())) + } else { + AlignNode::alignment_in(styles) + } + .resolve(styles); // Layout the block itself. let sticky = BlockNode::sticky_in(styles); diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs index 5d465f86..34514eac 100644 --- a/library/src/layout/grid.rs +++ b/library/src/layout/grid.rs @@ -60,7 +60,7 @@ use super::Sizing; /// ``` /// /// ## Parameters -/// - gutter: `TrackSizings` (named) +/// - gutter: `TrackSizings` (named, settable) /// Defines the gaps between rows & columns. /// /// If there are more gutters than defined sizes, the last gutter is repeated. @@ -69,41 +69,36 @@ use super::Sizing; /// Category: layout #[node(Layout)] pub struct GridNode { - /// The contents of the table cells. - /// - /// The cells are populated in row-major order. - #[variadic] - pub children: Vec<Content>, - /// Defines the column sizes. /// /// Either specify a track size array or provide an integer to create a grid /// with that many `{auto}`-sized columns. Note that opposed to rows and /// gutters, providing a single track size will only ever create a single /// column. - #[named] - #[default] pub columns: TrackSizings, /// Defines the row sizes. /// /// If there are more cells than fit the defined rows, the last row is /// repeated until there are no more cells. - #[named] - #[default] pub rows: TrackSizings, /// Defines the gaps between columns. Takes precedence over `gutter`. - #[named] - #[shorthand(gutter)] - #[default] + #[parse( + let gutter = args.named("gutter")?; + args.named("column-gutter")?.or_else(|| gutter.clone()) + )] pub column_gutter: TrackSizings, /// Defines the gaps between rows. Takes precedence over `gutter`. - #[named] - #[shorthand(gutter)] - #[default] + #[parse(args.named("row-gutter")?.or_else(|| gutter.clone()))] pub row_gutter: TrackSizings, + + /// The contents of the table cells. + /// + /// The cells are populated in row-major order. + #[variadic] + pub children: Vec<Content>, } impl Layout for GridNode { @@ -117,8 +112,8 @@ impl Layout for GridNode { let cells = self.children(); let layouter = GridLayouter::new( vt, - Axes::new(&self.columns().0, &self.rows().0), - Axes::new(&self.column_gutter().0, &self.row_gutter().0), + Axes::new(&self.columns(styles).0, &self.rows(styles).0), + Axes::new(&self.column_gutter(styles).0, &self.row_gutter(styles).0), &cells, regions, styles, diff --git a/library/src/layout/list.rs b/library/src/layout/list.rs index e6e42263..57b653c0 100644 --- a/library/src/layout/list.rs +++ b/library/src/layout/list.rs @@ -38,19 +38,6 @@ use super::GridLayouter; /// Category: layout #[node(Layout)] pub struct ListNode { - /// The bullet list's children. - /// - /// When using the list syntax, adjacent items are automatically collected - /// into lists, even through constructs like for loops. - /// - /// ```example - /// #for letter in "ABC" [ - /// - Letter #letter - /// ] - /// ``` - #[variadic] - pub children: Vec<ListItem>, - /// 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, @@ -64,7 +51,6 @@ pub struct ListNode { /// - To make a list wide, simply insert /// a blank line between the items. /// ``` - #[named] #[default(true)] pub tight: bool, @@ -89,18 +75,14 @@ pub struct ListNode { /// - Items /// - Items /// ``` - #[settable] #[default(ListMarker::Content(vec![]))] pub marker: ListMarker, /// The indent of each item's marker. - #[settable] #[resolve] - #[default] pub indent: Length, /// The spacing between the marker and the body of each item. - #[settable] #[resolve] #[default(Em::new(0.5).into())] pub body_indent: Length, @@ -108,15 +90,24 @@ pub struct ListNode { /// The spacing between the items of a wide (non-tight) list. /// /// If set to `{auto}`, uses the spacing [below blocks]($func/block.below). - #[settable] - #[default] pub spacing: Smart<Spacing>, + /// The bullet list's children. + /// + /// When using the list syntax, adjacent items are automatically collected + /// into lists, even through constructs like for loops. + /// + /// ```example + /// #for letter in "ABC" [ + /// - Letter #letter + /// ] + /// ``` + #[variadic] + pub children: Vec<ListItem>, + /// The nesting depth. - #[settable] + #[internal] #[fold] - #[skip] - #[default] depth: Depth, } @@ -127,17 +118,17 @@ impl Layout for ListNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let indent = Self::indent_in(styles); - let body_indent = Self::body_indent_in(styles); - let gutter = if self.tight() { + let indent = self.indent(styles); + let body_indent = self.body_indent(styles); + let gutter = if self.tight(styles) { ParNode::leading_in(styles).into() } else { - Self::spacing_in(styles) + self.spacing(styles) .unwrap_or_else(|| BlockNode::below_in(styles).amount()) }; - let depth = Self::depth_in(styles); - let marker = Self::marker_in(styles).resolve(vt.world(), depth)?; + let depth = self.depth(styles); + let marker = self.marker(styles).resolve(vt.world(), depth)?; let mut cells = vec![]; for item in self.children() { diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index 6d57912f..e846f6f0 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -48,7 +48,7 @@ use std::mem; use typed_arena::Arena; use typst::diag::SourceResult; use typst::model::{ - applicable, realize, Content, Node, SequenceNode, Style, StyleChain, StyleVecBuilder, + applicable, realize, Content, Node, SequenceNode, StyleChain, StyleVecBuilder, StyledNode, }; @@ -124,8 +124,6 @@ impl Layout for Content { let mut vt = Vt { world, provider, introspector }; let scratch = Scratch::default(); let (realized, styles) = realize_block(&mut vt, &scratch, node, styles)?; - let barrier = Style::Barrier(realized.id()); - let styles = styles.chain_one(&barrier); realized .with::<dyn Layout>() .unwrap() @@ -283,7 +281,7 @@ impl<'a, 'v, 't> Builder<'a, 'v, 't> { let keep = content .to::<PagebreakNode>() - .map_or(false, |pagebreak| !pagebreak.weak()); + .map_or(false, |pagebreak| !pagebreak.weak(styles)); self.interrupt_page(keep.then(|| styles))?; @@ -399,7 +397,7 @@ struct DocBuilder<'a> { impl<'a> DocBuilder<'a> { fn accept(&mut self, content: &Content, styles: StyleChain<'a>) -> bool { if let Some(pagebreak) = content.to::<PagebreakNode>() { - self.keep_next = !pagebreak.weak(); + self.keep_next = !pagebreak.weak(styles); return true; } @@ -440,11 +438,11 @@ impl<'a> FlowBuilder<'a> { if content.has::<dyn Layout>() || content.is::<ParNode>() { let is_tight_list = if let Some(node) = content.to::<ListNode>() { - node.tight() + node.tight(styles) } else if let Some(node) = content.to::<EnumNode>() { - node.tight() + node.tight(styles) } else if let Some(node) = content.to::<TermsNode>() { - node.tight() + node.tight(styles) } else { false }; @@ -478,7 +476,7 @@ impl<'a> ParBuilder<'a> { || content.is::<HNode>() || content.is::<LinebreakNode>() || content.is::<SmartQuoteNode>() - || content.to::<FormulaNode>().map_or(false, |node| !node.block()) + || content.to::<FormulaNode>().map_or(false, |node| !node.block(styles)) || content.is::<BoxNode>() { self.0.push(content.clone(), styles); @@ -539,7 +537,7 @@ impl<'a> ListBuilder<'a> { .iter() .map(|(item, map)| { let item = item.to::<ListItem>().unwrap(); - ListItem::new(item.body().styled_with_map(map.clone())) + item.clone().with_body(item.body().styled_with_map(map.clone())) }) .collect::<Vec<_>>(), ) @@ -551,8 +549,7 @@ impl<'a> ListBuilder<'a> { .iter() .map(|(item, map)| { let item = item.to::<EnumItem>().unwrap(); - EnumItem::new(item.body().styled_with_map(map.clone())) - .with_number(item.number()) + item.clone().with_body(item.body().styled_with_map(map.clone())) }) .collect::<Vec<_>>(), ) @@ -564,10 +561,11 @@ impl<'a> ListBuilder<'a> { .iter() .map(|(item, map)| { let item = item.to::<TermItem>().unwrap(); - TermItem::new( - item.term().styled_with_map(map.clone()), - item.description().styled_with_map(map.clone()), - ) + item.clone() + .with_term(item.term().styled_with_map(map.clone())) + .with_description( + item.description().styled_with_map(map.clone()), + ) }) .collect::<Vec<_>>(), ) diff --git a/library/src/layout/pad.rs b/library/src/layout/pad.rs index 05aafc76..7d0bbe04 100644 --- a/library/src/layout/pad.rs +++ b/library/src/layout/pad.rs @@ -16,62 +16,44 @@ use crate::prelude::*; /// ``` /// /// ## Parameters -/// - x: `Rel<Length>` (named) +/// - x: `Rel<Length>` (named, settable) /// The horizontal padding. Both `left` and `right` take precedence over this. /// -/// - y: `Rel<Length>` (named) +/// - y: `Rel<Length>` (named, settable) /// The vertical padding. Both `top` and `bottom` take precedence over this. /// -/// - rest: `Rel<Length>` (named) +/// - rest: `Rel<Length>` (named, settable) /// The padding for all sides. All other parameters take precedence over this. /// /// Display: Padding /// Category: layout -#[node(Construct, Layout)] +#[node(Layout)] pub struct PadNode { - /// The content to pad at the sides. - #[positional] - #[required] - pub body: Content, - /// The padding at the left side. - #[named] - #[default] + #[parse( + let all = args.named("rest")?.or(args.find()?); + let x = args.named("x")?.or(all); + let y = args.named("y")?.or(all); + args.named("left")?.or(x) + )] pub left: Rel<Length>, - /// The padding at the right side. - #[named] - #[default] - pub right: Rel<Length>, - /// The padding at the top side. - #[named] - #[default] + #[parse(args.named("top")?.or(y))] pub top: Rel<Length>, + /// The padding at the right side. + #[parse(args.named("right")?.or(x))] + pub right: Rel<Length>, + /// The padding at the bottom side. - #[named] - #[default] + #[parse(args.named("bottom")?.or(y))] pub bottom: Rel<Length>, -} -impl Construct for PadNode { - fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { - let all = args.named("rest")?.or(args.find()?); - let x = args.named("x")?; - let y = args.named("y")?; - let left = args.named("left")?.or(x).or(all).unwrap_or_default(); - let top = args.named("top")?.or(y).or(all).unwrap_or_default(); - let right = args.named("right")?.or(x).or(all).unwrap_or_default(); - let bottom = args.named("bottom")?.or(y).or(all).unwrap_or_default(); - let body = args.expect::<Content>("body")?; - Ok(Self::new(body) - .with_left(left) - .with_top(top) - .with_bottom(bottom) - .with_right(right) - .pack()) - } + /// The content to pad at the sides. + #[positional] + #[required] + pub body: Content, } impl Layout for PadNode { @@ -81,10 +63,15 @@ impl Layout for PadNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let mut backlog = vec![]; + let sides = Sides::new( + self.left(styles), + self.top(styles), + self.right(styles), + self.bottom(styles), + ); // Layout child into padded regions. - let sides = Sides::new(self.left(), self.top(), self.right(), self.bottom()); + let mut backlog = vec![]; let padding = sides.resolve(styles); let pod = regions.map(&mut backlog, |size| shrink(size, padding)); let mut fragment = self.body().layout(vt, styles, pod)?; diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index 0c3de30c..5fe3c90a 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -21,29 +21,14 @@ use crate::prelude::*; /// ``` /// /// ## Parameters -/// - paper: `Paper` (positional, settable) +/// - paper: `Paper` (positional, named, settable) /// A standard paper size to set width and height. When this is not specified, /// Typst defaults to `{"a4"}` paper. /// /// Display: Page /// Category: layout #[node] -#[set({ - if let Some(paper) = args.named_or_find::<Paper>("paper")? { - styles.set(Self::set_width(Smart::Custom(paper.width().into()))); - styles.set(Self::set_height(Smart::Custom(paper.height().into()))); - } -})] pub struct PageNode { - /// The contents of the page(s). - /// - /// Multiple pages will be created if the content does not fit on a single - /// page. A new page with the page properties prior to the function invocation - /// will be created after the body has been typeset. - #[positional] - #[required] - pub body: Content, - /// The width of the page. /// /// ```example @@ -56,8 +41,12 @@ pub struct PageNode { /// box(square(width: 1cm)) /// } /// ``` - #[settable] #[resolve] + #[parse( + let paper = args.named_or_find::<Paper>("paper")?; + args.named("width")? + .or_else(|| paper.map(|paper| Smart::Custom(paper.width().into()))) + )] #[default(Smart::Custom(Paper::A4.width().into()))] pub width: Smart<Length>, @@ -67,8 +56,11 @@ pub struct PageNode { /// 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. - #[settable] #[resolve] + #[parse( + args.named("height")? + .or_else(|| paper.map(|paper| Smart::Custom(paper.height().into()))) + )] #[default(Smart::Custom(Paper::A4.height().into()))] pub height: Smart<Length>, @@ -90,7 +82,6 @@ pub struct PageNode { /// New York, NY 10001 \ /// +1 555 555 5555 /// ``` - #[settable] #[default(false)] pub flipped: bool, @@ -122,9 +113,7 @@ pub struct PageNode { /// fill: aqua, /// ) /// ``` - #[settable] #[fold] - #[default] pub margin: Sides<Option<Smart<Rel<Length>>>>, /// How many columns the page has. @@ -141,7 +130,6 @@ pub struct PageNode { /// emissions and mitigate the impacts /// of a rapidly changing climate. /// ``` - #[settable] #[default(NonZeroUsize::new(1).unwrap())] pub columns: NonZeroUsize, @@ -157,8 +145,6 @@ pub struct PageNode { /// #set text(fill: rgb("fdfdfd")) /// *Dark mode enabled.* /// ``` - #[settable] - #[default] pub fill: Option<Paint>, /// The page's header. @@ -180,8 +166,6 @@ pub struct PageNode { /// /// #lorem(18) /// ``` - #[settable] - #[default] pub header: Option<Marginal>, /// The page's footer. @@ -205,8 +189,6 @@ pub struct PageNode { /// /// #lorem(18) /// ``` - #[settable] - #[default] pub footer: Option<Marginal>, /// Content in the page's background. @@ -227,8 +209,6 @@ pub struct PageNode { /// In the year 2023, we plan to take over the world /// (of typesetting). /// ``` - #[settable] - #[default] pub background: Option<Marginal>, /// Content in the page's foreground. @@ -245,9 +225,16 @@ pub struct PageNode { /// "Weak Reject" because they did /// not understand our approach... /// ``` - #[settable] - #[default] pub foreground: Option<Marginal>, + + /// The contents of the page(s). + /// + /// Multiple pages will be created if the content does not fit on a single + /// page. A new page with the page properties prior to the function invocation + /// will be created after the body has been typeset. + #[positional] + #[required] + pub body: Content, } impl PageNode { @@ -260,10 +247,10 @@ impl PageNode { ) -> SourceResult<Fragment> { // When one of the lengths is infinite the page fits its content along // that axis. - let width = Self::width_in(styles).unwrap_or(Abs::inf()); - let height = Self::height_in(styles).unwrap_or(Abs::inf()); + let width = self.width(styles).unwrap_or(Abs::inf()); + let height = self.height(styles).unwrap_or(Abs::inf()); let mut size = Size::new(width, height); - if Self::flipped_in(styles) { + if self.flipped(styles) { std::mem::swap(&mut size.x, &mut size.y); } @@ -274,14 +261,14 @@ impl PageNode { // Determine the margins. let default = Rel::from(0.1190 * min); - let padding = Self::margin_in(styles).map(|side| side.unwrap_or(default)); + let padding = self.margin(styles).map(|side| side.unwrap_or(default)); let mut child = self.body(); // Realize columns. - let columns = Self::columns_in(styles); + let columns = self.columns(styles); if columns.get() > 1 { - child = ColumnsNode::new(columns, child).pack(); + child = ColumnsNode::new(child).with_count(columns).pack(); } // Realize margins. @@ -291,11 +278,11 @@ impl PageNode { let regions = Regions::repeat(size, size.map(Abs::is_finite)); let mut fragment = child.layout(vt, styles, regions)?; - let fill = Self::fill_in(styles); - let header = Self::header_in(styles); - let footer = Self::footer_in(styles); - let foreground = Self::foreground_in(styles); - let background = Self::background_in(styles); + let fill = self.fill(styles); + let header = self.header(styles); + let footer = self.footer(styles); + let foreground = self.foreground(styles); + let background = self.background(styles); // Realize overlays. for frame in &mut fragment { @@ -352,7 +339,6 @@ impl PageNode { pub struct PagebreakNode { /// If `{true}`, the page break is skipped if the current page is already /// empty. - #[named] #[default(false)] pub weak: bool, } diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs index 10388f88..93cca452 100644 --- a/library/src/layout/par.rs +++ b/library/src/layout/par.rs @@ -43,11 +43,6 @@ use crate::text::{ /// Category: layout #[node(Construct)] pub struct ParNode { - /// The paragraph's children. - #[variadic] - #[skip] - pub children: Vec<Content>, - /// The indent the first line of a consecutive paragraph should have. /// /// The first paragraph on a page will never be indented. @@ -56,15 +51,12 @@ pub struct ParNode { /// space between paragraphs or indented first lines. Consider turning the /// [paragraph spacing]($func/block.spacing) off when using this property /// (e.g. using `[#show par: set block(spacing: 0pt)]`). - #[settable] #[resolve] - #[default] pub indent: Length, /// The spacing between lines. /// /// The default value is `{0.65em}`. - #[settable] #[resolve] #[default(Em::new(0.65).into())] pub leading: Length, @@ -78,7 +70,6 @@ pub struct ParNode { /// 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). - #[settable] #[default(false)] pub justify: bool, @@ -105,9 +96,13 @@ pub struct ParNode { /// very aesthetic example is one /// of them. /// ``` - #[settable] #[default] pub linebreaks: Smart<Linebreaks>, + + /// The paragraph's children. + #[internal] + #[variadic] + pub children: Vec<Content>, } impl Construct for ParNode { @@ -115,9 +110,11 @@ impl Construct for ParNode { // The paragraph constructor is special: It doesn't create a paragraph // node. Instead, it just ensures that the passed content lives in a // separate paragraph and styles it. + let styles = Self::set(args)?; + let body = args.expect::<Content>("body")?; Ok(Content::sequence(vec![ ParbreakNode::new().pack(), - args.expect("body")?, + body.styled_with_map(styles), ParbreakNode::new().pack(), ])) } @@ -334,7 +331,7 @@ enum Segment<'a> { /// A math formula. Formula(&'a FormulaNode), /// A box with arbitrary content. - Box(&'a BoxNode), + Box(&'a BoxNode, bool), } impl Segment<'_> { @@ -343,8 +340,8 @@ impl Segment<'_> { match *self { Self::Text(len) => len, Self::Spacing(_) => SPACING_REPLACE.len_utf8(), - Self::Box(node) if node.width().is_fractional() => SPACING_REPLACE.len_utf8(), - Self::Formula(_) | Self::Box(_) => NODE_REPLACE.len_utf8(), + Self::Box(_, true) => SPACING_REPLACE.len_utf8(), + Self::Formula(_) | Self::Box(_, _) => NODE_REPLACE.len_utf8(), } } } @@ -540,7 +537,7 @@ fn collect<'a>( full.push(SPACING_REPLACE); Segment::Spacing(node.amount()) } else if let Some(node) = child.to::<LinebreakNode>() { - let c = if node.justify() { '\u{2028}' } else { '\n' }; + let c = if node.justify(styles) { '\u{2028}' } else { '\n' }; full.push(c); Segment::Text(c.len_utf8()) } else if let Some(node) = child.to::<SmartQuoteNode>() { @@ -561,21 +558,18 @@ fn collect<'a>( } }); - full.push_str(quoter.quote("es, node.double(), peeked)); + full.push_str(quoter.quote("es, node.double(styles), peeked)); } else { - full.push(if node.double() { '"' } else { '\'' }); + full.push(if node.double(styles) { '"' } else { '\'' }); } Segment::Text(full.len() - prev) } else if let Some(node) = child.to::<FormulaNode>() { full.push(NODE_REPLACE); Segment::Formula(node) } else if let Some(node) = child.to::<BoxNode>() { - full.push(if node.width().is_fractional() { - SPACING_REPLACE - } else { - NODE_REPLACE - }); - Segment::Box(node) + let frac = node.width(styles).is_fractional(); + full.push(if frac { SPACING_REPLACE } else { NODE_REPLACE }); + Segment::Box(node, frac) } else if let Some(span) = child.span() { bail!(span, "unexpected document child"); } else { @@ -645,8 +639,8 @@ fn prepare<'a>( frame.translate(Point::with_y(TextNode::baseline_in(styles))); items.push(Item::Frame(frame)); } - Segment::Box(node) => { - if let Sizing::Fr(v) = node.width() { + Segment::Box(node, _) => { + if let Sizing::Fr(v) = node.width(styles) { items.push(Item::Fractional(v, Some((node, styles)))); } else { let pod = Regions::one(region, Axes::splat(false)); diff --git a/library/src/layout/place.rs b/library/src/layout/place.rs index b4aaf73d..8d7aa229 100644 --- a/library/src/layout/place.rs +++ b/library/src/layout/place.rs @@ -34,11 +34,6 @@ pub struct PlaceNode { #[default(Axes::with_x(Some(GenAlign::Start)))] pub alignment: Axes<Option<GenAlign>>, - /// The content to place. - #[positional] - #[required] - pub body: Content, - /// The horizontal displacement of the placed content. /// /// ```example @@ -48,14 +43,15 @@ pub struct PlaceNode { /// place(center, dx: amount - 32pt, dy: amount)[A] /// } /// ``` - #[named] - #[default] pub dx: Rel<Length>, /// The vertical displacement of the placed content. - #[named] - #[default] pub dy: Rel<Length>, + + /// The content to place. + #[positional] + #[required] + pub body: Content, } impl Layout for PlaceNode { @@ -65,7 +61,7 @@ impl Layout for PlaceNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let out_of_flow = self.out_of_flow(); + let out_of_flow = self.out_of_flow(styles); // The pod is the base area of the region because for absolute // placement we don't really care about the already used area. @@ -77,8 +73,8 @@ impl Layout for PlaceNode { let child = self .body() - .moved(Axes::new(self.dx(), self.dy())) - .aligned(self.alignment()); + .moved(Axes::new(self.dx(styles), self.dy(styles))) + .aligned(self.alignment(styles)); let mut frame = child.layout(vt, styles, pod)?.into_frame(); @@ -95,8 +91,8 @@ impl PlaceNode { /// Whether this node wants to be placed relative to its its parent's base /// origin. Instead of relative to the parent's current flow/cursor /// position. - pub fn out_of_flow(&self) -> bool { - self.alignment().y.is_some() + pub fn out_of_flow(&self, styles: StyleChain) -> bool { + self.alignment(styles).y.is_some() } } diff --git a/library/src/layout/spacing.rs b/library/src/layout/spacing.rs index 9f552730..c11a2f06 100644 --- a/library/src/layout/spacing.rs +++ b/library/src/layout/spacing.rs @@ -42,7 +42,6 @@ pub struct HNode { /// #h(8pt, weak: true) on both /// sides, they do show up. /// ``` - #[named] #[default(false)] pub weak: bool, } @@ -51,7 +50,7 @@ impl Behave for HNode { fn behaviour(&self) -> Behaviour { if self.amount().is_fractional() { Behaviour::Destructive - } else if self.weak() { + } else if self.weak(StyleChain::default()) { Behaviour::Weak(1) } else { Behaviour::Ignorant @@ -86,7 +85,7 @@ impl Behave for HNode { /// ``` /// /// ## Parameters -/// - weak: `bool` (named) +/// - weak: `bool` (named, settable) /// If true, the spacing collapses at the start or end of a flow. Moreover, /// from multiple adjacent weak spacings all but the largest one collapse. /// Weak spacings will always collapse adjacent paragraph spacing, even if the @@ -103,7 +102,7 @@ impl Behave for HNode { /// /// Display: Spacing (V) /// Category: layout -#[node(Construct, Behave)] +#[node(Behave)] pub struct VNode { /// How much spacing to insert. #[positional] @@ -111,24 +110,11 @@ pub struct VNode { pub amount: Spacing, /// The node's weakness level, see also [`Behaviour`]. - #[named] - #[skip] - #[default] + #[internal] + #[parse(args.named("weak")?.map(|v: bool| v as usize))] pub weakness: usize, } -impl Construct for VNode { - fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { - let amount = args.expect("spacing")?; - let node = if args.named("weak")?.unwrap_or(false) { - Self::weak(amount) - } else { - Self::strong(amount) - }; - Ok(node.pack()) - } -} - impl VNode { /// Normal strong spacing. pub fn strong(amount: Spacing) -> Self { @@ -160,8 +146,8 @@ impl Behave for VNode { fn behaviour(&self) -> Behaviour { if self.amount().is_fractional() { Behaviour::Destructive - } else if self.weakness() > 0 { - Behaviour::Weak(self.weakness()) + } else if self.weakness(StyleChain::default()) > 0 { + Behaviour::Weak(self.weakness(StyleChain::default())) } else { Behaviour::Ignorant } diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index f4e4ab2c..c21fa884 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -22,24 +22,21 @@ use crate::prelude::*; /// Category: layout #[node(Layout)] pub struct StackNode { - /// The childfren to stack along the axis. - #[variadic] - pub children: Vec<StackChild>, - /// The direction along which the items are stacked. Possible values are: /// /// - `{ltr}`: Left to right. /// - `{rtl}`: Right to left. /// - `{ttb}`: Top to bottom. /// - `{btt}`: Bottom to top. - #[named] #[default(Dir::TTB)] pub dir: Dir, /// Spacing to insert between items where no explicit spacing was provided. - #[named] - #[default] pub spacing: Option<Spacing>, + + /// The childfren to stack along the axis. + #[variadic] + pub children: Vec<StackChild>, } impl Layout for StackNode { @@ -49,10 +46,10 @@ impl Layout for StackNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let mut layouter = StackLayouter::new(self.dir(), regions, styles); + let mut layouter = StackLayouter::new(self.dir(styles), regions, styles); // Spacing to insert before the next block. - let spacing = self.spacing(); + let spacing = self.spacing(styles); let mut deferred = None; for child in self.children() { @@ -201,12 +198,15 @@ impl<'a> StackLayouter<'a> { // Block-axis alignment of the `AlignNode` is respected // by the stack node. - let aligns = match block.to::<StyledNode>() { - Some(styled) => AlignNode::alignment_in(styles.chain(&styled.map())), - None => AlignNode::alignment_in(styles), - }; + let aligns = if let Some(align) = block.to::<AlignNode>() { + align.alignment(styles) + } else if let Some(styled) = block.to::<StyledNode>() { + AlignNode::alignment_in(styles.chain(&styled.map())) + } else { + AlignNode::alignment_in(styles) + } + .resolve(styles); - let aligns = aligns.resolve(styles); let fragment = block.layout(vt, styles, self.regions)?; let len = fragment.len(); for (i, frame) in fragment.into_iter().enumerate() { diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs index 1bd47df0..59635119 100644 --- a/library/src/layout/table.rs +++ b/library/src/layout/table.rs @@ -30,7 +30,7 @@ use crate::prelude::*; /// ``` /// /// ## Parameters -/// - gutter: `TrackSizings` (named) +/// - gutter: `TrackSizings` (named, settable) /// Defines the gaps between rows & columns. /// See the [grid documentation]($func/grid) for more information on gutters. /// @@ -38,36 +38,27 @@ use crate::prelude::*; /// Category: layout #[node(Layout)] pub struct TableNode { - /// The contents of the table cells. - #[variadic] - pub children: Vec<Content>, - /// Defines the column sizes. /// See the [grid documentation]($func/grid) for more information on track /// sizing. - #[named] - #[default] pub columns: TrackSizings, /// Defines the row sizes. /// See the [grid documentation]($func/grid) for more information on track /// sizing. - #[named] - #[default] pub rows: TrackSizings, /// Defines the gaps between columns. Takes precedence over `gutter`. /// See the [grid documentation]($func/grid) for more information on gutters. - #[named] - #[shorthand(gutter)] - #[default] + #[parse( + let gutter = args.named("gutter")?; + args.named("column-gutter")?.or_else(|| gutter.clone()) + )] pub column_gutter: TrackSizings, /// Defines the gaps between rows. Takes precedence over `gutter`. /// See the [grid documentation]($func/grid) for more information on gutters. - #[named] - #[shorthand(gutter)] - #[default] + #[parse(args.named("row-gutter")?.or_else(|| gutter.clone()))] pub row_gutter: TrackSizings, /// How to fill the cells. @@ -90,8 +81,6 @@ pub struct TableNode { /// [Profit:], [500 €], [1000 €], [1500 €], /// ) /// ``` - #[settable] - #[default] pub fill: Celled<Option<Paint>>, /// How to align the cell's content. @@ -99,15 +88,12 @@ pub struct TableNode { /// This can either be a single alignment or a function that returns an /// alignment. The function is passed the cell's column and row index, /// starting at zero. If set to `{auto}`, the outer alignment is used. - #[settable] - #[default] pub align: Celled<Smart<Axes<Option<GenAlign>>>>, /// How to stroke the cells. /// /// This can be a color, a stroke width, both, or `{none}` to disable /// the stroke. - #[settable] #[resolve] #[fold] #[default(Some(PartialStroke::default()))] @@ -116,9 +102,12 @@ pub struct TableNode { /// How much to pad the cells's content. /// /// The default value is `{5pt}`. - #[settable] #[default(Abs::pt(5.0).into())] pub inset: Rel<Length>, + + /// The contents of the table cells. + #[variadic] + pub children: Vec<Content>, } impl Layout for TableNode { @@ -128,11 +117,11 @@ impl Layout for TableNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let inset = Self::inset_in(styles); - let align = Self::align_in(styles); + let inset = self.inset(styles); + let align = self.align(styles); - let tracks = Axes::new(self.columns().0, self.rows().0); - let gutter = Axes::new(self.column_gutter().0, self.row_gutter().0); + let tracks = Axes::new(self.columns(styles).0, self.rows(styles).0); + let gutter = Axes::new(self.column_gutter(styles).0, self.row_gutter(styles).0); let cols = tracks.x.len().max(1); let cells: Vec<_> = self .children() @@ -151,8 +140,8 @@ impl Layout for TableNode { }) .collect::<SourceResult<_>>()?; - let fill = Self::fill_in(styles); - let stroke = Self::stroke_in(styles).map(PartialStroke::unwrap_or_default); + let fill = self.fill(styles); + let stroke = self.stroke(styles).map(PartialStroke::unwrap_or_default); // Prepare grid layout by unifying content and gutter tracks. let layouter = GridLayouter::new( diff --git a/library/src/layout/terms.rs b/library/src/layout/terms.rs index d859a447..8ab4edc6 100644 --- a/library/src/layout/terms.rs +++ b/library/src/layout/terms.rs @@ -23,21 +23,6 @@ use crate::text::{SpaceNode, TextNode}; /// Category: layout #[node(Layout)] pub struct TermsNode { - /// The term list's children. - /// - /// When using the term list syntax, adjacent items are automatically - /// collected into term lists, even through constructs like for loops. - /// - /// ```example - /// #for year, product in ( - /// "1978": "TeX", - /// "1984": "LaTeX", - /// "2019": "Typst", - /// ) [/ #product: Born in #year.] - /// ``` - #[variadic] - pub children: Vec<TermItem>, - /// 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 @@ -53,14 +38,11 @@ pub struct TermsNode { /// insert a blank line between the /// items. /// ``` - #[named] #[default(true)] pub tight: bool, /// The indentation of each item's term. - #[settable] #[resolve] - #[default] pub indent: Length, /// The hanging indent of the description. @@ -70,7 +52,6 @@ pub struct TermsNode { /// / Term: This term list does not /// make use of hanging indents. /// ``` - #[settable] #[resolve] #[default(Em::new(1.0).into())] pub hanging_indent: Length, @@ -78,9 +59,22 @@ pub struct TermsNode { /// The spacing between the items of a wide (non-tight) term list. /// /// If set to `{auto}`, uses the spacing [below blocks]($func/block.below). - #[settable] - #[default] pub spacing: Smart<Spacing>, + + /// The term list's children. + /// + /// When using the term list syntax, adjacent items are automatically + /// collected into term lists, even through constructs like for loops. + /// + /// ```example + /// #for year, product in ( + /// "1978": "TeX", + /// "1984": "LaTeX", + /// "2019": "Typst", + /// ) [/ #product: Born in #year.] + /// ``` + #[variadic] + pub children: Vec<TermItem>, } impl Layout for TermsNode { @@ -90,12 +84,12 @@ impl Layout for TermsNode { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - let indent = Self::indent_in(styles); - let body_indent = Self::hanging_indent_in(styles); - let gutter = if self.tight() { + let indent = self.indent(styles); + let body_indent = self.hanging_indent(styles); + let gutter = if self.tight(styles) { ParNode::leading_in(styles).into() } else { - Self::spacing_in(styles) + self.spacing(styles) .unwrap_or_else(|| BlockNode::below_in(styles).amount()) }; diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs index 41d3d120..4521da32 100644 --- a/library/src/layout/transform.rs +++ b/library/src/layout/transform.rs @@ -25,20 +25,16 @@ use crate::prelude::*; /// Category: layout #[node(Layout)] pub struct MoveNode { - /// The content to move. - #[positional] - #[required] - pub body: Content, - /// The horizontal displacement of the content. - #[named] - #[default] pub dx: Rel<Length>, /// The vertical displacement of the content. - #[named] - #[default] pub dy: Rel<Length>, + + /// The content to move. + #[positional] + #[required] + pub body: Content, } impl Layout for MoveNode { @@ -50,7 +46,7 @@ impl Layout for MoveNode { ) -> SourceResult<Fragment> { 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(), self.dy()).resolve(styles); + 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)); frame.translate(delta.to_point()); Ok(Fragment::frame(frame)) @@ -83,14 +79,8 @@ pub struct RotateNode { /// ``` /// #[positional] - #[required] pub angle: Angle, - /// The content to rotate. - #[positional] - #[required] - pub body: Content, - /// The origin of the rotation. /// /// By default, the origin is the center of the rotated element. If, @@ -107,10 +97,13 @@ pub struct RotateNode { /// #box(rotate(30deg, origin: top + left, square())) /// #box(rotate(30deg, origin: bottom + right, square())) /// ``` - #[settable] #[resolve] - #[default] pub origin: Axes<Option<GenAlign>>, + + /// The content to rotate. + #[positional] + #[required] + pub body: Content, } impl Layout for RotateNode { @@ -122,10 +115,10 @@ impl Layout for RotateNode { ) -> SourceResult<Fragment> { let pod = Regions::one(regions.base(), Axes::splat(false)); let mut frame = self.body().layout(vt, styles, pod)?.into_frame(); - let origin = Self::origin_in(styles).unwrap_or(Align::CENTER_HORIZON); + let origin = self.origin(styles).unwrap_or(Align::CENTER_HORIZON); let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); let ts = Transform::translate(x, y) - .pre_concat(Transform::rotate(self.angle())) + .pre_concat(Transform::rotate(self.angle(styles))) .pre_concat(Transform::translate(-x, -y)); frame.transform(ts); Ok(Fragment::frame(frame)) @@ -146,24 +139,22 @@ impl Layout for RotateNode { /// /// Display: Scale /// Category: layout -#[node(Construct, Layout)] +#[node(Layout)] pub struct ScaleNode { - /// The content to scale. - #[positional] - #[required] - pub body: Content, - /// The horizontal scaling factor. /// /// The body will be mirrored horizontally if the parameter is negative. - #[named] + #[parse( + let all = args.find()?; + args.named("x")?.or(all) + )] #[default(Ratio::one())] pub x: Ratio, /// The vertical scaling factor. /// /// The body will be mirrored vertically if the parameter is negative. - #[named] + #[parse(args.named("y")?.or(all))] #[default(Ratio::one())] pub y: Ratio, @@ -175,19 +166,13 @@ pub struct ScaleNode { /// A#box(scale(75%)[A])A \ /// B#box(scale(75%, origin: bottom + left)[B])B /// ``` - #[settable] #[resolve] - #[default] pub origin: Axes<Option<GenAlign>>, -} -impl Construct for ScaleNode { - fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> { - let all = args.find()?; - let x = args.named("x")?.or(all).unwrap_or(Ratio::one()); - let y = args.named("y")?.or(all).unwrap_or(Ratio::one()); - Ok(Self::new(args.expect::<Content>("body")?).with_x(x).with_y(y).pack()) - } + /// The content to scale. + #[positional] + #[required] + pub body: Content, } impl Layout for ScaleNode { @@ -199,10 +184,10 @@ impl Layout for ScaleNode { ) -> SourceResult<Fragment> { let pod = Regions::one(regions.base(), Axes::splat(false)); let mut frame = self.body().layout(vt, styles, pod)?.into_frame(); - let origin = Self::origin_in(styles).unwrap_or(Align::CENTER_HORIZON); + let origin = self.origin(styles).unwrap_or(Align::CENTER_HORIZON); let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); let transform = Transform::translate(x, y) - .pre_concat(Transform::scale(self.x(), self.y())) + .pre_concat(Transform::scale(self.x(styles), self.y(styles))) .pre_concat(Transform::translate(-x, -y)); frame.transform(transform); Ok(Fragment::frame(frame)) |
