summaryrefslogtreecommitdiff
path: root/library/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-09 14:17:24 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-09 14:42:14 +0100
commitc38d72383d2068361d635d6c1c78ba97aa917801 (patch)
treee758418a2d704d69dee88faf4a9a9c69b25b47ca /library/src
parentd7a65fa26d131179d9d82226e5ee1b562084e48a (diff)
Make all optional fields settable
Diffstat (limited to 'library/src')
-rw-r--r--library/src/layout/align.rs11
-rw-r--r--library/src/layout/columns.rs20
-rw-r--r--library/src/layout/container.rs124
-rw-r--r--library/src/layout/enum.rs104
-rw-r--r--library/src/layout/flow.rs19
-rw-r--r--library/src/layout/grid.rs33
-rw-r--r--library/src/layout/list.rs49
-rw-r--r--library/src/layout/mod.rs30
-rw-r--r--library/src/layout/pad.rs67
-rw-r--r--library/src/layout/page.rs74
-rw-r--r--library/src/layout/par.rs44
-rw-r--r--library/src/layout/place.rs24
-rw-r--r--library/src/layout/spacing.rs28
-rw-r--r--library/src/layout/stack.rs28
-rw-r--r--library/src/layout/table.rs43
-rw-r--r--library/src/layout/terms.rs44
-rw-r--r--library/src/layout/transform.rs65
-rw-r--r--library/src/lib.rs8
-rw-r--r--library/src/math/attach.rs14
-rw-r--r--library/src/math/delimited.rs26
-rw-r--r--library/src/math/matrix.rs54
-rw-r--r--library/src/math/mod.rs17
-rw-r--r--library/src/math/op.rs3
-rw-r--r--library/src/math/underover.rs12
-rw-r--r--library/src/meta/document.rs17
-rw-r--r--library/src/meta/heading.rs24
-rw-r--r--library/src/meta/link.rs33
-rw-r--r--library/src/meta/outline.rs25
-rw-r--r--library/src/text/deco.rs72
-rw-r--r--library/src/text/misc.rs14
-rw-r--r--library/src/text/mod.rs90
-rw-r--r--library/src/text/quotes.rs2
-rw-r--r--library/src/text/raw.rs11
-rw-r--r--library/src/text/shift.rs38
-rw-r--r--library/src/visualize/image.rs30
-rw-r--r--library/src/visualize/line.rs84
-rw-r--r--library/src/visualize/shape.rs231
37 files changed, 610 insertions, 1002 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(&regions.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(&quotes, node.double(), peeked));
+ full.push_str(quoter.quote(&quotes, 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))
diff --git a/library/src/lib.rs b/library/src/lib.rs
index fcd0bf45..bcbd703d 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -176,11 +176,11 @@ fn items() -> LangItems {
strong: |body| text::StrongNode::new(body).pack(),
emph: |body| text::EmphNode::new(body).pack(),
raw: |text, lang, block| {
- let content = text::RawNode::new(text).with_block(block).pack();
- match lang {
- Some(_) => content.styled(text::RawNode::set_lang(lang)),
- None => content,
+ let mut node = text::RawNode::new(text).with_block(block);
+ if let Some(lang) = lang {
+ node = node.with_lang(Some(lang));
}
+ node.pack()
},
link: |url| meta::LinkNode::from_url(url).pack(),
ref_: |target| meta::RefNode::new(target).pack(),
diff --git a/library/src/math/attach.rs b/library/src/math/attach.rs
index 7ba1484a..c2d7703d 100644
--- a/library/src/math/attach.rs
+++ b/library/src/math/attach.rs
@@ -21,13 +21,9 @@ pub struct AttachNode {
pub base: Content,
/// The top attachment.
- #[named]
- #[default]
pub top: Option<Content>,
/// The bottom attachment.
- #[named]
- #[default]
pub bottom: Option<Content>,
}
@@ -40,11 +36,17 @@ impl LayoutMath for AttachNode {
let base = ctx.layout_fragment(&base)?;
ctx.style(ctx.style.for_subscript());
- let top = self.top().map(|node| ctx.layout_fragment(&node)).transpose()?;
+ let top = self
+ .top(ctx.styles())
+ .map(|node| ctx.layout_fragment(&node))
+ .transpose()?;
ctx.unstyle();
ctx.style(ctx.style.for_superscript());
- let bottom = self.bottom().map(|node| ctx.layout_fragment(&node)).transpose()?;
+ let bottom = self
+ .bottom(ctx.styles())
+ .map(|node| ctx.layout_fragment(&node))
+ .transpose()?;
ctx.unstyle();
let display_limits = display_limits
diff --git a/library/src/math/delimited.rs b/library/src/math/delimited.rs
index 63cb916c..f9d22c43 100644
--- a/library/src/math/delimited.rs
+++ b/library/src/math/delimited.rs
@@ -16,23 +16,17 @@ pub(super) const DELIM_SHORT_FALL: Em = Em::new(0.1);
///
/// Display: Left/Right
/// Category: math
-#[node(Construct, LayoutMath)]
+#[node(LayoutMath)]
pub struct LrNode {
- /// The delimited content, including the delimiters.
- #[positional]
- #[required]
- pub body: Content,
-
/// The size of the brackets, relative to the height of the wrapped content.
///
/// Defaults to `{100%}`.
- #[named]
- #[default]
pub size: Smart<Rel<Length>>,
-}
-impl Construct for LrNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ /// The delimited content, including the delimiters.
+ #[positional]
+ #[required]
+ #[parse(
let mut body = Content::empty();
for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
if i > 0 {
@@ -40,16 +34,16 @@ impl Construct for LrNode {
}
body += arg;
}
- let size = args.named::<Smart<Rel<Length>>>("size")?.unwrap_or_default();
- Ok(Self::new(body).with_size(size).pack())
- }
+ body
+ )]
+ pub body: Content,
}
impl LayoutMath for LrNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
let mut body = self.body();
if let Some(node) = body.to::<LrNode>() {
- if node.size().is_auto() {
+ if node.size(ctx.styles()).is_auto() {
body = node.body();
}
}
@@ -63,7 +57,7 @@ impl LayoutMath for LrNode {
.unwrap_or_default();
let height = self
- .size()
+ .size(ctx.styles())
.unwrap_or(Rel::one())
.resolve(ctx.styles())
.relative_to(2.0 * max_extent);
diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs
index d4bf52f3..6dec645c 100644
--- a/library/src/math/matrix.rs
+++ b/library/src/math/matrix.rs
@@ -18,24 +18,23 @@ const VERTICAL_PADDING: Ratio = Ratio::new(0.1);
/// Category: math
#[node(LayoutMath)]
pub struct VecNode {
- /// The elements of the vector.
- #[variadic]
- pub children: Vec<Content>,
-
/// The delimiter to use.
///
/// ```example
/// #set math.vec(delim: "[")
/// $ vec(1, 2) $
/// ```
- #[settable]
#[default(Delimiter::Paren)]
pub delim: Delimiter,
+
+ /// The elements of the vector.
+ #[variadic]
+ pub children: Vec<Content>,
}
impl LayoutMath for VecNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let delim = Self::delim_in(ctx.styles());
+ let delim = self.delim(ctx.styles());
let frame = layout_vec_body(ctx, &self.children(), Align::Center)?;
layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close()))
}
@@ -63,31 +62,26 @@ impl LayoutMath for VecNode {
///
/// Display: Matrix
/// Category: math
-#[node(Construct, LayoutMath)]
+#[node(LayoutMath)]
pub struct MatNode {
- /// An array of arrays with the rows of the matrix.
- ///
- /// ```example
- /// #let data = ((1, 2, 3), (4, 5, 6))
- /// #let matrix = math.mat(..data)
- /// $ v := matrix $
- /// ```
- #[variadic]
- pub rows: Vec<Vec<Content>>,
-
/// The delimiter to use.
///
/// ```example
/// #set math.mat(delim: "[")
/// $ mat(1, 2; 3, 4) $
/// ```
- #[settable]
#[default(Delimiter::Paren)]
pub delim: Delimiter,
-}
-impl Construct for MatNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ /// An array of arrays with the rows of the matrix.
+ ///
+ /// ```example
+ /// #let data = ((1, 2, 3), (4, 5, 6))
+ /// #let matrix = math.mat(..data)
+ /// $ v := matrix $
+ /// ```
+ #[variadic]
+ #[parse(
let mut rows = vec![];
let mut width = 0;
@@ -109,13 +103,14 @@ impl Construct for MatNode {
}
}
- Ok(Self::new(rows).pack())
- }
+ rows
+ )]
+ pub rows: Vec<Vec<Content>>,
}
impl LayoutMath for MatNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let delim = Self::delim_in(ctx.styles());
+ let delim = self.delim(ctx.styles());
let frame = layout_mat_body(ctx, &self.rows())?;
layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close()))
}
@@ -139,24 +134,23 @@ impl LayoutMath for MatNode {
/// Category: math
#[node(LayoutMath)]
pub struct CasesNode {
- /// The branches of the case distinction.
- #[variadic]
- pub children: Vec<Content>,
-
/// The delimiter to use.
///
/// ```example
/// #set math.cases(delim: "[")
/// $ x = cases(1, 2) $
/// ```
- #[settable]
#[default(Delimiter::Brace)]
pub delim: Delimiter,
+
+ /// The branches of the case distinction.
+ #[variadic]
+ pub children: Vec<Content>,
}
impl LayoutMath for CasesNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- let delim = Self::delim_in(ctx.styles());
+ let delim = self.delim(ctx.styles());
let frame = layout_vec_body(ctx, &self.children(), Align::Left)?;
layout_delimiters(ctx, frame, Some(delim.open()), None)
}
diff --git a/library/src/math/mod.rs b/library/src/math/mod.rs
index 3f0b0607..0c9dc338 100644
--- a/library/src/math/mod.rs
+++ b/library/src/math/mod.rs
@@ -134,21 +134,20 @@ pub fn module() -> Module {
/// Category: math
#[node(Show, Finalize, Layout, LayoutMath)]
pub struct FormulaNode {
+ /// Whether the formula is displayed as a separate block.
+ #[default(false)]
+ pub block: bool,
+
/// The content of the formula.
#[positional]
#[required]
pub body: Content,
-
- /// Whether the formula is displayed as a separate block.
- #[named]
- #[default(false)]
- pub block: bool,
}
impl Show for FormulaNode {
- fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult<Content> {
+ fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
let mut realized = self.clone().pack().guarded(Guard::Base(NodeId::of::<Self>()));
- if self.block() {
+ if self.block(styles) {
realized = realized.aligned(Axes::with_x(Some(Align::Center.into())))
}
Ok(realized)
@@ -156,7 +155,7 @@ impl Show for FormulaNode {
}
impl Finalize for FormulaNode {
- fn finalize(&self, realized: Content) -> Content {
+ fn finalize(&self, realized: Content, _: StyleChain) -> Content {
realized
.styled(TextNode::set_weight(FontWeight::from_number(450)))
.styled(TextNode::set_font(FontList(vec![FontFamily::new(
@@ -172,7 +171,7 @@ impl Layout for FormulaNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let block = self.block();
+ let block = self.block(styles);
// Find a math font.
let variant = variant(styles);
diff --git a/library/src/math/op.rs b/library/src/math/op.rs
index c855cd92..aa2e4cf7 100644
--- a/library/src/math/op.rs
+++ b/library/src/math/op.rs
@@ -30,7 +30,6 @@ pub struct OpNode {
/// Whether the operator should force attachments to display as limits.
///
/// Defaults to `{false}`.
- #[named]
#[default(false)]
pub limits: bool,
}
@@ -41,7 +40,7 @@ impl LayoutMath for OpNode {
ctx.push(
FrameFragment::new(ctx, frame)
.with_class(MathClass::Large)
- .with_limits(self.limits()),
+ .with_limits(self.limits(ctx.styles())),
);
Ok(())
}
diff --git a/library/src/math/underover.rs b/library/src/math/underover.rs
index 87f30c0f..2aabf132 100644
--- a/library/src/math/underover.rs
+++ b/library/src/math/underover.rs
@@ -68,13 +68,12 @@ pub struct UnderbraceNode {
/// The optional content below the brace.
#[positional]
- #[default]
pub annotation: Option<Content>,
}
impl LayoutMath for UnderbraceNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body(), &self.annotation(), '⏟', BRACE_GAP, false)
+ layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⏟', BRACE_GAP, false)
}
}
@@ -96,13 +95,12 @@ pub struct OverbraceNode {
/// The optional content above the brace.
#[positional]
- #[default]
pub annotation: Option<Content>,
}
impl LayoutMath for OverbraceNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body(), &self.annotation(), '⏞', BRACE_GAP, true)
+ layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⏞', BRACE_GAP, true)
}
}
@@ -124,13 +122,12 @@ pub struct UnderbracketNode {
/// The optional content below the bracket.
#[positional]
- #[default]
pub annotation: Option<Content>,
}
impl LayoutMath for UnderbracketNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body(), &self.annotation(), '⎵', BRACKET_GAP, false)
+ layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⎵', BRACKET_GAP, false)
}
}
@@ -152,13 +149,12 @@ pub struct OverbracketNode {
/// The optional content above the bracket.
#[positional]
- #[default]
pub annotation: Option<Content>,
}
impl LayoutMath for OverbracketNode {
fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> {
- layout(ctx, &self.body(), &self.annotation(), '⎴', BRACKET_GAP, true)
+ layout(ctx, &self.body(), &self.annotation(ctx.styles()), '⎴', BRACKET_GAP, true)
}
}
diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs
index 1325d85b..8da3c731 100644
--- a/library/src/meta/document.rs
+++ b/library/src/meta/document.rs
@@ -16,20 +16,17 @@ use crate::prelude::*;
/// Category: meta
#[node(LayoutRoot)]
pub struct DocumentNode {
- /// The page runs.
- #[variadic]
- pub children: Vec<Content>,
-
/// The document's title. This is often rendered as the title of the
/// PDF viewer window.
- #[settable]
- #[default]
pub title: Option<EcoString>,
/// The document's authors.
- #[settable]
- #[default]
pub author: Author,
+
+ /// The page runs.
+ #[internal]
+ #[variadic]
+ pub children: Vec<Content>,
}
impl LayoutRoot for DocumentNode {
@@ -58,8 +55,8 @@ impl LayoutRoot for DocumentNode {
Ok(Document {
pages,
- title: Self::title_in(styles),
- author: Self::author_in(styles).0,
+ title: self.title(styles),
+ author: self.author(styles).0,
})
}
}
diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs
index f0107a6a..09cbc8b1 100644
--- a/library/src/meta/heading.rs
+++ b/library/src/meta/heading.rs
@@ -42,13 +42,7 @@ use crate::text::{TextNode, TextSize};
/// Category: meta
#[node(Prepare, Show, Finalize)]
pub struct HeadingNode {
- /// The heading's title.
- #[positional]
- #[required]
- pub body: Content,
-
/// The logical nesting depth of the heading, starting from one.
- #[named]
#[default(NonZeroUsize::new(1).unwrap())]
pub level: NonZeroUsize,
@@ -62,8 +56,6 @@ pub struct HeadingNode {
/// == A subsection
/// === A sub-subsection
/// ```
- #[settable]
- #[default]
pub numbering: Option<Numbering>,
/// Whether the heading should appear in the outline.
@@ -78,9 +70,13 @@ pub struct HeadingNode {
/// This heading does not appear
/// in the outline.
/// ```
- #[settable]
#[default(true)]
pub outlined: bool,
+
+ /// The heading's title.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl Prepare for HeadingNode {
@@ -106,11 +102,11 @@ impl Prepare for HeadingNode {
}
let mut numbers = Value::None;
- if let Some(numbering) = Self::numbering_in(styles) {
+ if let Some(numbering) = self.numbering(styles) {
numbers = numbering.apply(vt.world(), counter.advance(self))?;
}
- this.push_field("outlined", Value::Bool(Self::outlined_in(styles)));
+ this.push_field("outlined", Value::Bool(self.outlined(styles)));
this.push_field("numbers", numbers);
let meta = Meta::Node(my_id, this.clone());
@@ -132,8 +128,8 @@ impl Show for HeadingNode {
}
impl Finalize for HeadingNode {
- fn finalize(&self, realized: Content) -> Content {
- let level = self.level().get();
+ fn finalize(&self, realized: Content, styles: StyleChain) -> Content {
+ let level = self.level(styles).get();
let scale = match level {
1 => 1.4,
2 => 1.2,
@@ -165,7 +161,7 @@ impl HeadingCounter {
/// Advance the counter and return the numbers for the given heading.
pub fn advance(&mut self, heading: &HeadingNode) -> &[NonZeroUsize] {
- let level = heading.level().get();
+ let level = heading.level(StyleChain::default()).get();
if self.0.len() >= level {
self.0[level - 1] = self.0[level - 1].saturating_add(1);
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
index 32b70153..d4d4d8ca 100644
--- a/library/src/meta/link.rs
+++ b/library/src/meta/link.rs
@@ -25,7 +25,7 @@ use crate::text::{Hyphenate, TextNode};
///
/// Display: Link
/// Category: meta
-#[node(Construct, Show, Finalize)]
+#[node(Show, Finalize)]
pub struct LinkNode {
/// The destination the link points to.
///
@@ -48,6 +48,10 @@ pub struct LinkNode {
///
#[positional]
#[required]
+ #[parse(
+ let dest = args.expect::<Destination>("destination")?;
+ dest.clone()
+ )]
pub dest: Destination,
/// How the link is represented.
@@ -56,7 +60,14 @@ pub struct LinkNode {
/// parameter can be omitted. In this case, the URL will be shown as the
/// link.
#[positional]
- #[default]
+ #[required]
+ #[parse(match &dest {
+ Destination::Url(url) => match args.eat()? {
+ Some(body) => body,
+ None => body_from_url(url),
+ },
+ Destination::Internal(_) => args.expect("body")?,
+ })]
pub body: Content,
}
@@ -64,21 +75,7 @@ impl LinkNode {
/// Create a link node from a URL with its bare text.
pub fn from_url(url: EcoString) -> Self {
let body = body_from_url(&url);
- Self::new(Destination::Url(url)).with_body(body)
- }
-}
-
-impl Construct for LinkNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let dest = args.expect::<Destination>("destination")?;
- let body = match &dest {
- Destination::Url(url) => match args.eat()? {
- Some(body) => body,
- None => body_from_url(url),
- },
- Destination::Internal(_) => args.expect("body")?,
- };
- Ok(Self::new(dest).with_body(body).pack())
+ Self::new(Destination::Url(url), body)
}
}
@@ -89,7 +86,7 @@ impl Show for LinkNode {
}
impl Finalize for LinkNode {
- fn finalize(&self, realized: Content) -> Content {
+ fn finalize(&self, realized: Content, _: StyleChain) -> Content {
realized
.styled(MetaNode::set_data(vec![Meta::Link(self.dest())]))
.styled(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))))
diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs
index 3ccc991e..9e5614aa 100644
--- a/library/src/meta/outline.rs
+++ b/library/src/meta/outline.rs
@@ -30,14 +30,11 @@ pub struct OutlineNode {
/// language]($func/text.lang) will be used. This is the default.
/// - When set to `{none}`, the outline will not have a title.
/// - A custom title can be set by passing content.
- #[settable]
#[default(Some(Smart::Auto))]
pub title: Option<Smart<Content>>,
/// The maximum depth up to which headings are included in the outline. When
/// this argument is `{none}`, all headings are included.
- #[settable]
- #[default]
pub depth: Option<NonZeroUsize>,
/// Whether to indent the subheadings to align the start of their numbering
@@ -57,7 +54,6 @@ pub struct OutlineNode {
/// == Products
/// #lorem(10)
/// ```
- #[settable]
#[default(false)]
pub indent: bool,
@@ -69,7 +65,6 @@ pub struct OutlineNode {
///
/// = A New Beginning
/// ```
- #[settable]
#[default(Some(RepeatNode::new(TextNode::packed(".")).pack()))]
pub fill: Option<Content>,
}
@@ -102,7 +97,7 @@ impl Show for OutlineNode {
styles: StyleChain,
) -> SourceResult<Content> {
let mut seq = vec![ParbreakNode::new().pack()];
- if let Some(title) = Self::title_in(styles) {
+ if let Some(title) = self.title(styles) {
let title = title.clone().unwrap_or_else(|| {
TextNode::packed(match TextNode::lang_in(styles) {
Lang::GERMAN => "Inhaltsverzeichnis",
@@ -112,14 +107,15 @@ impl Show for OutlineNode {
seq.push(
HeadingNode::new(title)
- .pack()
- .styled(HeadingNode::set_numbering(None))
- .styled(HeadingNode::set_outlined(false)),
+ .with_level(NonZeroUsize::new(1).unwrap())
+ .with_numbering(None)
+ .with_outlined(false)
+ .pack(),
);
}
- let indent = Self::indent_in(styles);
- let depth = Self::depth_in(styles);
+ let indent = self.indent(styles);
+ let depth = self.depth(styles);
let mut ancestors: Vec<&Content> = vec![];
for (_, node) in vt.locate(Selector::node::<HeadingNode>()) {
@@ -129,13 +125,14 @@ impl Show for OutlineNode {
let heading = node.to::<HeadingNode>().unwrap();
if let Some(depth) = depth {
- if depth < heading.level() {
+ if depth < heading.level(StyleChain::default()) {
continue;
}
}
while ancestors.last().map_or(false, |last| {
- last.to::<HeadingNode>().unwrap().level() >= heading.level()
+ last.to::<HeadingNode>().unwrap().level(StyleChain::default())
+ >= heading.level(StyleChain::default())
}) {
ancestors.pop();
}
@@ -171,7 +168,7 @@ impl Show for OutlineNode {
seq.push(start.linked(Destination::Internal(loc)));
// Add filler symbols between the section name and page number.
- if let Some(filler) = Self::fill_in(styles) {
+ if let Some(filler) = self.fill(styles) {
seq.push(SpaceNode::new().pack());
seq.push(
BoxNode::new()
diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs
index 1b3167e1..2a552226 100644
--- a/library/src/text/deco.rs
+++ b/library/src/text/deco.rs
@@ -15,11 +15,6 @@ use crate::prelude::*;
/// Category: text
#[node(Show)]
pub struct UnderlineNode {
- /// The content to underline.
- #[positional]
- #[required]
- pub body: Content,
-
/// How to stroke the line. The text color and thickness are read from the
/// font tables if `{auto}`.
///
@@ -30,10 +25,8 @@ pub struct UnderlineNode {
/// [care],
/// )
/// ```
- #[settable]
#[resolve]
#[fold]
- #[default]
pub stroke: Smart<PartialStroke>,
/// Position of the line relative to the baseline, read from the font tables
@@ -44,9 +37,7 @@ pub struct UnderlineNode {
/// The Tale Of A Faraway Line I
/// ]
/// ```
- #[settable]
#[resolve]
- #[default]
pub offset: Smart<Length>,
/// Amount that the line will be longer or shorter than its associated text.
@@ -56,9 +47,7 @@ pub struct UnderlineNode {
/// underline(extent: 2pt)[Chapter 1]
/// )
/// ```
- #[settable]
#[resolve]
- #[default]
pub extent: Length,
/// Whether the line skips sections in which it would collide with the
@@ -68,19 +57,23 @@ pub struct UnderlineNode {
/// This #underline(evade: true)[is great].
/// This #underline(evade: false)[is less great].
/// ```
- #[settable]
#[default(true)]
pub evade: bool,
+
+ /// The content to underline.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl Show for UnderlineNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().styled(TextNode::set_deco(Decoration {
line: DecoLine::Underline,
- stroke: Self::stroke_in(styles).unwrap_or_default(),
- offset: Self::offset_in(styles),
- extent: Self::extent_in(styles),
- evade: Self::evade_in(styles),
+ stroke: self.stroke(styles).unwrap_or_default(),
+ offset: self.offset(styles),
+ extent: self.extent(styles),
+ evade: self.evade(styles),
})))
}
}
@@ -96,11 +89,6 @@ impl Show for UnderlineNode {
/// Category: text
#[node(Show)]
pub struct OverlineNode {
- /// The content to add a line over.
- #[positional]
- #[required]
- pub body: Content,
-
/// How to stroke the line. The text color and thickness are read from the
/// font tables if `{auto}`.
///
@@ -112,10 +100,8 @@ pub struct OverlineNode {
/// [The Forest Theme],
/// )
/// ```
- #[settable]
#[resolve]
#[fold]
- #[default]
pub stroke: Smart<PartialStroke>,
/// Position of the line relative to the baseline, read from the font tables
@@ -126,9 +112,7 @@ pub struct OverlineNode {
/// The Tale Of A Faraway Line II
/// ]
/// ```
- #[settable]
#[resolve]
- #[default]
pub offset: Smart<Length>,
/// Amount that the line will be longer or shorter than its associated text.
@@ -138,9 +122,7 @@ pub struct OverlineNode {
/// #set underline(extent: 4pt)
/// #overline(underline[Typography Today])
/// ```
- #[settable]
#[resolve]
- #[default]
pub extent: Length,
/// Whether the line skips sections in which it would collide with the
@@ -155,19 +137,23 @@ pub struct OverlineNode {
/// [Temple],
/// )
/// ```
- #[settable]
#[default(true)]
pub evade: bool,
+
+ /// The content to add a line over.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl Show for OverlineNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().styled(TextNode::set_deco(Decoration {
line: DecoLine::Overline,
- stroke: Self::stroke_in(styles).unwrap_or_default(),
- offset: Self::offset_in(styles),
- extent: Self::extent_in(styles),
- evade: Self::evade_in(styles),
+ stroke: self.stroke(styles).unwrap_or_default(),
+ offset: self.offset(styles),
+ extent: self.extent(styles),
+ evade: self.evade(styles),
})))
}
}
@@ -183,11 +169,6 @@ impl Show for OverlineNode {
/// Category: text
#[node(Show)]
pub struct StrikeNode {
- /// The content to strike through.
- #[positional]
- #[required]
- pub body: Content,
-
/// How to stroke the line. The text color and thickness are read from the
/// font tables if `{auto}`.
///
@@ -198,10 +179,8 @@ pub struct StrikeNode {
/// This is #strike(stroke: 1.5pt + red)[very stricken through]. \
/// This is #strike(stroke: 10pt)[redacted].
/// ```
- #[settable]
#[resolve]
#[fold]
- #[default]
pub stroke: Smart<PartialStroke>,
/// Position of the line relative to the baseline, read from the font tables
@@ -214,9 +193,7 @@ pub struct StrikeNode {
/// This is #strike(offset: auto)[low-ish]. \
/// This is #strike(offset: -3.5pt)[on-top].
/// ```
- #[settable]
#[resolve]
- #[default]
pub offset: Smart<Length>,
/// Amount that the line will be longer or shorter than its associated text.
@@ -225,19 +202,22 @@ pub struct StrikeNode {
/// This #strike(extent: -2pt)[skips] parts of the word.
/// This #strike(extent: 2pt)[extends] beyond the word.
/// ```
- #[settable]
#[resolve]
- #[default]
pub extent: Length,
+
+ /// The content to strike through.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl Show for StrikeNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
Ok(self.body().styled(TextNode::set_deco(Decoration {
line: DecoLine::Strikethrough,
- stroke: Self::stroke_in(styles).unwrap_or_default(),
- offset: Self::offset_in(styles),
- extent: Self::extent_in(styles),
+ stroke: self.stroke(styles).unwrap_or_default(),
+ offset: self.offset(styles),
+ extent: self.extent(styles),
evade: false,
})))
}
diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs
index 91ac1748..64ab5bd2 100644
--- a/library/src/text/misc.rs
+++ b/library/src/text/misc.rs
@@ -51,7 +51,6 @@ pub struct LinebreakNode {
/// line breaks in this paragraph #jb
/// for an _interesting_ result. #jb
/// ```
- #[named]
#[default(false)]
pub justify: bool,
}
@@ -85,25 +84,24 @@ impl Behave for LinebreakNode {
/// Category: text
#[node(Show)]
pub struct StrongNode {
- /// The content to strongly emphasize.
- #[positional]
- #[required]
- pub body: Content,
-
/// The delta to apply on the font weight.
///
/// ```example
/// #set strong(delta: 0)
/// No *effect!*
/// ```
- #[settable]
#[default(300)]
pub delta: i64,
+
+ /// The content to strongly emphasize.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl Show for StrongNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
- Ok(self.body().styled(TextNode::set_delta(Delta(Self::delta_in(styles)))))
+ Ok(self.body().styled(TextNode::set_delta(Delta(self.delta(styles)))))
}
}
diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs
index 2318c426..83f4e2d7 100644
--- a/library/src/text/mod.rs
+++ b/library/src/text/mod.rs
@@ -46,12 +46,6 @@ use crate::prelude::*;
/// Category: text
#[node(Construct)]
pub struct TextNode {
- /// The text.
- #[positional]
- #[required]
- #[skip]
- pub text: EcoString,
-
/// A prioritized sequence of font families.
///
/// When processing text, Typst tries all specified font families in order
@@ -69,7 +63,6 @@ pub struct TextNode {
/// هذا عربي.
///
/// ```
- #[settable]
#[default(FontList(vec![FontFamily::new("Linux Libertine")]))]
pub font: FontList,
@@ -90,7 +83,6 @@ pub struct TextNode {
/// #set text(fallback: false)
/// هذا عربي
/// ```
- #[settable]
#[default(true)]
pub fallback: bool,
@@ -111,8 +103,6 @@ pub struct TextNode {
/// #text(font: "Linux Libertine", style: "italic")[Italic]
/// #text(font: "DejaVu Sans", style: "oblique")[Oblique]
/// ```
- #[settable]
- #[default(FontStyle::Normal)]
pub style: FontStyle,
/// The desired thickness of the font's glyphs. Accepts an integer between
@@ -132,8 +122,6 @@ pub struct TextNode {
/// #text(weight: 500)[Medium] \
/// #text(weight: "bold")[Bold]
/// ```
- #[settable]
- #[default(FontWeight::REGULAR)]
pub weight: FontWeight,
/// The desired width of the glyphs. Accepts a ratio between `{50%}` and
@@ -144,8 +132,6 @@ pub struct TextNode {
/// #text(stretch: 75%)[Condensed] \
/// #text(stretch: 100%)[Normal]
/// ```
- #[settable]
- #[default(FontStretch::NORMAL)]
pub stretch: FontStretch,
/// The size of the glyphs. This value forms the basis of the `em` unit:
@@ -158,8 +144,7 @@ pub struct TextNode {
/// #set text(size: 20pt)
/// very #text(1.5em)[big] text
/// ```
- #[settable]
- #[shorthand]
+ #[parse(args.named_or_find("size")?)]
#[fold]
#[default(Abs::pt(11.0))]
pub size: TextSize,
@@ -170,8 +155,7 @@ pub struct TextNode {
/// #set text(fill: red)
/// This text is red.
/// ```
- #[shorthand]
- #[settable]
+ #[parse(args.named_or_find("fill")?)]
#[default(Color::BLACK.into())]
pub fill: Paint,
@@ -181,9 +165,7 @@ pub struct TextNode {
/// #set text(tracking: 1.5pt)
/// Distant text.
/// ```
- #[settable]
#[resolve]
- #[default(Length::zero())]
pub tracking: Length,
/// The amount of space between words.
@@ -195,7 +177,6 @@ pub struct TextNode {
/// #set text(spacing: 200%)
/// Text with distant words.
/// ```
- #[settable]
#[resolve]
#[default(Rel::one())]
pub spacing: Rel<Length>,
@@ -206,9 +187,7 @@ pub struct TextNode {
/// A #text(baseline: 3pt)[lowered]
/// word.
/// ```
- #[settable]
#[resolve]
- #[default(Length::zero())]
pub baseline: Length,
/// Whether certain glyphs can hang over into the margin in justified text.
@@ -231,7 +210,6 @@ pub struct TextNode {
/// margin, making the paragraph's
/// edge less clear.
/// ```
- #[settable]
#[default(true)]
pub overhang: bool,
@@ -248,7 +226,6 @@ pub struct TextNode {
/// #set text(top-edge: "cap-height")
/// #rect(fill: aqua)[Typst]
/// ```
- #[settable]
#[default(TextEdge::Metric(VerticalFontMetric::CapHeight))]
pub top_edge: TextEdge,
@@ -265,7 +242,6 @@ pub struct TextNode {
/// #set text(bottom-edge: "descender")
/// #rect(fill: aqua)[Typst]
/// ```
- #[settable]
#[default(TextEdge::Metric(VerticalFontMetric::Baseline))]
pub bottom_edge: TextEdge,
@@ -286,15 +262,12 @@ pub struct TextNode {
/// = Einleitung
/// In diesem Dokument, ...
/// ```
- #[settable]
#[default(Lang::ENGLISH)]
pub lang: Lang,
/// An [ISO 3166-1 alpha-2 region code.](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
///
/// This lets the text processing pipeline make more informed choices.
- #[settable]
- #[default(None)]
pub region: Option<Region>,
/// The dominant direction for text and inline objects. Possible values are:
@@ -321,9 +294,7 @@ pub struct TextNode {
/// #set text(dir: rtl)
/// هذا عربي.
/// ```
- #[settable]
#[resolve]
- #[default(HorizontalDir(Smart::Auto))]
pub dir: HorizontalDir,
/// Whether to hyphenate text to improve line breaking. When `{auto}`, text
@@ -343,9 +314,7 @@ pub struct TextNode {
/// enabling hyphenation can
/// improve justification.
/// ```
- #[settable]
#[resolve]
- #[default(Hyphenate(Smart::Auto))]
pub hyphenate: Hyphenate,
/// Whether to apply kerning.
@@ -363,7 +332,6 @@ pub struct TextNode {
/// #set text(kerning: false)
/// Totally
/// ```
- #[settable]
#[default(true)]
pub kerning: bool,
@@ -380,7 +348,6 @@ pub struct TextNode {
/// #set text(alternates: true)
/// 0, a, g, ß
/// ```
- #[settable]
#[default(false)]
pub alternates: bool,
@@ -389,8 +356,6 @@ pub struct TextNode {
/// you need to consult your font to know which sets are available. When set
/// to an integer between `{1}` and `{20}`, enables the corresponding
/// OpenType font feature from `ss01`, ..., `ss20`.
- #[settable]
- #[default(None)]
pub stylistic_set: Option<StylisticSet>,
/// Whether standard ligatures are active.
@@ -407,19 +372,16 @@ pub struct TextNode {
/// #set text(ligatures: false)
/// A fine ligature.
/// ```
- #[settable]
#[default(true)]
pub ligatures: bool,
/// Whether ligatures that should be used sparingly are active. Setting this
/// to `{true}` enables the OpenType `dlig` font feature.
- #[settable]
#[default(false)]
pub discretionary_ligatures: bool,
/// Whether historical ligatures are active. Setting this to `{true}`
/// enables the OpenType `hlig` font feature.
- #[settable]
#[default(false)]
pub historical_ligatures: bool,
@@ -434,8 +396,6 @@ pub struct TextNode {
/// #set text(number-type: "old-style")
/// Number 9.
/// ```
- #[settable]
- #[default(Smart::Auto)]
pub number_type: Smart<NumberType>,
/// The width of numbers / figures. When set to `{auto}`, the default
@@ -451,8 +411,6 @@ pub struct TextNode {
/// A 12 B 34. \
/// A 56 B 78.
/// ```
- #[settable]
- #[default(Smart::Auto)]
pub number_width: Smart<NumberWidth>,
/// Whether to have a slash through the zero glyph. Setting this to `{true}`
@@ -461,7 +419,6 @@ pub struct TextNode {
/// ```example
/// 0, #text(slashed-zero: true)[0]
/// ```
- #[settable]
#[default(false)]
pub slashed_zero: bool,
@@ -472,7 +429,6 @@ pub struct TextNode {
/// 1/2 \
/// #text(fractions: true)[1/2]
/// ```
- #[settable]
#[default(false)]
pub fractions: bool,
@@ -488,43 +444,39 @@ pub struct TextNode {
/// #set text(features: ("frac",))
/// 1/2
/// ```
- #[settable]
#[fold]
- #[default(FontFeatures(vec![]))]
pub features: FontFeatures,
+ /// The text.
+ #[internal]
+ #[positional]
+ #[required]
+ pub text: EcoString,
+
/// A delta to apply on the font weight.
- #[settable]
+ #[internal]
#[fold]
- #[skip]
- #[default(0)]
pub delta: Delta,
/// Whether the font style should be inverted.
- #[settable]
+ #[internal]
#[fold]
- #[skip]
#[default(false)]
pub emph: Toggle,
+ /// Decorative lines.
+ #[internal]
+ #[fold]
+ pub deco: Decoration,
+
/// A case transformation that should be applied to the text.
- #[settable]
- #[skip]
- #[default(None)]
+ #[internal]
pub case: Option<Case>,
/// Whether small capital glyphs should be used. ("smcp")
- #[settable]
- #[skip]
+ #[internal]
#[default(false)]
pub smallcaps: bool,
-
- /// Decorative lines.
- #[settable]
- #[fold]
- #[skip]
- #[default(vec![])]
- pub deco: Decoration,
}
impl TextNode {
@@ -539,7 +491,9 @@ impl Construct for TextNode {
// The text constructor is special: It doesn't create a text node.
// Instead, it leaves the passed argument structurally unchanged, but
// styles all text in it.
- args.expect("body")
+ let styles = Self::set(args)?;
+ let body = args.expect::<Content>("body")?;
+ Ok(body.styled_with_map(styles))
}
}
@@ -651,7 +605,7 @@ cast_to_value! {
}
/// The direction of text and inline objects in their line.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct HorizontalDir(pub Smart<Dir>);
cast_from_value! {
@@ -680,7 +634,7 @@ impl Resolve for HorizontalDir {
}
/// Whether to hyphenate text.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Hyphenate(pub Smart<bool>);
cast_from_value! {
diff --git a/library/src/text/quotes.rs b/library/src/text/quotes.rs
index 1c602871..863cc3bd 100644
--- a/library/src/text/quotes.rs
+++ b/library/src/text/quotes.rs
@@ -27,7 +27,6 @@ use crate::prelude::*;
#[node]
pub struct SmartQuoteNode {
/// Whether this should be a double quote.
- #[named]
#[default(true)]
pub double: bool,
@@ -41,7 +40,6 @@ pub struct SmartQuoteNode {
///
/// These are "dumb" quotes.
/// ```
- #[settable]
#[default(true)]
pub enabled: bool,
}
diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs
index 8526a278..36a0fc78 100644
--- a/library/src/text/raw.rs
+++ b/library/src/text/raw.rs
@@ -87,7 +87,6 @@ pub struct RawNode {
/// rg "Hello World"
/// ```
/// ````
- #[named]
#[default(false)]
pub block: bool,
@@ -102,8 +101,6 @@ pub struct RawNode {
/// This is *Typst!*
/// ```
/// ````
- #[settable]
- #[default]
pub lang: Option<EcoString>,
}
@@ -114,7 +111,7 @@ impl Prepare for RawNode {
mut this: Content,
styles: StyleChain,
) -> SourceResult<Content> {
- this.push_field("lang", Self::lang_in(styles).clone());
+ this.push_field("lang", self.lang(styles).clone());
Ok(this)
}
}
@@ -122,7 +119,7 @@ impl Prepare for RawNode {
impl Show for RawNode {
fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult<Content> {
let text = self.text();
- let lang = Self::lang_in(styles).as_ref().map(|s| s.to_lowercase());
+ let lang = self.lang(styles).as_ref().map(|s| s.to_lowercase());
let foreground = THEME
.settings
.foreground
@@ -170,7 +167,7 @@ impl Show for RawNode {
TextNode::packed(text)
};
- if self.block() {
+ if self.block(styles) {
realized = BlockNode::new().with_body(Some(realized)).pack();
}
@@ -179,7 +176,7 @@ impl Show for RawNode {
}
impl Finalize for RawNode {
- fn finalize(&self, realized: Content) -> Content {
+ fn finalize(&self, realized: Content, _: StyleChain) -> Content {
let mut map = StyleMap::new();
map.set(TextNode::set_overhang(false));
map.set(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false))));
diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs
index 98718365..ccdb0197 100644
--- a/library/src/text/shift.rs
+++ b/library/src/text/shift.rs
@@ -16,11 +16,6 @@ use crate::prelude::*;
/// Category: text
#[node(Show)]
pub struct SubNode {
- /// The text to display in subscript.
- #[positional]
- #[required]
- pub body: Content,
-
/// Whether to prefer the dedicated subscript characters of the font.
///
/// If this is enabled, Typst first tries to transform the text to subscript
@@ -31,23 +26,25 @@ pub struct SubNode {
/// N#sub(typographic: true)[1]
/// N#sub(typographic: false)[1]
/// ```
- #[settable]
#[default(true)]
pub typographic: bool,
/// The baseline shift for synthetic subscripts. Does not apply if
/// `typographic` is true and the font has subscript codepoints for the
/// given `body`.
- #[settable]
#[default(Em::new(0.2).into())]
pub baseline: Length,
/// The font size for synthetic subscripts. Does not apply if
/// `typographic` is true and the font has subscript codepoints for the
/// given `body`.
- #[settable]
#[default(TextSize(Em::new(0.6).into()))]
pub size: TextSize,
+
+ /// The text to display in subscript.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl Show for SubNode {
@@ -59,7 +56,7 @@ impl Show for SubNode {
) -> SourceResult<Content> {
let body = self.body();
let mut transformed = None;
- if Self::typographic_in(styles) {
+ if self.typographic(styles) {
if let Some(text) = search_text(&body, true) {
if is_shapable(vt, &text, styles) {
transformed = Some(TextNode::packed(text));
@@ -68,8 +65,8 @@ impl Show for SubNode {
};
Ok(transformed.unwrap_or_else(|| {
- body.styled(TextNode::set_baseline(Self::baseline_in(styles)))
- .styled(TextNode::set_size(Self::size_in(styles)))
+ body.styled(TextNode::set_baseline(self.baseline(styles)))
+ .styled(TextNode::set_size(self.size(styles)))
}))
}
}
@@ -87,11 +84,6 @@ impl Show for SubNode {
/// Category: text
#[node(Show)]
pub struct SuperNode {
- /// The text to display in superscript.
- #[positional]
- #[required]
- pub body: Content,
-
/// Whether to prefer the dedicated superscript characters of the font.
///
/// If this is enabled, Typst first tries to transform the text to
@@ -102,23 +94,25 @@ pub struct SuperNode {
/// N#super(typographic: true)[1]
/// N#super(typographic: false)[1]
/// ```
- #[settable]
#[default(true)]
pub typographic: bool,
/// The baseline shift for synthetic superscripts. Does not apply if
/// `typographic` is true and the font has superscript codepoints for the
/// given `body`.
- #[settable]
#[default(Em::new(-0.5).into())]
pub baseline: Length,
/// The font size for synthetic superscripts. Does not apply if
/// `typographic` is true and the font has superscript codepoints for the
/// given `body`.
- #[settable]
#[default(TextSize(Em::new(0.6).into()))]
pub size: TextSize,
+
+ /// The text to display in superscript.
+ #[positional]
+ #[required]
+ pub body: Content,
}
impl Show for SuperNode {
@@ -130,7 +124,7 @@ impl Show for SuperNode {
) -> SourceResult<Content> {
let body = self.body();
let mut transformed = None;
- if Self::typographic_in(styles) {
+ if self.typographic(styles) {
if let Some(text) = search_text(&body, false) {
if is_shapable(vt, &text, styles) {
transformed = Some(TextNode::packed(text));
@@ -139,8 +133,8 @@ impl Show for SuperNode {
};
Ok(transformed.unwrap_or_else(|| {
- body.styled(TextNode::set_baseline(Self::baseline_in(styles)))
- .styled(TextNode::set_size(Self::size_in(styles)))
+ body.styled(TextNode::set_baseline(self.baseline(styles)))
+ .styled(TextNode::set_size(self.size(styles)))
}))
}
}
diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs
index 1bc864e5..1fdb418b 100644
--- a/library/src/visualize/image.rs
+++ b/library/src/visualize/image.rs
@@ -20,41 +20,31 @@ use crate::prelude::*;
///
/// Display: Image
/// Category: visualize
-#[node(Construct, Layout)]
+#[node(Layout)]
pub struct ImageNode {
/// Path to an image file.
#[positional]
#[required]
+ #[parse(
+ let Spanned { v: path, span } =
+ args.expect::<Spanned<EcoString>>("path to image file")?;
+ let path: EcoString = vm.locate(&path).at(span)?.to_string_lossy().into();
+ let _ = load(vm.world(), &path).at(span)?;
+ path
+ )]
pub path: EcoString,
/// The width of the image.
- #[named]
- #[default]
pub width: Smart<Rel<Length>>,
/// The height of the image.
- #[named]
- #[default]
pub height: Smart<Rel<Length>>,
/// How the image should adjust itself to a given area.
- #[settable]
#[default(ImageFit::Cover)]
pub fit: ImageFit,
}
-impl Construct for ImageNode {
- fn construct(vm: &Vm, args: &mut Args) -> SourceResult<Content> {
- let Spanned { v: path, span } =
- args.expect::<Spanned<EcoString>>("path to image file")?;
- let path: EcoString = vm.locate(&path).at(span)?.to_string_lossy().into();
- let _ = load(vm.world(), &path).at(span)?;
- let width = args.named::<Smart<Rel<Length>>>("width")?.unwrap_or_default();
- let height = args.named::<Smart<Rel<Length>>>("height")?.unwrap_or_default();
- Ok(ImageNode::new(path).with_width(width).with_height(height).pack())
- }
-}
-
impl Layout for ImageNode {
fn layout(
&self,
@@ -63,7 +53,7 @@ impl Layout for ImageNode {
regions: Regions,
) -> SourceResult<Fragment> {
let image = load(vt.world(), &self.path()).unwrap();
- let sizing = Axes::new(self.width(), self.height());
+ let sizing = Axes::new(self.width(styles), self.height(styles));
let region = sizing
.zip(regions.base())
.map(|(s, r)| s.map(|v| v.resolve(styles).relative_to(r)))
@@ -90,7 +80,7 @@ impl Layout for ImageNode {
};
// Compute the actual size of the fitted image.
- let fit = Self::fit_in(styles);
+ let fit = self.fit(styles);
let fitted = match fit {
ImageFit::Cover | ImageFit::Contain => {
if wide == (fit == ImageFit::Contain) {
diff --git a/library/src/visualize/line.rs b/library/src/visualize/line.rs
index 6beb7beb..015abbb0 100644
--- a/library/src/visualize/line.rs
+++ b/library/src/visualize/line.rs
@@ -9,34 +9,28 @@ use crate::prelude::*;
/// #line(end: (50%, 50%))
/// ```
///
-/// ## Parameters
-/// - end: `Axes<Rel<Length>>` (named)
-/// The end point of the line.
-/// Must be an array of exactly two relative lengths.
-///
-/// - length: `Rel<Length>` (named)
-/// The line's length. Mutually exclusive with `end`.
-///
-/// - angle: `Angle` (named)
-/// The angle at which the line points away from the origin. Mutually
-/// exclusive with `end`.
-///
/// Display: Line
/// Category: visualize
-#[node(Construct, Layout)]
+#[node(Layout)]
pub struct LineNode {
/// The start point of the line.
///
/// Must be an array of exactly two relative lengths.
- #[named]
- #[default]
+ #[resolve]
pub start: Axes<Rel<Length>>,
/// The offset from `start` where the line ends.
- #[named]
- #[default]
- #[skip]
- pub delta: Axes<Rel<Length>>,
+ #[resolve]
+ pub end: Smart<Axes<Rel<Length>>>,
+
+ /// The line's length. Mutually exclusive with `end`.
+ #[resolve]
+ #[default(Abs::pt(30.0).into())]
+ pub length: Rel<Length>,
+
+ /// The angle at which the line points away from the origin. Mutually
+ /// exclusive with `end`.
+ pub angle: Angle,
/// How to stroke the line. This can be:
///
@@ -50,33 +44,11 @@ pub struct LineNode {
/// ```example
/// #line(length: 100%, stroke: 2pt + red)
/// ```
- #[settable]
#[resolve]
#[fold]
- #[default]
pub stroke: PartialStroke,
}
-impl Construct for LineNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let start = args.named("start")?.unwrap_or_default();
- let delta = match args.named::<Axes<Rel<Length>>>("end")? {
- Some(end) => end.zip(start).map(|(to, from)| to - from),
- None => {
- let length =
- args.named::<Rel<Length>>("length")?.unwrap_or(Abs::pt(30.0).into());
-
- let angle = args.named::<Angle>("angle")?.unwrap_or_default();
- let x = angle.cos() * length;
- let y = angle.sin() * length;
-
- Axes::new(x, y)
- }
- };
- Ok(Self::new().with_start(start).with_delta(delta).pack())
- }
-}
-
impl Layout for LineNode {
fn layout(
&self,
@@ -84,27 +56,27 @@ impl Layout for LineNode {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let stroke = Self::stroke_in(styles).unwrap_or_default();
-
- let origin = self
- .start()
- .resolve(styles)
- .zip(regions.base())
- .map(|(l, b)| l.relative_to(b));
+ let resolve = |axes: Axes<Rel<Abs>>| {
+ axes.zip(regions.base()).map(|(l, b)| l.relative_to(b))
+ };
- let delta = self
- .delta()
- .resolve(styles)
- .zip(regions.base())
- .map(|(l, b)| l.relative_to(b));
+ let start = resolve(self.start(styles));
+ let delta =
+ self.end(styles).map(|end| resolve(end) - start).unwrap_or_else(|| {
+ let length = self.length(styles);
+ let angle = self.angle(styles);
+ let x = angle.cos() * length;
+ let y = angle.sin() * length;
+ resolve(Axes::new(x, y))
+ });
- let size = origin.max(origin + delta).max(Size::zero());
+ let stroke = self.stroke(styles).unwrap_or_default();
+ let size = start.max(start + delta).max(Size::zero());
let target = regions.expand.select(regions.size, size);
let mut frame = Frame::new(target);
let shape = Geometry::Line(delta.to_point()).stroked(stroke);
- frame.push(origin.to_point(), Element::Shape(shape));
-
+ frame.push(start.to_point(), Element::Shape(shape));
Ok(Fragment::frame(frame))
}
}
diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs
index fc1d462e..ab953846 100644
--- a/library/src/visualize/shape.rs
+++ b/library/src/visualize/shape.rs
@@ -20,22 +20,10 @@ use crate::prelude::*;
/// Category: visualize
#[node(Layout)]
pub struct RectNode {
- /// The content to place into the rectangle.
- ///
- /// When this is omitted, the rectangle takes on a default size of at most
- /// `{45pt}` by `{30pt}`.
- #[positional]
- #[default]
- pub body: Option<Content>,
-
/// The rectangle's width, relative to its parent container.
- #[named]
- #[default]
pub width: Smart<Rel<Length>>,
/// The rectangle's height, relative to its parent container.
- #[named]
- #[default]
pub height: Smart<Rel<Length>>,
/// How to fill the rectangle.
@@ -46,8 +34,6 @@ pub struct RectNode {
/// ```example
/// #rect(fill: blue)
/// ```
- #[settable]
- #[default]
pub fill: Option<Paint>,
/// How to stroke the rectangle. This can be:
@@ -82,10 +68,8 @@ pub struct RectNode {
/// rect(stroke: 2pt + red),
/// )
/// ```
- #[settable]
#[resolve]
#[fold]
- #[default]
pub stroke: Smart<Sides<Option<Option<PartialStroke>>>>,
/// How much to round the rectangle's corners, relative to the minimum of
@@ -122,10 +106,8 @@ pub struct RectNode {
/// ),
/// )
/// ```
- #[settable]
#[resolve]
#[fold]
- #[default]
pub radius: Corners<Option<Rel<Length>>>,
/// How much to pad the rectangle's content.
@@ -138,7 +120,6 @@ pub struct RectNode {
/// ```example
/// #rect(inset: 0pt)[Tight])
/// ```
- #[settable]
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
@@ -146,11 +127,16 @@ pub struct RectNode {
/// How much to expand the rectangle's size without affecting the layout.
/// See the [box's documentation]($func/box.outset) for more details.
- #[settable]
#[resolve]
#[fold]
- #[default]
pub outset: Sides<Option<Rel<Length>>>,
+
+ /// The content to place into the rectangle.
+ ///
+ /// When this is omitted, the rectangle takes on a default size of at most
+ /// `{45pt}` by `{30pt}`.
+ #[positional]
+ pub body: Option<Content>,
}
impl Layout for RectNode {
@@ -165,13 +151,13 @@ impl Layout for RectNode {
styles,
regions,
ShapeKind::Rect,
- &self.body(),
- Axes::new(self.width(), self.height()),
- Self::fill_in(styles),
- Self::stroke_in(styles),
- Self::inset_in(styles),
- Self::outset_in(styles),
- Self::radius_in(styles),
+ &self.body(styles),
+ Axes::new(self.width(styles), self.height(styles)),
+ self.fill(styles),
+ self.stroke(styles),
+ self.inset(styles),
+ self.outset(styles),
+ self.radius(styles),
)
}
}
@@ -191,66 +177,57 @@ impl Layout for RectNode {
/// ```
///
/// ## Parameters
-/// - size: `Smart<Length>` (named)
+/// - size: `Smart<Length>` (named, settable)
/// The square's side length. This is mutually exclusive with `width` and
/// `height`.
///
/// Display: Square
/// Category: visualize
-#[node(Construct, Layout)]
+#[node(Layout)]
pub struct SquareNode {
- /// The content to place into the square. The square expands to fit this
- /// content, keeping the 1-1 aspect ratio.
- ///
- /// When this is omitted, the square takes on a default size of at most
- /// `{30pt}`.
- #[positional]
- #[default]
- pub body: Option<Content>,
-
/// The square's width. This is mutually exclusive with `size` and `height`.
///
/// In contrast to `size`, this can be relative to the parent container's
/// width.
- #[named]
- #[default]
+ #[parse(
+ let size = args.named::<Smart<Length>>("size")?.map(|s| s.map(Rel::from));
+ match size {
+ None => args.named("width")?,
+ size => size,
+ }
+ )]
pub width: Smart<Rel<Length>>,
/// The square's height. This is mutually exclusive with `size` and `width`.
///
/// In contrast to `size`, this can be relative to the parent container's
/// height.
- #[named]
- #[default]
+ #[parse(match size {
+ None => args.named("height")?,
+ size => size,
+ })]
pub height: Smart<Rel<Length>>,
/// How to fill the square. See the
/// [rectangle's documentation]($func/rect.fill) for more details.
- #[settable]
- #[default]
pub fill: Option<Paint>,
/// How to stroke the square. See the [rectangle's
/// documentation]($func/rect.stroke) for more details.
- #[settable]
#[resolve]
#[fold]
- #[default]
pub stroke: Smart<Sides<Option<Option<PartialStroke>>>>,
/// How much to round the square'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 square's content. See the [rectangle's
/// documentation]($func/rect.inset) for more details.
///
/// The default value is `{5pt}`.
- #[settable]
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
@@ -258,33 +235,17 @@ pub struct SquareNode {
/// How much to expand the square'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>>>,
-}
-impl Construct for SquareNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let size = args.named::<Smart<Length>>("size")?.map(|s| s.map(Rel::from));
- let width = match size {
- None => args.named("width")?,
- size => size,
- }
- .unwrap_or_default();
- let height = match size {
- None => args.named("height")?,
- size => size,
- }
- .unwrap_or_default();
- let body = args.eat::<Content>()?;
- Ok(Self::new()
- .with_body(body)
- .with_width(width)
- .with_height(height)
- .pack())
- }
+ /// The content to place into the square. The square expands to fit this
+ /// content, keeping the 1-1 aspect ratio.
+ ///
+ /// When this is omitted, the square takes on a default size of at most
+ /// `{30pt}`.
+ #[positional]
+ pub body: Option<Content>,
}
impl Layout for SquareNode {
@@ -299,13 +260,13 @@ impl Layout for SquareNode {
styles,
regions,
ShapeKind::Square,
- &self.body(),
- Axes::new(self.width(), self.height()),
- Self::fill_in(styles),
- Self::stroke_in(styles),
- Self::inset_in(styles),
- Self::outset_in(styles),
- Self::radius_in(styles),
+ &self.body(styles),
+ Axes::new(self.width(styles), self.height(styles)),
+ self.fill(styles),
+ self.stroke(styles),
+ self.inset(styles),
+ self.outset(styles),
+ self.radius(styles),
)
}
}
@@ -329,43 +290,26 @@ impl Layout for SquareNode {
/// Category: visualize
#[node(Layout)]
pub struct EllipseNode {
- /// The content to place into the ellipse.
- ///
- /// When this is omitted, the ellipse takes on a default size of at most
- /// `{45pt}` by `{30pt}`.
- #[positional]
- #[default]
- pub body: Option<Content>,
-
/// The ellipse's width, relative to its parent container.
- #[named]
- #[default]
pub width: Smart<Rel<Length>>,
/// The ellipse's height, relative to its parent container.
- #[named]
- #[default]
pub height: Smart<Rel<Length>>,
/// How to fill the ellipse. See the
/// [rectangle's documentation]($func/rect.fill) for more details.
- #[settable]
- #[default]
pub fill: Option<Paint>,
/// How to stroke the ellipse. See the [rectangle's
/// documentation]($func/rect.stroke) for more details.
- #[settable]
#[resolve]
#[fold]
- #[default]
pub stroke: Smart<Option<PartialStroke>>,
/// How much to pad the ellipse's content. See the [rectangle's
/// documentation]($func/rect.inset) for more details.
///
/// The default value is `{5pt}`.
- #[settable]
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
@@ -373,11 +317,16 @@ pub struct EllipseNode {
/// How much to expand the ellipse'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 content to place into the ellipse.
+ ///
+ /// When this is omitted, the ellipse takes on a default size of at most
+ /// `{45pt}` by `{30pt}`.
+ #[positional]
+ pub body: Option<Content>,
}
impl Layout for EllipseNode {
@@ -392,12 +341,12 @@ impl Layout for EllipseNode {
styles,
regions,
ShapeKind::Ellipse,
- &self.body(),
- Axes::new(self.width(), self.height()),
- Self::fill_in(styles),
- Self::stroke_in(styles).map(Sides::splat),
- Self::inset_in(styles),
- Self::outset_in(styles),
+ &self.body(styles),
+ Axes::new(self.width(styles), self.height(styles)),
+ self.fill(styles),
+ self.stroke(styles).map(Sides::splat),
+ self.inset(styles),
+ self.outset(styles),
Corners::splat(Rel::zero()),
)
}
@@ -419,27 +368,28 @@ impl Layout for EllipseNode {
/// ```
///
/// ## Parameters
-/// - radius: `Length` (named)
+/// - radius: `Length` (named, settable)
/// The circle's radius. This is mutually exclusive with `width` and
/// `height`.
///
/// Display: Circle
/// Category: visualize
-#[node(Construct, Layout)]
+#[node(Layout)]
pub struct CircleNode {
- /// The content to place into the circle. The circle expands to fit this
- /// content, keeping the 1-1 aspect ratio.
- #[positional]
- #[default]
- pub body: Option<Content>,
-
/// The circle's width. This is mutually exclusive with `radius` and
/// `height`.
///
/// In contrast to `size`, this can be relative to the parent container's
/// width.
- #[named]
- #[default]
+ #[parse(
+ let size = args
+ .named::<Smart<Length>>("radius")?
+ .map(|s| s.map(|r| 2.0 * Rel::from(r)));
+ match size {
+ None => args.named("width")?,
+ size => size,
+ }
+ )]
pub width: Smart<Rel<Length>>,
/// The circle's height.This is mutually exclusive with `radius` and
@@ -447,19 +397,18 @@ pub struct CircleNode {
///
/// In contrast to `size`, this can be relative to the parent container's
/// height.
- #[named]
- #[default]
+ #[parse(match size {
+ None => args.named("height")?,
+ size => size,
+ })]
pub height: Smart<Rel<Length>>,
/// How to fill the circle. See the
/// [rectangle's documentation]($func/rect.fill) for more details.
- #[settable]
- #[default]
pub fill: Option<Paint>,
/// How to stroke the circle. See the [rectangle's
/// documentation]($func/rect.stroke) for more details.
- #[settable]
#[resolve]
#[fold]
#[default(Smart::Auto)]
@@ -469,7 +418,6 @@ pub struct CircleNode {
/// documentation]($func/rect.inset) for more details.
///
/// The default value is `{5pt}`.
- #[settable]
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
@@ -477,35 +425,14 @@ pub struct CircleNode {
/// How much to expand the circle'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>>>,
-}
-impl Construct for CircleNode {
- fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
- let size = args
- .named::<Smart<Length>>("radius")?
- .map(|s| s.map(|r| 2.0 * Rel::from(r)));
- let width = match size {
- None => args.named("width")?,
- size => size,
- }
- .unwrap_or_default();
- let height = match size {
- None => args.named("height")?,
- size => size,
- }
- .unwrap_or_default();
- let body = args.eat::<Content>()?;
- Ok(Self::new()
- .with_body(body)
- .with_width(width)
- .with_height(height)
- .pack())
- }
+ /// The content to place into the circle. The circle expands to fit this
+ /// content, keeping the 1-1 aspect ratio.
+ #[positional]
+ pub body: Option<Content>,
}
impl Layout for CircleNode {
@@ -520,12 +447,12 @@ impl Layout for CircleNode {
styles,
regions,
ShapeKind::Circle,
- &self.body(),
- Axes::new(self.width(), self.height()),
- Self::fill_in(styles),
- Self::stroke_in(styles).map(Sides::splat),
- Self::inset_in(styles),
- Self::outset_in(styles),
+ &self.body(styles),
+ Axes::new(self.width(styles), self.height(styles)),
+ self.fill(styles),
+ self.stroke(styles).map(Sides::splat),
+ self.inset(styles),
+ self.outset(styles),
Corners::splat(Rel::zero()),
)
}