diff --git a/crates/typst-macros/src/elem.rs b/crates/typst-macros/src/elem.rs index 3a31d3ef0..eab359d6f 100644 --- a/crates/typst-macros/src/elem.rs +++ b/crates/typst-macros/src/elem.rs @@ -866,6 +866,28 @@ fn create_fields_impl(element: &Elem) -> TokenStream { quote! { Fields::#enum_ident => #expr } }); + // Sets fields from the style chain. + let materializes = visible_non_ghost() + .filter(|field| !field.required && !field.synthesized) + .map(|field| { + let Field { ident, .. } = field; + let value = create_style_chain_access( + field, + false, + if field.ghost { quote!(None) } else { quote!(self.#ident.as_ref()) }, + ); + + if field.fold { + quote! { self.#ident = Some(#value); } + } else { + quote! { + if self.#ident.is_none() { + self.#ident = Some(#value); + } + } + } + }); + // Creation of the `fields` dictionary for inherent fields. let field_inserts = visible_non_ghost().map(|field| { let Field { ident, name, .. } = field; @@ -917,6 +939,10 @@ fn create_fields_impl(element: &Elem) -> TokenStream { } } + fn materialize(&mut self, styles: #foundations::StyleChain) { + #(#materializes)* + } + fn fields(&self) -> #foundations::Dict { let mut fields = #foundations::Dict::new(); #(#field_inserts)* diff --git a/crates/typst/src/foundations/content.rs b/crates/typst/src/foundations/content.rs index 49497b8ff..6f912086e 100644 --- a/crates/typst/src/foundations/content.rs +++ b/crates/typst/src/foundations/content.rs @@ -14,11 +14,10 @@ use smallvec::smallvec; use crate::diag::{SourceResult, StrResult}; use crate::engine::Engine; use crate::foundations::{ - elem, func, scope, ty, Dict, Element, Fields, Finalize, Guard, IntoValue, Label, - NativeElement, Recipe, Repr, Selector, Str, Style, StyleChain, Styles, Synthesize, - Value, + elem, func, scope, ty, Dict, Element, Fields, Guard, IntoValue, Label, NativeElement, + Recipe, Repr, Selector, Str, Style, StyleChain, Styles, Value, }; -use crate::introspection::{Locatable, Location, Meta, MetaElem}; +use crate::introspection::{Location, Meta, MetaElem}; use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides}; use crate::model::{Destination, EmphElem, StrongElem}; use crate::syntax::Span; @@ -142,6 +141,16 @@ impl Content { self } + /// Check whether a show rule recipe is disabled. + pub fn is_guarded(&self, guard: Guard) -> bool { + self.inner.lifecycle.contains(guard.0) + } + + /// Whether this content has already been prepared. + pub fn is_prepared(&self) -> bool { + self.inner.lifecycle.contains(0) + } + /// Set the location of the content. pub fn set_location(&mut self, location: Location) { self.make_mut().location = Some(location); @@ -153,25 +162,6 @@ impl Content { self } - /// Whether the content needs to be realized specially. - pub fn needs_preparation(&self) -> bool { - !self.is_prepared() - && (self.can::() - || self.can::() - || self.can::() - || self.label().is_some()) - } - - /// Check whether a show rule recipe is disabled. - pub fn is_guarded(&self, guard: Guard) -> bool { - self.inner.lifecycle.contains(guard.0) - } - - /// Whether this content has already been prepared. - pub fn is_prepared(&self) -> bool { - self.inner.lifecycle.contains(0) - } - /// Mark this content as prepared. pub fn mark_prepared(&mut self) { self.make_mut().lifecycle.insert(0); @@ -227,6 +217,11 @@ impl Content { self.get_by_name(name).ok_or_else(|| missing_field(name)) } + /// Resolve all fields with the styles and save them in-place. + pub fn materialize(&mut self, styles: StyleChain) { + self.make_mut().elem.materialize(styles); + } + /// Create a new sequence element from multiples elements. pub fn sequence(iter: impl IntoIterator) -> Self { let mut iter = iter.into_iter(); diff --git a/crates/typst/src/foundations/element.rs b/crates/typst/src/foundations/element.rs index 489077e4a..6d73a896d 100644 --- a/crates/typst/src/foundations/element.rs +++ b/crates/typst/src/foundations/element.rs @@ -223,6 +223,9 @@ pub trait Fields { /// Get the field with the given ID in the presence of styles. fn field_with_styles(&self, id: u8, styles: StyleChain) -> Option; + /// Resolve all fields with the styles and save them in-place. + fn materialize(&mut self, styles: StyleChain); + /// Get the fields of the element. fn fields(&self) -> Dict; } @@ -282,17 +285,20 @@ pub trait Synthesize { -> SourceResult<()>; } -/// The base recipe for an element. +/// Defines a built-in show rule for an element. pub trait Show { /// Execute the base recipe for this element. fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult; } -/// Post-process an element after it was realized. -pub trait Finalize { +/// Defines built-in show set rules for an element. +/// +/// This is a bit more powerful than a user-defined show-set because it can +/// access the element's fields. +pub trait ShowSet { /// Finalize the fully realized form of the element. Use this for effects /// that should work even in the face of a user-defined show rule. - fn finalize(&self, realized: Content, styles: StyleChain) -> Content; + fn show_set(&self, styles: StyleChain) -> Styles; } /// How the element interacts with other elements. diff --git a/crates/typst/src/foundations/styles.rs b/crates/typst/src/foundations/styles.rs index 6946d076d..12ba28763 100644 --- a/crates/typst/src/foundations/styles.rs +++ b/crates/typst/src/foundations/styles.rs @@ -147,9 +147,15 @@ impl Styles { } } +impl From> for Styles { + fn from(style: Prehashed