diff --git a/docs/src/lib.rs b/docs/src/lib.rs index 7ddc1c70c..563d565c2 100644 --- a/docs/src/lib.rs +++ b/docs/src/lib.rs @@ -39,10 +39,12 @@ static FONTS: Lazy<(Prehashed, Vec)> = Lazy::new(|| { static LIBRARY: Lazy> = Lazy::new(|| { let mut lib = typst_library::build(); - lib.styles.set(PageNode::WIDTH, Smart::Custom(Abs::pt(240.0).into())); - lib.styles.set(PageNode::HEIGHT, Smart::Auto); lib.styles - .set(PageNode::MARGIN, Sides::splat(Some(Smart::Custom(Abs::pt(15.0).into())))); + .set(PageNode::set_width(Smart::Custom(Abs::pt(240.0).into()))); + lib.styles.set(PageNode::set_height(Smart::Auto)); + lib.styles.set(PageNode::set_margin(Sides::splat(Some(Smart::Custom( + Abs::pt(15.0).into(), + ))))); typst::eval::set_lang_items(lib.items.clone()); Prehashed::new(lib) }); diff --git a/library/src/layout/align.rs b/library/src/layout/align.rs index e0df04361..fdd4d0ea9 100644 --- a/library/src/layout/align.rs +++ b/library/src/layout/align.rs @@ -16,8 +16,7 @@ use crate::prelude::*; /// Category: layout #[node(Show)] #[set({ - let aligns: Axes> = args.find()?.unwrap_or_default(); - styles.set(Self::ALIGNMENT, aligns); + styles.set(Self::set_alignment(args.find()?.unwrap_or_default())); })] pub struct AlignNode { /// The alignment along both axes. diff --git a/library/src/layout/columns.rs b/library/src/layout/columns.rs index 94c04509a..27339628f 100644 --- a/library/src/layout/columns.rs +++ b/library/src/layout/columns.rs @@ -68,7 +68,7 @@ impl Layout for ColumnsNode { // Determine the width of the gutter and each column. let columns = self.count().get(); - let gutter = styles.get(Self::GUTTER).relative_to(regions.base().x); + let gutter = Self::gutter_in(styles).relative_to(regions.base().x); let width = (regions.size.x - gutter * (columns - 1) as f64) / columns as f64; let backlog: Vec<_> = std::iter::once(®ions.size.y) @@ -90,7 +90,7 @@ impl Layout for ColumnsNode { let mut frames = body.layout(vt, styles, pod)?.into_iter(); let mut finished = vec![]; - let dir = styles.get(TextNode::DIR); + let dir = TextNode::dir_in(styles); let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize; // Stitch together the columns for each region. diff --git a/library/src/layout/container.rs b/library/src/layout/container.rs index 8f5d69cc1..bdce1922f 100644 --- a/library/src/layout/container.rs +++ b/library/src/layout/container.rs @@ -134,7 +134,7 @@ impl Layout for BoxNode { // Apply inset. let mut body = self.body().unwrap_or_default(); - let inset = styles.get(Self::INSET); + let inset = Self::inset_in(styles); if inset.iter().any(|v| !v.is_zero()) { body = body.padded(inset.map(|side| side.map(Length::from))); } @@ -145,21 +145,20 @@ impl Layout for BoxNode { let mut frame = body.layout(vt, styles, pod)?.into_frame(); // Apply baseline shift. - let shift = styles.get(Self::BASELINE).relative_to(frame.height()); + let shift = Self::baseline_in(styles).relative_to(frame.height()); if !shift.is_zero() { frame.set_baseline(frame.baseline() - shift); } // Prepare fill and stroke. - let fill = styles.get(Self::FILL); - let stroke = styles - .get(Self::STROKE) - .map(|s| s.map(PartialStroke::unwrap_or_default)); + let fill = Self::fill_in(styles); + let stroke = + Self::stroke_in(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 = styles.get(Self::OUTSET); - let radius = styles.get(Self::RADIUS); + let outset = Self::outset_in(styles); + let radius = Self::radius_in(styles); frame.fill_and_stroke(fill, stroke, outset, radius); } @@ -220,16 +219,16 @@ impl Layout for BoxNode { #[set({ let spacing = args.named("spacing")?; styles.set_opt( - Self::ABOVE, args.named("above")? .map(VNode::block_around) - .or_else(|| spacing.map(VNode::block_spacing)), + .or_else(|| spacing.map(VNode::block_spacing)) + .map(Self::set_above), ); styles.set_opt( - Self::BELOW, args.named("below")? .map(VNode::block_around) - .or_else(|| spacing.map(VNode::block_spacing)), + .or_else(|| spacing.map(VNode::block_spacing)) + .map(Self::set_below), ); })] pub struct BlockNode { @@ -361,7 +360,7 @@ impl Layout for BlockNode { ) -> SourceResult { // Apply inset. let mut body = self.body().unwrap_or_default(); - let inset = styles.get(Self::INSET); + let inset = Self::inset_in(styles); if inset.iter().any(|v| !v.is_zero()) { body = body.clone().padded(inset.map(|side| side.map(Length::from))); } @@ -376,7 +375,7 @@ impl Layout for BlockNode { .unwrap_or(regions.base()); // Layout the child. - let mut frames = if styles.get(Self::BREAKABLE) { + let mut frames = if Self::breakable_in(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)); @@ -414,10 +413,9 @@ impl Layout for BlockNode { }; // Prepare fill and stroke. - let fill = styles.get(Self::FILL); - let stroke = styles - .get(Self::STROKE) - .map(|s| s.map(PartialStroke::unwrap_or_default)); + let fill = Self::fill_in(styles); + let stroke = + Self::stroke_in(styles).map(|s| s.map(PartialStroke::unwrap_or_default)); // Add fill and/or stroke. if fill.is_some() || stroke.iter().any(Option::is_some) { @@ -426,8 +424,8 @@ impl Layout for BlockNode { skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty()); } - let outset = styles.get(Self::OUTSET); - let radius = styles.get(Self::RADIUS); + let outset = Self::outset_in(styles); + let radius = Self::radius_in(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 853a7a672..4876b6167 100644 --- a/library/src/layout/enum.rs +++ b/library/src/layout/enum.rs @@ -187,21 +187,20 @@ impl Layout for EnumNode { styles: StyleChain, regions: Regions, ) -> SourceResult { - let numbering = styles.get(Self::NUMBERING); - let indent = styles.get(Self::INDENT); - let body_indent = styles.get(Self::BODY_INDENT); + 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() { - styles.get(ParNode::LEADING).into() + ParNode::leading_in(styles).into() } else { - styles - .get(Self::SPACING) - .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount()) + Self::spacing_in(styles) + .unwrap_or_else(|| BlockNode::below_in(styles).amount()) }; let mut cells = vec![]; let mut number = NonZeroUsize::new(1).unwrap(); - let mut parents = styles.get(Self::PARENTS); - let full = styles.get(Self::FULL); + let mut parents = Self::parents_in(styles); + let full = Self::full_in(styles); for item in self.children() { number = item.number().unwrap_or(number); @@ -223,7 +222,7 @@ impl Layout for EnumNode { cells.push(Content::empty()); cells.push(resolved); cells.push(Content::empty()); - cells.push(item.body().styled(Self::PARENTS, Parent(number))); + cells.push(item.body().styled(Self::set_parents(Parent(number)))); number = number.saturating_add(1); } diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs index 8554bd981..5d679570d 100644 --- a/library/src/layout/flow.rs +++ b/library/src/layout/flow.rs @@ -134,8 +134,8 @@ impl<'a> FlowLayouter<'a> { par: &ParNode, styles: StyleChain, ) -> SourceResult<()> { - let aligns = styles.get(AlignNode::ALIGNMENT).resolve(styles); - let leading = styles.get(ParNode::LEADING); + let aligns = AlignNode::alignment_in(styles).resolve(styles); + let leading = ParNode::leading_in(styles); let consecutive = self.last_was_par; let frames = par .layout(vt, styles, consecutive, self.regions.base(), self.regions.expand.x)? @@ -180,8 +180,8 @@ impl<'a> FlowLayouter<'a> { content: &Content, styles: StyleChain, ) -> SourceResult<()> { - let aligns = styles.get(AlignNode::ALIGNMENT).resolve(styles); - let sticky = styles.get(BlockNode::STICKY); + let aligns = AlignNode::alignment_in(styles).resolve(styles); + let sticky = BlockNode::sticky_in(styles); let pod = Regions::one(self.regions.base(), Axes::splat(false)); let layoutable = content.with::().unwrap(); let frame = layoutable.layout(vt, styles, pod)?.into_frame(); @@ -208,10 +208,10 @@ impl<'a> FlowLayouter<'a> { } // How to align the block. - let aligns = styles.get(AlignNode::ALIGNMENT).resolve(styles); + let aligns = AlignNode::alignment_in(styles).resolve(styles); // Layout the block itself. - let sticky = styles.get(BlockNode::STICKY); + let sticky = BlockNode::sticky_in(styles); let fragment = block.layout(vt, styles, self.regions)?; for (i, frame) in fragment.into_iter().enumerate() { if i > 0 { diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs index b6465e1c3..5d465f865 100644 --- a/library/src/layout/grid.rs +++ b/library/src/layout/grid.rs @@ -262,7 +262,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { } // Reverse for RTL. - let is_rtl = styles.get(TextNode::DIR) == Dir::RTL; + let is_rtl = TextNode::dir_in(styles) == Dir::RTL; if is_rtl { cols.reverse(); } diff --git a/library/src/layout/hide.rs b/library/src/layout/hide.rs index 5ba7dea40..43d9a2a8d 100644 --- a/library/src/layout/hide.rs +++ b/library/src/layout/hide.rs @@ -25,6 +25,6 @@ pub struct HideNode { impl Show for HideNode { fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult { - Ok(self.body().styled(MetaNode::DATA, vec![Meta::Hidden])) + Ok(self.body().styled(MetaNode::set_data(vec![Meta::Hidden]))) } } diff --git a/library/src/layout/list.rs b/library/src/layout/list.rs index 5ba6b9b0f..e6e42263f 100644 --- a/library/src/layout/list.rs +++ b/library/src/layout/list.rs @@ -127,25 +127,24 @@ impl Layout for ListNode { styles: StyleChain, regions: Regions, ) -> SourceResult { - let indent = styles.get(Self::INDENT); - let body_indent = styles.get(Self::BODY_INDENT); + let indent = Self::indent_in(styles); + let body_indent = Self::body_indent_in(styles); let gutter = if self.tight() { - styles.get(ParNode::LEADING).into() + ParNode::leading_in(styles).into() } else { - styles - .get(Self::SPACING) - .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount()) + Self::spacing_in(styles) + .unwrap_or_else(|| BlockNode::below_in(styles).amount()) }; - let depth = styles.get(Self::DEPTH); - let marker = styles.get(Self::MARKER).resolve(vt.world(), depth)?; + let depth = Self::depth_in(styles); + let marker = Self::marker_in(styles).resolve(vt.world(), depth)?; let mut cells = vec![]; for item in self.children() { cells.push(Content::empty()); cells.push(marker.clone()); cells.push(Content::empty()); - cells.push(item.body().styled(Self::DEPTH, Depth)); + cells.push(item.body().styled(Self::set_depth(Depth))); } let layouter = GridLayouter::new( diff --git a/library/src/layout/mod.rs b/library/src/layout/mod.rs index 96d16ec84..6d57912f8 100644 --- a/library/src/layout/mod.rs +++ b/library/src/layout/mod.rs @@ -450,13 +450,13 @@ impl<'a> FlowBuilder<'a> { }; if !last_was_parbreak && is_tight_list { - let leading = styles.get(ParNode::LEADING); + let leading = ParNode::leading_in(styles); let spacing = VNode::list_attach(leading.into()); self.0.push(spacing.pack(), styles); } - let above = styles.get(BlockNode::ABOVE); - let below = styles.get(BlockNode::BELOW); + let above = BlockNode::above_in(styles); + let below = BlockNode::below_in(styles); self.0.push(above.clone().pack(), styles); self.0.push(content.clone(), styles); self.0.push(below.clone().pack(), styles); diff --git a/library/src/layout/page.rs b/library/src/layout/page.rs index 3b5579818..0c3de30cf 100644 --- a/library/src/layout/page.rs +++ b/library/src/layout/page.rs @@ -30,8 +30,8 @@ use crate::prelude::*; #[node] #[set({ if let Some(paper) = args.named_or_find::("paper")? { - styles.set(Self::WIDTH, Smart::Custom(paper.width().into())); - styles.set(Self::HEIGHT, Smart::Custom(paper.height().into())); + styles.set(Self::set_width(Smart::Custom(paper.width().into()))); + styles.set(Self::set_height(Smart::Custom(paper.height().into()))); } })] pub struct PageNode { @@ -260,10 +260,10 @@ impl PageNode { ) -> SourceResult { // When one of the lengths is infinite the page fits its content along // that axis. - let width = styles.get(Self::WIDTH).unwrap_or(Abs::inf()); - let height = styles.get(Self::HEIGHT).unwrap_or(Abs::inf()); + let width = Self::width_in(styles).unwrap_or(Abs::inf()); + let height = Self::height_in(styles).unwrap_or(Abs::inf()); let mut size = Size::new(width, height); - if styles.get(Self::FLIPPED) { + if Self::flipped_in(styles) { std::mem::swap(&mut size.x, &mut size.y); } @@ -274,12 +274,12 @@ impl PageNode { // Determine the margins. let default = Rel::from(0.1190 * min); - let padding = styles.get(Self::MARGIN).map(|side| side.unwrap_or(default)); + let padding = Self::margin_in(styles).map(|side| side.unwrap_or(default)); let mut child = self.body(); // Realize columns. - let columns = styles.get(Self::COLUMNS); + let columns = Self::columns_in(styles); if columns.get() > 1 { child = ColumnsNode::new(columns, child).pack(); } @@ -291,11 +291,11 @@ impl PageNode { let regions = Regions::repeat(size, size.map(Abs::is_finite)); let mut fragment = child.layout(vt, styles, regions)?; - let fill = styles.get(Self::FILL); - let header = styles.get(Self::HEADER); - let footer = styles.get(Self::FOOTER); - let foreground = styles.get(Self::FOREGROUND); - let background = styles.get(Self::BACKGROUND); + 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); // Realize overlays. for frame in &mut fragment { diff --git a/library/src/layout/par.rs b/library/src/layout/par.rs index 2564940c0..10388f882 100644 --- a/library/src/layout/par.rs +++ b/library/src/layout/par.rs @@ -2,7 +2,7 @@ use unicode_bidi::{BidiInfo, Level as BidiLevel}; use unicode_script::{Script, UnicodeScript}; use xi_unicode::LineBreakIterator; -use typst::model::{Key, StyledNode}; +use typst::model::StyledNode; use super::{BoxNode, HNode, Sizing, Spacing}; use crate::layout::AlignNode; @@ -495,7 +495,7 @@ fn collect<'a>( let mut iter = children.iter().peekable(); if consecutive { - let indent = styles.get(ParNode::INDENT); + let indent = ParNode::indent_in(*styles); if !indent.is_zero() && children .iter() @@ -530,7 +530,7 @@ fn collect<'a>( Segment::Text(1) } else if let Some(node) = child.to::() { let prev = full.len(); - if let Some(case) = styles.get(TextNode::CASE) { + if let Some(case) = TextNode::case_in(styles) { full.push_str(&case.apply(&node.text())); } else { full.push_str(&node.text()); @@ -545,9 +545,9 @@ fn collect<'a>( Segment::Text(c.len_utf8()) } else if let Some(node) = child.to::() { let prev = full.len(); - if styles.get(SmartQuoteNode::ENABLED) { - let lang = styles.get(TextNode::LANG); - let region = styles.get(TextNode::REGION); + if SmartQuoteNode::enabled_in(styles) { + let lang = TextNode::lang_in(styles); + let region = TextNode::region_in(styles); let quotes = Quotes::from_lang(lang, region); let peeked = iter.peek().and_then(|child| { if let Some(node) = child.to::() { @@ -613,7 +613,7 @@ fn prepare<'a>( ) -> SourceResult> { let bidi = BidiInfo::new( text, - match styles.get(TextNode::DIR) { + match TextNode::dir_in(styles) { Dir::LTR => Some(BidiLevel::ltr()), Dir::RTL => Some(BidiLevel::rtl()), _ => None, @@ -642,7 +642,7 @@ fn prepare<'a>( Segment::Formula(formula) => { let pod = Regions::one(region, Axes::splat(false)); let mut frame = formula.layout(vt, styles, pod)?.into_frame(); - frame.translate(Point::with_y(styles.get(TextNode::BASELINE))); + frame.translate(Point::with_y(TextNode::baseline_in(styles))); items.push(Item::Frame(frame)); } Segment::Box(node) => { @@ -651,7 +651,7 @@ fn prepare<'a>( } else { let pod = Regions::one(region, Axes::splat(false)); let mut frame = node.layout(vt, styles, pod)?.into_frame(); - frame.translate(Point::with_y(styles.get(TextNode::BASELINE))); + frame.translate(Point::with_y(TextNode::baseline_in(styles))); items.push(Item::Frame(frame)); } } @@ -664,10 +664,10 @@ fn prepare<'a>( bidi, items, styles, - hyphenate: shared_get(styles, children, TextNode::HYPHENATE), - lang: shared_get(styles, children, TextNode::LANG), - align: styles.get(AlignNode::ALIGNMENT).x.resolve(styles), - justify: styles.get(ParNode::JUSTIFY), + hyphenate: shared_get(styles, children, TextNode::hyphenate_in), + lang: shared_get(styles, children, TextNode::lang_in), + align: AlignNode::alignment_in(styles).x.resolve(styles), + justify: ParNode::justify_in(styles), }) } @@ -727,22 +727,23 @@ fn is_compatible(a: Script, b: Script) -> bool { /// Get a style property, but only if it is the same for all children of the /// paragraph. -fn shared_get<'a, K: Key>( +fn shared_get<'a, T: PartialEq>( styles: StyleChain<'a>, children: &[Content], - key: K, -) -> Option { + getter: fn(StyleChain) -> T, +) -> Option { + let value = getter(styles); children .iter() .filter_map(|child| child.to::()) - .all(|node| !node.map().contains(key)) - .then(|| styles.get(key)) + .all(|node| getter(styles.chain(&node.map())) == value) + .then(|| value) } /// Find suitable linebreaks. fn linebreak<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec> { - let linebreaks = p.styles.get(ParNode::LINEBREAKS).unwrap_or_else(|| { - if p.styles.get(ParNode::JUSTIFY) { + let linebreaks = ParNode::linebreaks_in(p.styles).unwrap_or_else(|| { + if ParNode::justify_in(p.styles) { Linebreaks::Optimized } else { Linebreaks::Simple @@ -840,7 +841,7 @@ fn linebreak_optimized<'a>(vt: &Vt, p: &'a Preparation<'a>, width: Abs) -> Vec { .hyphenate .or_else(|| { let shaped = self.p.find(offset)?.text()?; - Some(shaped.styles.get(TextNode::HYPHENATE)) + Some(TextNode::hyphenate_in(shaped.styles)) }) .unwrap_or(false) } @@ -1014,7 +1015,7 @@ impl Breakpoints<'_> { fn lang(&self, offset: usize) -> Option { let lang = self.p.lang.or_else(|| { let shaped = self.p.find(offset)?.text()?; - Some(shaped.styles.get(TextNode::LANG)) + Some(TextNode::lang_in(shaped.styles)) })?; let bytes = lang.as_str().as_bytes().try_into().ok()?; @@ -1155,7 +1156,7 @@ fn finalize( .collect::>()?; // Prevent orphans. - let leading = p.styles.get(ParNode::LEADING); + let leading = ParNode::leading_in(p.styles); if frames.len() >= 2 && !frames[1].is_empty() { let second = frames.remove(1); let first = &mut frames[0]; @@ -1199,7 +1200,7 @@ fn commit( if let Some(Item::Text(text)) = reordered.first() { if let Some(glyph) = text.glyphs.first() { if !text.dir.is_positive() - && text.styles.get(TextNode::OVERHANG) + && TextNode::overhang_in(text.styles) && (reordered.len() > 1 || text.glyphs.len() > 1) { let amount = overhang(glyph.c) * glyph.x_advance.at(text.size); @@ -1213,7 +1214,7 @@ fn commit( if let Some(Item::Text(text)) = reordered.last() { if let Some(glyph) = text.glyphs.last() { if text.dir.is_positive() - && text.styles.get(TextNode::OVERHANG) + && TextNode::overhang_in(text.styles) && (reordered.len() > 1 || text.glyphs.len() > 1) { let amount = overhang(glyph.c) * glyph.x_advance.at(text.size); @@ -1257,7 +1258,7 @@ fn commit( let region = Size::new(amount, full); let pod = Regions::one(region, Axes::new(true, false)); let mut frame = node.layout(vt, *styles, pod)?.into_frame(); - frame.translate(Point::with_y(styles.get(TextNode::BASELINE))); + frame.translate(Point::with_y(TextNode::baseline_in(*styles))); push(&mut offset, frame); } else { offset += amount; diff --git a/library/src/layout/repeat.rs b/library/src/layout/repeat.rs index 67dca2857..0fd9ad83e 100644 --- a/library/src/layout/repeat.rs +++ b/library/src/layout/repeat.rs @@ -40,7 +40,7 @@ impl Layout for RepeatNode { ) -> SourceResult { let pod = Regions::one(regions.size, Axes::new(false, false)); let piece = self.body().layout(vt, styles, pod)?.into_frame(); - let align = styles.get(AlignNode::ALIGNMENT).x.resolve(styles); + let align = AlignNode::alignment_in(styles).x.resolve(styles); let fill = regions.size.x; let width = piece.width(); diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index 430af715a..f4e4ab2c1 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -201,10 +201,9 @@ impl<'a> StackLayouter<'a> { // Block-axis alignment of the `AlignNode` is respected // by the stack node. - let aligns = if let Some(styled) = block.to::() { - styles.chain(&styled.map()).get(AlignNode::ALIGNMENT) - } else { - styles.get(AlignNode::ALIGNMENT) + let aligns = match block.to::() { + Some(styled) => AlignNode::alignment_in(styles.chain(&styled.map())), + None => AlignNode::alignment_in(styles), }; let aligns = aligns.resolve(styles); diff --git a/library/src/layout/table.rs b/library/src/layout/table.rs index 0083a7bf5..1bd47df03 100644 --- a/library/src/layout/table.rs +++ b/library/src/layout/table.rs @@ -128,8 +128,8 @@ impl Layout for TableNode { styles: StyleChain, regions: Regions, ) -> SourceResult { - let inset = styles.get(Self::INSET); - let align = styles.get(Self::ALIGN); + let inset = Self::inset_in(styles); + let align = Self::align_in(styles); let tracks = Axes::new(self.columns().0, self.rows().0); let gutter = Axes::new(self.column_gutter().0, self.row_gutter().0); @@ -144,15 +144,15 @@ impl Layout for TableNode { let x = i % cols; let y = i / cols; if let Smart::Custom(alignment) = align.resolve(vt, x, y)? { - child = child.styled(AlignNode::ALIGNMENT, alignment) + child = child.styled(AlignNode::set_alignment(alignment)); } Ok(child) }) .collect::>()?; - let fill = styles.get(Self::FILL); - let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default); + let fill = Self::fill_in(styles); + let stroke = Self::stroke_in(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 2933ea20a..d859a4472 100644 --- a/library/src/layout/terms.rs +++ b/library/src/layout/terms.rs @@ -90,14 +90,13 @@ impl Layout for TermsNode { styles: StyleChain, regions: Regions, ) -> SourceResult { - let indent = styles.get(Self::INDENT); - let body_indent = styles.get(Self::HANGING_INDENT); + let indent = Self::indent_in(styles); + let body_indent = Self::hanging_indent_in(styles); let gutter = if self.tight() { - styles.get(ParNode::LEADING).into() + ParNode::leading_in(styles).into() } else { - styles - .get(Self::SPACING) - .unwrap_or_else(|| styles.get(BlockNode::BELOW).amount()) + Self::spacing_in(styles) + .unwrap_or_else(|| BlockNode::below_in(styles).amount()) }; let mut cells = vec![]; diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs index b71cfa9f1..41d3d120a 100644 --- a/library/src/layout/transform.rs +++ b/library/src/layout/transform.rs @@ -122,7 +122,7 @@ impl Layout for RotateNode { ) -> SourceResult { let pod = Regions::one(regions.base(), Axes::splat(false)); let mut frame = self.body().layout(vt, styles, pod)?.into_frame(); - let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); + let origin = Self::origin_in(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())) @@ -199,7 +199,7 @@ impl Layout for ScaleNode { ) -> SourceResult { let pod = Regions::one(regions.base(), Axes::splat(false)); let mut frame = self.body().layout(vt, styles, pod)?.into_frame(); - let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); + let origin = Self::origin_in(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())) diff --git a/library/src/lib.rs b/library/src/lib.rs index e0994f25f..fcd0bf45c 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -164,8 +164,8 @@ fn styles() -> StyleMap { fn items() -> LangItems { LangItems { layout: |world, content, styles| content.layout_root(world, styles), - em: |styles| styles.get(text::TextNode::SIZE), - dir: |styles| styles.get(text::TextNode::DIR), + em: text::TextNode::size_in, + dir: text::TextNode::dir_in, space: || text::SpaceNode::new().pack(), linebreak: || text::LinebreakNode::new().pack(), text: |text| text::TextNode::new(text).pack(), @@ -178,7 +178,7 @@ fn items() -> LangItems { raw: |text, lang, block| { let content = text::RawNode::new(text).with_block(block).pack(); match lang { - Some(_) => content.styled(text::RawNode::LANG, lang), + Some(_) => content.styled(text::RawNode::set_lang(lang)), None => content, } }, diff --git a/library/src/math/ctx.rs b/library/src/math/ctx.rs index a3aa40472..991d582c6 100644 --- a/library/src/math/ctx.rs +++ b/library/src/math/ctx.rs @@ -49,7 +49,7 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { ) -> Self { let table = font.ttf().tables().math.unwrap(); let constants = table.constants.unwrap(); - let size = styles.get(TextNode::SIZE); + let size = TextNode::size_in(styles); let ttf = font.ttf(); let space_width = ttf .glyph_index(' ') @@ -175,21 +175,20 @@ impl<'a, 'b, 'v> MathContext<'a, 'b, 'v> { pub fn style(&mut self, style: MathStyle) { self.style_stack.push((self.style, self.size)); - let base_size = self.styles().get(TextNode::SIZE) / self.style.size.factor(self); + let base_size = TextNode::size_in(self.styles()) / self.style.size.factor(self); self.size = base_size * style.size.factor(self); - self.map.set(TextNode::SIZE, TextSize(self.size.into())); - self.map.set( - TextNode::STYLE, - if style.italic == Smart::Custom(true) { + self.map.set(TextNode::set_size(TextSize(self.size.into()))); + self.map + .set(TextNode::set_style(if style.italic == Smart::Custom(true) { FontStyle::Italic } else { FontStyle::Normal - }, - ); - self.map.set( - TextNode::WEIGHT, - if style.bold { FontWeight::BOLD } else { FontWeight::REGULAR }, - ); + })); + self.map.set(TextNode::set_weight(if style.bold { + FontWeight::BOLD + } else { + FontWeight::REGULAR + })); self.style = style; } diff --git a/library/src/math/frac.rs b/library/src/math/frac.rs index ea647fc52..c1f4065b0 100644 --- a/library/src/math/frac.rs +++ b/library/src/math/frac.rs @@ -133,7 +133,7 @@ fn layout( line_pos, Element::Shape( Geometry::Line(Point::with_x(line_width)).stroked(Stroke { - paint: ctx.styles().get(TextNode::FILL), + paint: TextNode::fill_in(ctx.styles()), thickness, }), ), diff --git a/library/src/math/fragment.rs b/library/src/math/fragment.rs index 93c5946bb..73daa4b2c 100644 --- a/library/src/math/fragment.rs +++ b/library/src/math/fragment.rs @@ -180,8 +180,8 @@ impl GlyphFragment { id, c, font: ctx.font.clone(), - lang: ctx.styles().get(TextNode::LANG), - fill: ctx.styles().get(TextNode::FILL), + lang: TextNode::lang_in(ctx.styles()), + fill: TextNode::fill_in(ctx.styles()), style: ctx.style, font_size: ctx.size, width, diff --git a/library/src/math/matrix.rs b/library/src/math/matrix.rs index c73037353..d4bf52f3d 100644 --- a/library/src/math/matrix.rs +++ b/library/src/math/matrix.rs @@ -35,7 +35,7 @@ pub struct VecNode { impl LayoutMath for VecNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - let delim = ctx.styles().get(Self::DELIM); + let delim = Self::delim_in(ctx.styles()); let frame = layout_vec_body(ctx, &self.children(), Align::Center)?; layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close())) } @@ -115,7 +115,7 @@ impl Construct for MatNode { impl LayoutMath for MatNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - let delim = ctx.styles().get(Self::DELIM); + let delim = Self::delim_in(ctx.styles()); let frame = layout_mat_body(ctx, &self.rows())?; layout_delimiters(ctx, frame, Some(delim.open()), Some(delim.close())) } @@ -156,7 +156,7 @@ pub struct CasesNode { impl LayoutMath for CasesNode { fn layout_math(&self, ctx: &mut MathContext) -> SourceResult<()> { - let delim = ctx.styles().get(Self::DELIM); + let delim = Self::delim_in(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 6f5e59077..3f0b0607e 100644 --- a/library/src/math/mod.rs +++ b/library/src/math/mod.rs @@ -158,11 +158,10 @@ impl Show for FormulaNode { impl Finalize for FormulaNode { fn finalize(&self, realized: Content) -> Content { realized - .styled(TextNode::WEIGHT, FontWeight::from_number(450)) - .styled( - TextNode::FONT, - FontList(vec![FontFamily::new("New Computer Modern Math")]), - ) + .styled(TextNode::set_weight(FontWeight::from_number(450))) + .styled(TextNode::set_font(FontList(vec![FontFamily::new( + "New Computer Modern Math", + )]))) } } @@ -196,10 +195,10 @@ impl Layout for FormulaNode { let mut frame = ctx.layout_frame(self)?; if !block { - let slack = styles.get(ParNode::LEADING) * 0.7; - let top_edge = styles.get(TextNode::TOP_EDGE).resolve(styles, font.metrics()); + let slack = ParNode::leading_in(styles) * 0.7; + let top_edge = TextNode::top_edge_in(styles).resolve(styles, font.metrics()); let bottom_edge = - -styles.get(TextNode::BOTTOM_EDGE).resolve(styles, font.metrics()); + -TextNode::bottom_edge_in(styles).resolve(styles, font.metrics()); let ascent = top_edge.max(frame.ascent() - slack); let descent = bottom_edge.max(frame.descent() - slack); @@ -232,7 +231,9 @@ impl LayoutMath for Content { if let Some(styled) = self.to::() { let map = styled.map(); - if map.contains(TextNode::FONT) { + if TextNode::font_in(ctx.styles().chain(&map)) + != TextNode::font_in(ctx.styles()) + { let frame = ctx.layout_content(self)?; ctx.push(FrameFragment::new(ctx, frame).with_spaced(true)); return Ok(()); @@ -241,7 +242,7 @@ impl LayoutMath for Content { let prev_map = std::mem::replace(&mut ctx.map, map); let prev_size = ctx.size; ctx.map.apply(prev_map.clone()); - ctx.size = ctx.styles().get(TextNode::SIZE); + ctx.size = TextNode::size_in(ctx.styles()); styled.body().layout_math(ctx)?; ctx.size = prev_size; ctx.map = prev_map; diff --git a/library/src/math/root.rs b/library/src/math/root.rs index 191acb94a..e190c65f3 100644 --- a/library/src/math/root.rs +++ b/library/src/math/root.rs @@ -128,7 +128,7 @@ fn layout( line_pos, Element::Shape( Geometry::Line(Point::with_x(radicand.width())) - .stroked(Stroke { paint: ctx.styles().get(TextNode::FILL), thickness }), + .stroked(Stroke { paint: TextNode::fill_in(ctx.styles()), thickness }), ), ); diff --git a/library/src/math/row.rs b/library/src/math/row.rs index b7720c141..ecb2e31e9 100644 --- a/library/src/math/row.rs +++ b/library/src/math/row.rs @@ -103,7 +103,7 @@ impl MathRow { pub fn to_frame(self, ctx: &MathContext) -> Frame { let styles = ctx.styles(); - let align = styles.get(AlignNode::ALIGNMENT).x.resolve(styles); + let align = AlignNode::alignment_in(styles).x.resolve(styles); self.to_aligned_frame(ctx, &[], align) } @@ -124,7 +124,7 @@ impl MathRow { if self.iter().any(|frag| matches!(frag, MathFragment::Linebreak)) { let fragments: Vec<_> = std::mem::take(&mut self.0); let leading = if ctx.style.size >= MathSize::Text { - ctx.styles().get(ParNode::LEADING) + ParNode::leading_in(ctx.styles()) } else { TIGHT_LEADING.scaled(ctx) }; diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs index 29ed38fec..1325d85b0 100644 --- a/library/src/meta/document.rs +++ b/library/src/meta/document.rs @@ -58,8 +58,8 @@ impl LayoutRoot for DocumentNode { Ok(Document { pages, - title: styles.get(Self::TITLE).clone(), - author: styles.get(Self::AUTHOR).0.clone(), + title: Self::title_in(styles), + author: Self::author_in(styles).0, }) } } diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs index bd95001b4..f0107a6a5 100644 --- a/library/src/meta/heading.rs +++ b/library/src/meta/heading.rs @@ -106,15 +106,15 @@ impl Prepare for HeadingNode { } let mut numbers = Value::None; - if let Some(numbering) = styles.get(Self::NUMBERING) { + if let Some(numbering) = Self::numbering_in(styles) { numbers = numbering.apply(vt.world(), counter.advance(self))?; } - this.push_field("outlined", Value::Bool(styles.get(Self::OUTLINED))); + this.push_field("outlined", Value::Bool(Self::outlined_in(styles))); this.push_field("numbers", numbers); let meta = Meta::Node(my_id, this.clone()); - Ok(this.styled(MetaNode::DATA, vec![meta])) + Ok(this.styled(MetaNode::set_data(vec![meta]))) } } @@ -145,11 +145,11 @@ impl Finalize for HeadingNode { let below = Em::new(0.75) / scale; let mut map = StyleMap::new(); - map.set(TextNode::SIZE, TextSize(size.into())); - map.set(TextNode::WEIGHT, FontWeight::BOLD); - map.set(BlockNode::ABOVE, VNode::block_around(above.into())); - map.set(BlockNode::BELOW, VNode::block_around(below.into())); - map.set(BlockNode::STICKY, true); + map.set(TextNode::set_size(TextSize(size.into()))); + map.set(TextNode::set_weight(FontWeight::BOLD)); + map.set(BlockNode::set_above(VNode::block_around(above.into()))); + map.set(BlockNode::set_below(VNode::block_around(below.into()))); + map.set(BlockNode::set_sticky(true)); realized.styled_with_map(map) } } diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs index 4f07aaee5..32b70153c 100644 --- a/library/src/meta/link.rs +++ b/library/src/meta/link.rs @@ -91,8 +91,8 @@ impl Show for LinkNode { impl Finalize for LinkNode { fn finalize(&self, realized: Content) -> Content { realized - .styled(MetaNode::DATA, vec![Meta::Link(self.dest())]) - .styled(TextNode::HYPHENATE, Hyphenate(Smart::Custom(false))) + .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 4b5a44f73..3ccc991eb 100644 --- a/library/src/meta/outline.rs +++ b/library/src/meta/outline.rs @@ -102,9 +102,9 @@ impl Show for OutlineNode { styles: StyleChain, ) -> SourceResult { let mut seq = vec![ParbreakNode::new().pack()]; - if let Some(title) = styles.get(Self::TITLE) { + if let Some(title) = Self::title_in(styles) { let title = title.clone().unwrap_or_else(|| { - TextNode::packed(match styles.get(TextNode::LANG) { + TextNode::packed(match TextNode::lang_in(styles) { Lang::GERMAN => "Inhaltsverzeichnis", Lang::ENGLISH | _ => "Contents", }) @@ -113,13 +113,13 @@ impl Show for OutlineNode { seq.push( HeadingNode::new(title) .pack() - .styled(HeadingNode::NUMBERING, None) - .styled(HeadingNode::OUTLINED, false), + .styled(HeadingNode::set_numbering(None)) + .styled(HeadingNode::set_outlined(false)), ); } - let indent = styles.get(Self::INDENT); - let depth = styles.get(Self::DEPTH); + let indent = Self::indent_in(styles); + let depth = Self::depth_in(styles); let mut ancestors: Vec<&Content> = vec![]; for (_, node) in vt.locate(Selector::node::()) { @@ -171,7 +171,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) = styles.get(Self::FILL) { + if let Some(filler) = Self::fill_in(styles) { seq.push(SpaceNode::new().pack()); seq.push( BoxNode::new() diff --git a/library/src/shared/ext.rs b/library/src/shared/ext.rs index f797e1352..44f933e2e 100644 --- a/library/src/shared/ext.rs +++ b/library/src/shared/ext.rs @@ -1,6 +1,8 @@ //! Extension traits. +use crate::layout::{AlignNode, MoveNode, PadNode}; use crate::prelude::*; +use crate::text::{EmphNode, FontFamily, FontList, StrongNode, TextNode, UnderlineNode}; /// Additional methods on content. pub trait ContentExt { @@ -28,27 +30,27 @@ pub trait ContentExt { impl ContentExt for Content { fn strong(self) -> Self { - crate::text::StrongNode::new(self).pack() + StrongNode::new(self).pack() } fn emph(self) -> Self { - crate::text::EmphNode::new(self).pack() + EmphNode::new(self).pack() } fn underlined(self) -> Self { - crate::text::UnderlineNode::new(self).pack() + UnderlineNode::new(self).pack() } fn linked(self, dest: Destination) -> Self { - self.styled(MetaNode::DATA, vec![Meta::Link(dest.clone())]) + self.styled(MetaNode::set_data(vec![Meta::Link(dest.clone())])) } fn aligned(self, aligns: Axes>) -> Self { - self.styled(crate::layout::AlignNode::ALIGNMENT, aligns) + self.styled(AlignNode::set_alignment(aligns)) } fn padded(self, padding: Sides>) -> Self { - crate::layout::PadNode::new(self) + PadNode::new(self) .with_left(padding.left) .with_top(padding.top) .with_right(padding.right) @@ -57,10 +59,7 @@ impl ContentExt for Content { } fn moved(self, delta: Axes>) -> Self { - crate::layout::MoveNode::new(self) - .with_dx(delta.x) - .with_dy(delta.y) - .pack() + MoveNode::new(self).with_dx(delta.x).with_dy(delta.y).pack() } } @@ -68,18 +67,15 @@ impl ContentExt for Content { pub trait StyleMapExt { /// Set a font family composed of a preferred family and existing families /// from a style chain. - fn set_family(&mut self, preferred: crate::text::FontFamily, existing: StyleChain); + fn set_family(&mut self, preferred: FontFamily, existing: StyleChain); } impl StyleMapExt for StyleMap { - fn set_family(&mut self, preferred: crate::text::FontFamily, existing: StyleChain) { - self.set( - crate::text::TextNode::FONT, - crate::text::FontList( - std::iter::once(preferred) - .chain(existing.get(crate::text::TextNode::FONT).0.iter().cloned()) - .collect(), - ), - ); + fn set_family(&mut self, preferred: FontFamily, existing: StyleChain) { + self.set(TextNode::set_font(FontList( + std::iter::once(preferred) + .chain(TextNode::font_in(existing)) + .collect(), + ))); } } diff --git a/library/src/text/deco.rs b/library/src/text/deco.rs index d47b336b9..1b3167e10 100644 --- a/library/src/text/deco.rs +++ b/library/src/text/deco.rs @@ -75,16 +75,13 @@ pub struct UnderlineNode { impl Show for UnderlineNode { fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult { - Ok(self.body().styled( - TextNode::DECO, - Decoration { - line: DecoLine::Underline, - stroke: styles.get(Self::STROKE).unwrap_or_default(), - offset: styles.get(Self::OFFSET), - extent: styles.get(Self::EXTENT), - evade: styles.get(Self::EVADE), - }, - )) + 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), + }))) } } @@ -165,16 +162,13 @@ pub struct OverlineNode { impl Show for OverlineNode { fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult { - Ok(self.body().styled( - TextNode::DECO, - Decoration { - line: DecoLine::Overline, - stroke: styles.get(Self::STROKE).unwrap_or_default(), - offset: styles.get(Self::OFFSET), - extent: styles.get(Self::EXTENT), - evade: styles.get(Self::EVADE), - }, - )) + 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), + }))) } } @@ -239,16 +233,13 @@ pub struct StrikeNode { impl Show for StrikeNode { fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult { - Ok(self.body().styled( - TextNode::DECO, - Decoration { - line: DecoLine::Strikethrough, - stroke: styles.get(Self::STROKE).unwrap_or_default(), - offset: styles.get(Self::OFFSET), - extent: styles.get(Self::EXTENT), - evade: false, - }, - )) + 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), + evade: false, + }))) } } diff --git a/library/src/text/misc.rs b/library/src/text/misc.rs index 9caaf68e5..91ac17487 100644 --- a/library/src/text/misc.rs +++ b/library/src/text/misc.rs @@ -103,7 +103,7 @@ pub struct StrongNode { impl Show for StrongNode { fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult { - Ok(self.body().styled(TextNode::DELTA, Delta(styles.get(Self::DELTA)))) + Ok(self.body().styled(TextNode::set_delta(Delta(Self::delta_in(styles))))) } } @@ -164,7 +164,7 @@ pub struct EmphNode { impl Show for EmphNode { fn show(&self, _: &mut Vt, _: &Content, _: StyleChain) -> SourceResult { - Ok(self.body().styled(TextNode::EMPH, Toggle)) + Ok(self.body().styled(TextNode::set_emph(Toggle))) } } @@ -233,7 +233,7 @@ pub fn upper(args: &mut Args) -> SourceResult { fn case(case: Case, args: &mut Args) -> SourceResult { Ok(match args.expect("string or content")? { ToCase::Str(v) => Value::Str(case.apply(&v).into()), - ToCase::Content(v) => Value::Content(v.styled(TextNode::CASE, Some(case))), + ToCase::Content(v) => Value::Content(v.styled(TextNode::set_case(Some(case)))), }) } @@ -313,7 +313,7 @@ cast_to_value! { #[func] pub fn smallcaps(args: &mut Args) -> SourceResult { let body: Content = args.expect("content")?; - Ok(Value::Content(body.styled(TextNode::SMALLCAPS, true))) + Ok(Value::Content(body.styled(TextNode::set_smallcaps(true)))) } /// Create blind text. diff --git a/library/src/text/mod.rs b/library/src/text/mod.rs index 329ff6a98..2318c4269 100644 --- a/library/src/text/mod.rs +++ b/library/src/text/mod.rs @@ -578,6 +578,15 @@ cast_to_value! { #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct FontList(pub Vec); +impl IntoIterator for FontList { + type IntoIter = std::vec::IntoIter; + type Item = FontFamily; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + cast_from_value! { FontList, family: FontFamily => Self(vec![family]), @@ -664,7 +673,7 @@ impl Resolve for HorizontalDir { fn resolve(self, styles: StyleChain) -> Self::Output { match self.0 { - Smart::Auto => styles.get(TextNode::LANG).dir(), + Smart::Auto => TextNode::lang_in(styles).dir(), Smart::Custom(dir) => dir, } } @@ -688,7 +697,7 @@ impl Resolve for Hyphenate { fn resolve(self, styles: StyleChain) -> Self::Output { match self.0 { - Smart::Auto => styles.get(ParNode::JUSTIFY), + Smart::Auto => ParNode::justify_in(styles), Smart::Custom(v) => v, } } diff --git a/library/src/text/raw.rs b/library/src/text/raw.rs index 4eefb0eac..8526a278f 100644 --- a/library/src/text/raw.rs +++ b/library/src/text/raw.rs @@ -114,7 +114,7 @@ impl Prepare for RawNode { mut this: Content, styles: StyleChain, ) -> SourceResult { - this.push_field("lang", styles.get(Self::LANG).clone()); + this.push_field("lang", Self::lang_in(styles).clone()); Ok(this) } } @@ -122,7 +122,7 @@ impl Prepare for RawNode { impl Show for RawNode { fn show(&self, _: &mut Vt, _: &Content, styles: StyleChain) -> SourceResult { let text = self.text(); - let lang = styles.get(Self::LANG).as_ref().map(|s| s.to_lowercase()); + let lang = Self::lang_in(styles).as_ref().map(|s| s.to_lowercase()); let foreground = THEME .settings .foreground @@ -181,11 +181,11 @@ impl Show for RawNode { impl Finalize for RawNode { fn finalize(&self, realized: Content) -> Content { let mut map = StyleMap::new(); - map.set(TextNode::OVERHANG, false); - map.set(TextNode::HYPHENATE, Hyphenate(Smart::Custom(false))); - map.set(TextNode::SIZE, TextSize(Em::new(0.8).into())); - map.set(TextNode::FONT, FontList(vec![FontFamily::new("DejaVu Sans Mono")])); - map.set(SmartQuoteNode::ENABLED, false); + map.set(TextNode::set_overhang(false)); + map.set(TextNode::set_hyphenate(Hyphenate(Smart::Custom(false)))); + map.set(TextNode::set_size(TextSize(Em::new(0.8).into()))); + map.set(TextNode::set_font(FontList(vec![FontFamily::new("DejaVu Sans Mono")]))); + map.set(SmartQuoteNode::set_enabled(false)); realized.styled_with_map(map) } } @@ -221,7 +221,7 @@ fn styled(piece: &str, foreground: Paint, style: synt::Style) -> Content { let paint = to_typst(style.foreground).into(); if paint != foreground { - body = body.styled(TextNode::FILL, paint); + body = body.styled(TextNode::set_fill(paint)); } if style.font_style.contains(synt::FontStyle::BOLD) { diff --git a/library/src/text/shaping.rs b/library/src/text/shaping.rs index e1b121204..e7ce4027a 100644 --- a/library/src/text/shaping.rs +++ b/library/src/text/shaping.rs @@ -88,10 +88,10 @@ impl<'a> ShapedText<'a> { let mut frame = Frame::new(size); frame.set_baseline(top); - let shift = self.styles.get(TextNode::BASELINE); - let lang = self.styles.get(TextNode::LANG); - let decos = self.styles.get(TextNode::DECO); - let fill = self.styles.get(TextNode::FILL); + let shift = TextNode::baseline_in(self.styles); + let lang = TextNode::lang_in(self.styles); + let decos = TextNode::deco_in(self.styles); + let fill = TextNode::fill_in(self.styles); for ((font, y_offset), group) in self.glyphs.as_ref().group_by_key(|g| (g.font.clone(), g.y_offset)) @@ -137,8 +137,8 @@ impl<'a> ShapedText<'a> { let mut top = Abs::zero(); let mut bottom = Abs::zero(); - let top_edge = self.styles.get(TextNode::TOP_EDGE); - let bottom_edge = self.styles.get(TextNode::BOTTOM_EDGE); + let top_edge = TextNode::top_edge_in(self.styles); + let bottom_edge = TextNode::bottom_edge_in(self.styles); // Expand top and bottom by reading the font's vertical metrics. let mut expand = |font: &Font| { @@ -315,8 +315,7 @@ pub fn shape<'a>( styles: StyleChain<'a>, dir: Dir, ) -> ShapedText<'a> { - let size = styles.get(TextNode::SIZE); - + let size = TextNode::size_in(styles); let mut ctx = ShapingContext { vt, size, @@ -325,7 +324,7 @@ pub fn shape<'a>( styles, variant: variant(styles), tags: tags(styles), - fallback: styles.get(TextNode::FALLBACK), + fallback: TextNode::fallback_in(styles), dir, }; @@ -494,11 +493,9 @@ fn shape_tofus(ctx: &mut ShapingContext, base: usize, text: &str, font: Font) { /// Apply tracking and spacing to the shaped glyphs. fn track_and_space(ctx: &mut ShapingContext) { - let tracking = Em::from_length(ctx.styles.get(TextNode::TRACKING), ctx.size); - let spacing = ctx - .styles - .get(TextNode::SPACING) - .map(|abs| Em::from_length(abs, ctx.size)); + let tracking = Em::from_length(TextNode::tracking_in(ctx.styles), ctx.size); + let spacing = + TextNode::spacing_in(ctx.styles).map(|abs| Em::from_length(abs, ctx.size)); let mut glyphs = ctx.glyphs.iter_mut().peekable(); while let Some(glyph) = glyphs.next() { @@ -527,17 +524,17 @@ fn nbsp_delta(font: &Font) -> Option { /// Resolve the font variant. pub fn variant(styles: StyleChain) -> FontVariant { let mut variant = FontVariant::new( - styles.get(TextNode::STYLE), - styles.get(TextNode::WEIGHT), - styles.get(TextNode::STRETCH), + TextNode::style_in(styles), + TextNode::weight_in(styles), + TextNode::stretch_in(styles), ); - let delta = styles.get(TextNode::DELTA); + let delta = TextNode::delta_in(styles); variant.weight = variant .weight .thicken(delta.clamp(i16::MIN as i64, i16::MAX as i64) as i16); - if styles.get(TextNode::EMPH) { + if TextNode::emph_in(styles) { variant.style = match variant.style { FontStyle::Normal => FontStyle::Italic, FontStyle::Italic => FontStyle::Normal, @@ -558,10 +555,8 @@ pub fn families(styles: StyleChain) -> impl Iterator + Clone "segoe ui emoji", ]; - let tail = if styles.get(TextNode::FALLBACK) { FALLBACKS } else { &[] }; - styles - .get(TextNode::FONT) - .0 + let tail = if TextNode::fallback_in(styles) { FALLBACKS } else { &[] }; + TextNode::font_in(styles) .into_iter() .chain(tail.iter().copied().map(FontFamily::new)) } @@ -574,59 +569,59 @@ fn tags(styles: StyleChain) -> Vec { }; // Features that are on by default in Harfbuzz are only added if disabled. - if !styles.get(TextNode::KERNING) { + if !TextNode::kerning_in(styles) { feat(b"kern", 0); } // Features that are off by default in Harfbuzz are only added if enabled. - if styles.get(TextNode::SMALLCAPS) { + if TextNode::smallcaps_in(styles) { feat(b"smcp", 1); } - if styles.get(TextNode::ALTERNATES) { + if TextNode::alternates_in(styles) { feat(b"salt", 1); } let storage; - if let Some(set) = styles.get(TextNode::STYLISTIC_SET) { + if let Some(set) = TextNode::stylistic_set_in(styles) { storage = [b's', b's', b'0' + set.get() / 10, b'0' + set.get() % 10]; feat(&storage, 1); } - if !styles.get(TextNode::LIGATURES) { + if !TextNode::ligatures_in(styles) { feat(b"liga", 0); feat(b"clig", 0); } - if styles.get(TextNode::DISCRETIONARY_LIGATURES) { + if TextNode::discretionary_ligatures_in(styles) { feat(b"dlig", 1); } - if styles.get(TextNode::HISTORICAL_LIGATURES) { + if TextNode::historical_ligatures_in(styles) { feat(b"hilg", 1); } - match styles.get(TextNode::NUMBER_TYPE) { + match TextNode::number_type_in(styles) { Smart::Auto => {} Smart::Custom(NumberType::Lining) => feat(b"lnum", 1), Smart::Custom(NumberType::OldStyle) => feat(b"onum", 1), } - match styles.get(TextNode::NUMBER_WIDTH) { + match TextNode::number_width_in(styles) { Smart::Auto => {} Smart::Custom(NumberWidth::Proportional) => feat(b"pnum", 1), Smart::Custom(NumberWidth::Tabular) => feat(b"tnum", 1), } - if styles.get(TextNode::SLASHED_ZERO) { + if TextNode::slashed_zero_in(styles) { feat(b"zero", 1); } - if styles.get(TextNode::FRACTIONS) { + if TextNode::fractions_in(styles) { feat(b"frac", 1); } - for (tag, value) in styles.get(TextNode::FEATURES).0 { + for (tag, value) in TextNode::features_in(styles).0 { tags.push(Feature::new(tag, value, ..)) } @@ -636,8 +631,8 @@ fn tags(styles: StyleChain) -> Vec { /// Process the language and and region of a style chain into a /// rustybuzz-compatible BCP 47 language. fn language(styles: StyleChain) -> rustybuzz::Language { - let mut bcp: EcoString = styles.get(TextNode::LANG).as_str().into(); - if let Some(region) = styles.get(TextNode::REGION) { + let mut bcp: EcoString = TextNode::lang_in(styles).as_str().into(); + if let Some(region) = TextNode::region_in(styles) { bcp.push('-'); bcp.push_str(region.as_str()); } diff --git a/library/src/text/shift.rs b/library/src/text/shift.rs index c44cc3b08..98718365d 100644 --- a/library/src/text/shift.rs +++ b/library/src/text/shift.rs @@ -59,7 +59,7 @@ impl Show for SubNode { ) -> SourceResult { let body = self.body(); let mut transformed = None; - if styles.get(Self::TYPOGRAPHIC) { + if Self::typographic_in(styles) { if let Some(text) = search_text(&body, true) { if is_shapable(vt, &text, styles) { transformed = Some(TextNode::packed(text)); @@ -68,10 +68,8 @@ impl Show for SubNode { }; Ok(transformed.unwrap_or_else(|| { - let mut map = StyleMap::new(); - map.set(TextNode::BASELINE, styles.get(Self::BASELINE)); - map.set(TextNode::SIZE, styles.get(Self::SIZE)); - body.styled_with_map(map) + body.styled(TextNode::set_baseline(Self::baseline_in(styles))) + .styled(TextNode::set_size(Self::size_in(styles))) })) } } @@ -132,7 +130,7 @@ impl Show for SuperNode { ) -> SourceResult { let body = self.body(); let mut transformed = None; - if styles.get(Self::TYPOGRAPHIC) { + if Self::typographic_in(styles) { if let Some(text) = search_text(&body, false) { if is_shapable(vt, &text, styles) { transformed = Some(TextNode::packed(text)); @@ -141,10 +139,8 @@ impl Show for SuperNode { }; Ok(transformed.unwrap_or_else(|| { - let mut map = StyleMap::new(); - map.set(TextNode::BASELINE, styles.get(Self::BASELINE)); - map.set(TextNode::SIZE, styles.get(Self::SIZE)); - body.styled_with_map(map) + body.styled(TextNode::set_baseline(Self::baseline_in(styles))) + .styled(TextNode::set_size(Self::size_in(styles))) })) } } @@ -174,7 +170,7 @@ fn search_text(content: &Content, sub: bool) -> Option { /// given string. fn is_shapable(vt: &Vt, text: &str, styles: StyleChain) -> bool { let world = vt.world(); - for family in styles.get(TextNode::FONT).0.iter() { + for family in TextNode::font_in(styles) { if let Some(font) = world .book() .select(family.as_str(), variant(styles)) diff --git a/library/src/visualize/image.rs b/library/src/visualize/image.rs index fa5b70ad3..1bc864e57 100644 --- a/library/src/visualize/image.rs +++ b/library/src/visualize/image.rs @@ -90,7 +90,7 @@ impl Layout for ImageNode { }; // Compute the actual size of the fitted image. - let fit = styles.get(Self::FIT); + let fit = Self::fit_in(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 6fc7fc19f..6beb7bebf 100644 --- a/library/src/visualize/line.rs +++ b/library/src/visualize/line.rs @@ -84,7 +84,7 @@ impl Layout for LineNode { styles: StyleChain, regions: Regions, ) -> SourceResult { - let stroke = styles.get(Self::STROKE).unwrap_or_default(); + let stroke = Self::stroke_in(styles).unwrap_or_default(); let origin = self .start() diff --git a/library/src/visualize/shape.rs b/library/src/visualize/shape.rs index b3b9364ff..fc1d462e4 100644 --- a/library/src/visualize/shape.rs +++ b/library/src/visualize/shape.rs @@ -167,11 +167,11 @@ impl Layout for RectNode { ShapeKind::Rect, &self.body(), Axes::new(self.width(), self.height()), - styles.get(Self::FILL), - styles.get(Self::STROKE), - styles.get(Self::INSET), - styles.get(Self::OUTSET), - styles.get(Self::RADIUS), + Self::fill_in(styles), + Self::stroke_in(styles), + Self::inset_in(styles), + Self::outset_in(styles), + Self::radius_in(styles), ) } } @@ -301,11 +301,11 @@ impl Layout for SquareNode { ShapeKind::Square, &self.body(), Axes::new(self.width(), self.height()), - styles.get(Self::FILL), - styles.get(Self::STROKE), - styles.get(Self::INSET), - styles.get(Self::OUTSET), - styles.get(Self::RADIUS), + Self::fill_in(styles), + Self::stroke_in(styles), + Self::inset_in(styles), + Self::outset_in(styles), + Self::radius_in(styles), ) } } @@ -394,10 +394,10 @@ impl Layout for EllipseNode { ShapeKind::Ellipse, &self.body(), Axes::new(self.width(), self.height()), - styles.get(Self::FILL), - styles.get(Self::STROKE).map(Sides::splat), - styles.get(Self::INSET), - styles.get(Self::OUTSET), + Self::fill_in(styles), + Self::stroke_in(styles).map(Sides::splat), + Self::inset_in(styles), + Self::outset_in(styles), Corners::splat(Rel::zero()), ) } @@ -522,10 +522,10 @@ impl Layout for CircleNode { ShapeKind::Circle, &self.body(), Axes::new(self.width(), self.height()), - styles.get(Self::FILL), - styles.get(Self::STROKE).map(Sides::splat), - styles.get(Self::INSET), - styles.get(Self::OUTSET), + Self::fill_in(styles), + Self::stroke_in(styles).map(Sides::splat), + Self::inset_in(styles), + Self::outset_in(styles), Corners::splat(Rel::zero()), ) } diff --git a/macros/src/node.rs b/macros/src/node.rs index 8a6660ca1..a72988413 100644 --- a/macros/src/node.rs +++ b/macros/src/node.rs @@ -20,7 +20,9 @@ struct Field { attrs: Vec, vis: syn::Visibility, ident: Ident, + ident_in: Ident, with_ident: Ident, + set_ident: Ident, name: String, positional: bool, @@ -36,6 +38,7 @@ struct Field { skip: bool, ty: syn::Type, + output: syn::Type, default: Option, } @@ -62,20 +65,17 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result { let mut fields = vec![]; for field in &named.named { - let Some(mut ident) = field.ident.clone() else { + let Some(ident) = field.ident.clone() else { bail!(field, "expected named field"); }; let mut attrs = field.attrs.clone(); - let settable = has_attr(&mut attrs, "settable"); - if settable { - ident = Ident::new(&ident.to_string().to_uppercase(), ident.span()); - } - - let field = Field { + let mut field = Field { vis: field.vis.clone(), ident: ident.clone(), + ident_in: Ident::new(&format!("{}_in", ident), ident.span()), with_ident: Ident::new(&format!("with_{}", ident), ident.span()), + set_ident: Ident::new(&format!("set_{}", ident), ident.span()), name: kebab_case(&ident), positional: has_attr(&mut attrs, "positional"), @@ -88,12 +88,13 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result { Some(ident) => Shorthand::Named(ident), }), - settable, + settable: has_attr(&mut attrs, "settable"), fold: has_attr(&mut attrs, "fold"), resolve: has_attr(&mut attrs, "resolve"), skip: has_attr(&mut attrs, "skip"), ty: field.ty.clone(), + output: field.ty.clone(), default: parse_attr(&mut attrs, "default")?.map(|opt| { opt.unwrap_or_else(|| parse_quote! { ::std::default::Default::default() }) }), @@ -104,6 +105,15 @@ fn prepare(stream: TokenStream, body: &syn::ItemStruct) -> Result { }, }; + if field.resolve { + let output = &field.output; + field.output = parse_quote! { <#output as ::typst::model::Resolve>::Output }; + } + if field.fold { + let output = &field.output; + field.output = parse_quote! { <#output as ::typst::model::Fold>::Output }; + } + if !field.positional && !field.named && !field.variadic && !field.settable { bail!(ident, "expected positional, named, variadic, or settable"); } @@ -140,34 +150,22 @@ fn create(node: &Node) -> TokenStream { let attrs = &node.attrs; let vis = &node.vis; let ident = &node.ident; - let name = &node.name; + + // Inherent methods and functions. let new = create_new_func(node); + let field_methods = node.inherent().map(create_field_method); + let with_fields_methods = node.inherent().map(create_with_field_method); + let field_in_methods = node.settable().map(create_field_in_method); + let field_style_methods = node.settable().map(create_field_style_method); + + // Trait implementations. let construct = node .capable .iter() .all(|capability| capability != "Construct") .then(|| create_construct_impl(node)); let set = create_set_impl(node); - let builders = node.inherent().map(create_builder_method); - let accessors = node.inherent().map(create_accessor_method); - let vtable = create_vtable(node); - - let mut modules = vec![]; - let mut items = vec![]; - let scope = quote::format_ident!("__{}_keys", ident); - - for field in node.settable() { - let ident = &field.ident; - let attrs = &field.attrs; - let vis = &field.vis; - let ty = &field.ty; - modules.push(create_field_module(node, field)); - items.push(quote! { - #(#attrs)* - #vis const #ident: #scope::#ident::Key<#ty> - = #scope::#ident::Key(::std::marker::PhantomData); - }); - } + let node = create_node_impl(node); quote! { #(#attrs)* @@ -178,7 +176,10 @@ fn create(node: &Node) -> TokenStream { impl #ident { #new - #(#builders)* + #(#field_methods)* + #(#with_fields_methods)* + #(#field_in_methods)* + #(#field_style_methods)* /// The node's span. pub fn span(&self) -> Option<::typst::syntax::Span> { @@ -186,25 +187,7 @@ fn create(node: &Node) -> TokenStream { } } - impl #ident { - #(#accessors)* - #(#items)* - } - - impl ::typst::model::Node for #ident { - fn id() -> ::typst::model::NodeId { - static META: ::typst::model::NodeMeta = ::typst::model::NodeMeta { - name: #name, - vtable: #vtable, - }; - ::typst::model::NodeId::from_meta(&META) - } - - fn pack(self) -> ::typst::model::Content { - self.0 - } - } - + #node #construct #set @@ -213,12 +196,6 @@ fn create(node: &Node) -> TokenStream { value.0.into() } } - - #[allow(non_snake_case)] - mod #scope { - use super::*; - #(#modules)* - } } } @@ -252,20 +229,8 @@ fn create_new_func(node: &Node) -> TokenStream { } } -/// Create a builder pattern method for a field. -fn create_builder_method(field: &Field) -> TokenStream { - let Field { with_ident, ident, name, ty, .. } = field; - let doc = format!("Set the [`{}`](Self::{}) field.", name, ident); - quote! { - #[doc = #doc] - pub fn #with_ident(mut self, #ident: #ty) -> Self { - Self(self.0.with_field(#name, #ident)) - } - } -} - /// Create an accessor methods for a field. -fn create_accessor_method(field: &Field) -> TokenStream { +fn create_field_method(field: &Field) -> TokenStream { let Field { attrs, vis, ident, name, ty, .. } = field; quote! { #(#attrs)* @@ -275,6 +240,145 @@ fn create_accessor_method(field: &Field) -> TokenStream { } } +/// Create a builder pattern method for a field. +fn create_with_field_method(field: &Field) -> TokenStream { + let Field { vis, ident, with_ident, name, ty, .. } = field; + let doc = format!("Set the [`{}`](Self::{}) field.", name, ident); + quote! { + #[doc = #doc] + #vis fn #with_ident(mut self, #ident: #ty) -> Self { + Self(self.0.with_field(#name, #ident)) + } + } +} + +/// Create a style chain access method for a field. +fn create_field_in_method(field: &Field) -> TokenStream { + let Field { vis, ident_in, name, ty, output, default, .. } = field; + + let doc = format!("Access the `{}` field in the given style chain.", name); + let args = quote! { ::typst::model::NodeId::of::(), #name }; + + let body = if field.fold && field.resolve { + quote! { + fn next( + mut values: impl ::std::iter::Iterator, + styles: ::typst::model::StyleChain, + ) -> #output { + values + .next() + .map(|value| { + ::typst::model::Fold::fold( + ::typst::model::Resolve::resolve(value, styles), + next(values, styles), + ) + }) + .unwrap_or_else(|| #default) + } + next(styles.properties(#args), styles) + } + } else if field.fold { + quote! { + fn next( + mut values: impl ::std::iter::Iterator, + styles: ::typst::model::StyleChain, + ) -> #output { + values + .next() + .map(|value| { + ::typst::model::Fold::fold(value, next(values, styles)) + }) + .unwrap_or_else(|| #default) + } + next(styles.properties(#args), styles) + } + } else if field.resolve { + quote! { + ::typst::model::Resolve::resolve( + styles.property::<#ty>(#args).unwrap_or_else(|| #default), + styles + ) + } + } else { + quote! { + styles.property(#args).unwrap_or_else(|| #default) + } + }; + + quote! { + #[doc = #doc] + #[allow(unused)] + #vis fn #ident_in(styles: ::typst::model::StyleChain) -> #output { + #body + } + } +} + +/// Create a style creation method for a field. +fn create_field_style_method(field: &Field) -> TokenStream { + let Field { vis, ident, set_ident, name, ty, .. } = field; + let doc = format!("Create a style property for the `{}` field.", name); + quote! { + #[doc = #doc] + #vis fn #set_ident(#ident: #ty) -> ::typst::model::Property { + ::typst::model::Property::new( + ::typst::model::NodeId::of::(), + #name.into(), + #ident.into() + ) + } + } +} + +/// Create the node's `Node` implementation. +fn create_node_impl(node: &Node) -> TokenStream { + let ident = &node.ident; + let name = &node.name; + let vtable_func = create_vtable_func(node); + quote! { + impl ::typst::model::Node for #ident { + fn id() -> ::typst::model::NodeId { + static META: ::typst::model::NodeMeta = ::typst::model::NodeMeta { + name: #name, + vtable: #vtable_func, + }; + ::typst::model::NodeId::from_meta(&META) + } + + fn pack(self) -> ::typst::model::Content { + self.0 + } + } + } +} + +/// Create the node's metadata vtable. +fn create_vtable_func(node: &Node) -> TokenStream { + let ident = &node.ident; + let checks = + node.capable + .iter() + .filter(|&ident| ident != "Construct") + .map(|capability| { + quote! { + if id == ::std::any::TypeId::of::() { + return Some(unsafe { + ::typst::util::fat::vtable(& + Self(::typst::model::Content::new::<#ident>()) as &dyn #capability + ) + }); + } + } + }); + + quote! { + |id| { + #(#checks)* + None + } + } +} + /// Create the node's `Construct` implementation. fn create_construct_impl(node: &Node) -> TokenStream { let ident = &node.ident; @@ -353,8 +457,8 @@ fn create_set_impl(node: &Node) -> TokenStream { .settable() .filter(|field| !field.skip) .map(|field| { - let ident = &field.ident; let name = &field.name; + let set_ident = &field.set_ident; let value = match &field.shorthand { Some(Shorthand::Positional) => quote! { args.named_or_find(#name)? }, Some(Shorthand::Named(named)) => { @@ -364,7 +468,7 @@ fn create_set_impl(node: &Node) -> TokenStream { None => quote! { args.named(#name)? }, }; - quote! { styles.set_opt(Self::#ident, #value); } + quote! { styles.set_opt(#value.map(Self::#set_ident)); } }) .collect(); @@ -417,123 +521,3 @@ fn create_set_impl(node: &Node) -> TokenStream { } } } - -/// Create the module for a single field. -fn create_field_module(node: &Node, field: &Field) -> TokenStream { - let node_ident = &node.ident; - let ident = &field.ident; - let name = &field.name; - let ty = &field.ty; - let default = &field.default; - - let mut output = quote! { #ty }; - if field.resolve { - output = quote! { <#output as ::typst::model::Resolve>::Output }; - } - if field.fold { - output = quote! { <#output as ::typst::model::Fold>::Output }; - } - - let value = if field.resolve && field.fold { - quote! { - values - .next() - .map(|value| { - ::typst::model::Fold::fold( - ::typst::model::Resolve::resolve(value, chain), - Self::get(chain, values), - ) - }) - .unwrap_or(#default) - } - } else if field.resolve { - quote! { - ::typst::model::Resolve::resolve( - values.next().unwrap_or(#default), - chain - ) - } - } else if field.fold { - quote! { - values - .next() - .map(|value| { - ::typst::model::Fold::fold( - value, - Self::get(chain, values), - ) - }) - .unwrap_or(#default) - } - } else { - quote! { - values.next().unwrap_or(#default) - } - }; - - // Generate the contents of the module. - let scope = quote! { - use super::*; - - pub struct Key(pub ::std::marker::PhantomData); - impl ::std::marker::Copy for Key<#ty> {} - impl ::std::clone::Clone for Key<#ty> { - fn clone(&self) -> Self { *self } - } - - impl ::typst::model::Key for Key<#ty> { - type Value = #ty; - type Output = #output; - - fn id() -> ::typst::model::KeyId { - static META: ::typst::model::KeyMeta = ::typst::model::KeyMeta { - name: #name, - }; - ::typst::model::KeyId::from_meta(&META) - } - - fn node() -> ::typst::model::NodeId { - ::typst::model::NodeId::of::<#node_ident>() - } - - fn get( - chain: ::typst::model::StyleChain, - mut values: impl ::std::iter::Iterator, - ) -> Self::Output { - #value - } - } - }; - - // Generate the module code. - quote! { - pub mod #ident { #scope } - } -} - -/// Create the node's metadata vtable. -fn create_vtable(node: &Node) -> TokenStream { - let ident = &node.ident; - let checks = - node.capable - .iter() - .filter(|&ident| ident != "Construct") - .map(|capability| { - quote! { - if id == ::std::any::TypeId::of::() { - return Some(unsafe { - ::typst::util::fat::vtable(& - Self(::typst::model::Content::new::<#ident>()) as &dyn #capability - ) - }); - } - } - }); - - quote! { - |id| { - #(#checks)* - None - } - } -} diff --git a/src/doc.rs b/src/doc.rs index 0c01dcd26..ce6f4c96e 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -274,7 +274,7 @@ impl Frame { if self.is_empty() { return; } - for meta in styles.get(MetaNode::DATA) { + for meta in MetaNode::data_in(styles) { if matches!(meta, Meta::Hidden) { self.clear(); break; diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 8180f11de..0e0828e38 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -354,7 +354,7 @@ fn eval_markup( } let tail = eval_markup(vm, exprs)?; - seq.push(tail.styled_with_recipe(vm.world, recipe)?) + seq.push(tail.apply_recipe(vm.world, recipe)?) } expr => match expr.eval(vm)? { Value::Label(label) => { @@ -783,7 +783,7 @@ fn eval_code( } let tail = eval_code(vm, exprs)?.display(); - Value::Content(tail.styled_with_recipe(vm.world, recipe)?) + Value::Content(tail.apply_recipe(vm.world, recipe)?) } _ => expr.eval(vm)?, }; diff --git a/src/model/content.rs b/src/model/content.rs index 1b87aaea3..05c5d430a 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -7,7 +7,7 @@ use std::ops::{Add, AddAssign}; use comemo::Tracked; use ecow::{EcoString, EcoVec}; -use super::{node, Guard, Key, Property, Recipe, Style, StyleMap}; +use super::{node, Guard, Recipe, Style, StyleMap}; use crate::diag::{SourceResult, StrResult}; use crate::eval::{cast_from_value, Args, Cast, ParamInfo, Value, Vm}; use crate::syntax::Span; @@ -66,14 +66,9 @@ impl Content { self.with_field("label", label) } - /// Style this content with a single style property. - pub fn styled(self, key: K, value: K::Value) -> Self { - self.styled_with_entry(Style::Property(Property::new(key, value))) - } - /// Style this content with a style entry. - pub fn styled_with_entry(self, style: Style) -> Self { - self.styled_with_map(style.into()) + pub fn styled(self, style: impl Into