Scoped styles
This commit is contained in:
parent
af014cfe5e
commit
0b62439090
@ -17,37 +17,34 @@ pub fn properties(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// Expand a property impl block for a node.
|
||||
fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
|
||||
// Split the node type into name and generic type arguments.
|
||||
let (self_name, self_args) = parse_self(&*impl_block.self_ty)?;
|
||||
let self_ty = &*impl_block.self_ty;
|
||||
let (self_name, self_args) = parse_self(self_ty)?;
|
||||
|
||||
// Rewrite the const items from values to keys.
|
||||
let mut style_ids = vec![];
|
||||
let mut modules = vec![];
|
||||
for item in &mut impl_block.items {
|
||||
if let syn::ImplItem::Const(item) = item {
|
||||
let (style_id, module) = process_const(item, &self_name, &self_args)?;
|
||||
style_ids.push(style_id);
|
||||
let module = process_const(
|
||||
item,
|
||||
&impl_block.generics,
|
||||
self_ty,
|
||||
&self_name,
|
||||
&self_args,
|
||||
)?;
|
||||
modules.push(module);
|
||||
}
|
||||
}
|
||||
|
||||
// Here, we use the collected `style_ids` to provide a function that checks
|
||||
// whether a property belongs to the node.
|
||||
impl_block.items.insert(0, parse_quote! {
|
||||
/// Check whether the property with the given type id belongs to `Self`.
|
||||
pub fn has_property(id: StyleId) -> bool {
|
||||
[#(#style_ids),*].contains(&id)
|
||||
}
|
||||
});
|
||||
|
||||
// Put everything into a module with a hopefully unique type to isolate
|
||||
// it from the outside.
|
||||
let module = quote::format_ident!("{}_types", self_name);
|
||||
Ok(quote! {
|
||||
#[allow(non_snake_case)]
|
||||
mod #module {
|
||||
use std::any::TypeId;
|
||||
use std::marker::PhantomData;
|
||||
use once_cell::sync::Lazy;
|
||||
use crate::eval::{Nonfolding, Property, StyleId};
|
||||
use crate::eval::{Nonfolding, Property};
|
||||
use super::*;
|
||||
|
||||
#impl_block
|
||||
@ -85,19 +82,21 @@ fn parse_self(self_ty: &syn::Type) -> Result<(String, Vec<&syn::Type>)> {
|
||||
/// Process a single const item.
|
||||
fn process_const(
|
||||
item: &mut syn::ImplItemConst,
|
||||
impl_generics: &syn::Generics,
|
||||
self_ty: &syn::Type,
|
||||
self_name: &str,
|
||||
self_args: &[&syn::Type],
|
||||
) -> Result<(syn::Expr, syn::ItemMod)> {
|
||||
) -> Result<syn::ItemMod> {
|
||||
// The module that will contain the `Key` type.
|
||||
let module_name = &item.ident;
|
||||
|
||||
// The type of the property's value is what the user of our macro wrote
|
||||
// as type of the const ...
|
||||
let value_ty = &item.ty;
|
||||
|
||||
// ... but the real type of the const becomes Key<#key_param>.
|
||||
let key_param = if self_args.is_empty() {
|
||||
quote! { #value_ty }
|
||||
} else {
|
||||
quote! { (#value_ty, #(#self_args),*) }
|
||||
};
|
||||
// ... but the real type of the const becomes Key<#key_args>.
|
||||
let key_params = &impl_generics.params;
|
||||
let key_args = quote! { #value_ty #(, #self_args)* };
|
||||
|
||||
// The display name, e.g. `TextNode::STRONG`.
|
||||
let name = format!("{}::{}", self_name, &item.ident);
|
||||
@ -108,7 +107,7 @@ fn process_const(
|
||||
|
||||
let mut folder = None;
|
||||
let mut nonfolding = Some(quote! {
|
||||
impl<T: 'static> Nonfolding for Key<T> {}
|
||||
impl<#key_params> Nonfolding for Key<#key_args> {}
|
||||
});
|
||||
|
||||
// Look for a folding function like `#[fold(u64::add)]`.
|
||||
@ -127,54 +126,50 @@ fn process_const(
|
||||
}
|
||||
}
|
||||
|
||||
// The implementation of the `Property` trait.
|
||||
let property_impl = quote! {
|
||||
impl<T: 'static> Property for Key<T> {
|
||||
type Value = #value_ty;
|
||||
|
||||
const NAME: &'static str = #name;
|
||||
|
||||
fn default() -> Self::Value {
|
||||
#default
|
||||
}
|
||||
|
||||
fn default_ref() -> &'static Self::Value {
|
||||
static LAZY: Lazy<#value_ty> = Lazy::new(|| #default);
|
||||
&*LAZY
|
||||
}
|
||||
|
||||
#folder
|
||||
}
|
||||
|
||||
#nonfolding
|
||||
};
|
||||
|
||||
// The module that will contain the `Key` type.
|
||||
let module_name = &item.ident;
|
||||
|
||||
// Generate the style id and module code.
|
||||
let style_id = parse_quote! { StyleId::of::<#module_name::Key<#key_param>>() };
|
||||
// Generate the module code.
|
||||
let module = parse_quote! {
|
||||
#[allow(non_snake_case)]
|
||||
mod #module_name {
|
||||
use super::*;
|
||||
|
||||
pub struct Key<T>(pub PhantomData<T>);
|
||||
impl<T> Copy for Key<T> {}
|
||||
impl<T> Clone for Key<T> {
|
||||
pub struct Key<T, #key_params>(pub PhantomData<(T, #key_args)>);
|
||||
|
||||
impl<#key_params> Copy for Key<#key_args> {}
|
||||
impl<#key_params> Clone for Key<#key_args> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
#property_impl
|
||||
impl<#key_params> Property for Key<#key_args> {
|
||||
type Value = #value_ty;
|
||||
|
||||
const NAME: &'static str = #name;
|
||||
|
||||
fn node_id() -> TypeId {
|
||||
TypeId::of::<#self_ty>()
|
||||
}
|
||||
|
||||
fn default() -> Self::Value {
|
||||
#default
|
||||
}
|
||||
|
||||
fn default_ref() -> &'static Self::Value {
|
||||
static LAZY: Lazy<#value_ty> = Lazy::new(|| #default);
|
||||
&*LAZY
|
||||
}
|
||||
|
||||
#folder
|
||||
}
|
||||
|
||||
#nonfolding
|
||||
}
|
||||
};
|
||||
|
||||
// Replace type and initializer expression with the `Key`.
|
||||
item.attrs.retain(|attr| !attr.path.is_ident("fold"));
|
||||
item.ty = parse_quote! { #module_name::Key<#key_param> };
|
||||
item.ty = parse_quote! { #module_name::Key<#key_args> };
|
||||
item.expr = parse_quote! { #module_name::Key(PhantomData) };
|
||||
|
||||
Ok((style_id, module))
|
||||
Ok(module)
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ impl Class {
|
||||
let mut styles = StyleMap::new();
|
||||
self.set(args, &mut styles)?;
|
||||
let node = (self.construct)(ctx, args)?;
|
||||
Ok(node.styled_with_map(styles))
|
||||
Ok(node.styled_with_map(styles.scoped()))
|
||||
}
|
||||
|
||||
/// Execute the class's set rule.
|
||||
|
@ -302,7 +302,7 @@ impl Packer {
|
||||
if let Some(Styled { item: ParChild::Text(left), map }) =
|
||||
self.par.children.last_mut()
|
||||
{
|
||||
if child.map.compatible(map, TextNode::has_property) {
|
||||
if child.map.compatible::<TextNode>(map) {
|
||||
left.0.push_str(&right.0);
|
||||
return;
|
||||
}
|
||||
@ -380,7 +380,7 @@ impl Packer {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.par.styles.compatible(styles, ParNode::has_property) {
|
||||
if !self.par.styles.compatible::<ParNode>(styles) {
|
||||
self.parbreak(None);
|
||||
self.par.styles = styles.clone();
|
||||
return;
|
||||
@ -397,7 +397,7 @@ impl Packer {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.top && !self.flow.styles.compatible(styles, PageNode::has_property) {
|
||||
if self.top && !self.flow.styles.compatible::<PageNode>(styles) {
|
||||
self.pagebreak();
|
||||
self.flow.styles = styles.clone();
|
||||
return;
|
||||
|
@ -100,6 +100,16 @@ impl StyleMap {
|
||||
self.0.push(Entry::new(key, true));
|
||||
}
|
||||
|
||||
/// Mark all contained properties as _scoped_. This means that they only
|
||||
/// apply to the first descendant node (of their type) in the hierarchy and
|
||||
/// not its children, too. This is used by class constructors.
|
||||
pub fn scoped(mut self) -> Self {
|
||||
for entry in &mut self.0 {
|
||||
entry.scoped = true;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Make `self` the first link of the style chain `outer`.
|
||||
///
|
||||
/// The resulting style chain contains styles from `self` as well as
|
||||
@ -109,7 +119,10 @@ impl StyleMap {
|
||||
if self.is_empty() {
|
||||
*outer
|
||||
} else {
|
||||
StyleChain { inner: self, outer: Some(outer) }
|
||||
StyleChain {
|
||||
first: Link::Map(self),
|
||||
outer: Some(outer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,9 +135,7 @@ impl StyleMap {
|
||||
/// immutable style maps from different levels of the hierarchy.
|
||||
pub fn apply(&mut self, outer: &Self) {
|
||||
for outer in &outer.0 {
|
||||
if let Some(inner) =
|
||||
self.0.iter_mut().find(|inner| inner.style_id() == outer.style_id())
|
||||
{
|
||||
if let Some(inner) = self.0.iter_mut().find(|inner| inner.is_same(outer)) {
|
||||
*inner = inner.fold(outer);
|
||||
continue;
|
||||
}
|
||||
@ -145,13 +156,10 @@ impl StyleMap {
|
||||
self.0.retain(|x| other.0.contains(x));
|
||||
}
|
||||
|
||||
/// Whether two style maps are equal when filtered down to the given
|
||||
/// properties.
|
||||
pub fn compatible<F>(&self, other: &Self, filter: F) -> bool
|
||||
where
|
||||
F: Fn(StyleId) -> bool,
|
||||
{
|
||||
let f = |entry: &&Entry| filter(entry.style_id());
|
||||
/// Whether two style maps are equal when filtered down to properties of the
|
||||
/// node `T`.
|
||||
pub fn compatible<T: 'static>(&self, other: &Self) -> bool {
|
||||
let f = |entry: &&Entry| entry.is_of::<T>();
|
||||
self.0.iter().filter(f).count() == other.0.iter().filter(f).count()
|
||||
&& self.0.iter().filter(f).all(|x| other.0.contains(x))
|
||||
}
|
||||
@ -168,7 +176,7 @@ impl Debug for StyleMap {
|
||||
|
||||
impl PartialEq for StyleMap {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.compatible(other, |_| true)
|
||||
self.0.len() == other.0.len() && self.0.iter().all(|x| other.0.contains(x))
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,15 +190,22 @@ impl PartialEq for StyleMap {
|
||||
#[derive(Clone, Copy, Hash)]
|
||||
pub struct StyleChain<'a> {
|
||||
/// The first map in the chain.
|
||||
inner: &'a StyleMap,
|
||||
first: Link<'a>,
|
||||
/// The remaining maps in the chain.
|
||||
outer: Option<&'a Self>,
|
||||
}
|
||||
|
||||
/// The two kinds of links in the chain.
|
||||
#[derive(Clone, Copy, Hash)]
|
||||
enum Link<'a> {
|
||||
Map(&'a StyleMap),
|
||||
Barrier(TypeId),
|
||||
}
|
||||
|
||||
impl<'a> StyleChain<'a> {
|
||||
/// Start a new style chain with a root map.
|
||||
pub fn new(map: &'a StyleMap) -> Self {
|
||||
Self { inner: map, outer: None }
|
||||
Self { first: Link::Map(map), outer: None }
|
||||
}
|
||||
|
||||
/// Get the (folded) value of a copyable style property.
|
||||
@ -205,7 +220,7 @@ impl<'a> StyleChain<'a> {
|
||||
where
|
||||
P::Value: Copy,
|
||||
{
|
||||
self.get_cloned(key)
|
||||
self.get_impl(key, 0)
|
||||
}
|
||||
|
||||
/// Get a reference to a style property's value.
|
||||
@ -220,13 +235,7 @@ impl<'a> StyleChain<'a> {
|
||||
where
|
||||
P: Nonfolding,
|
||||
{
|
||||
if let Some(value) = self.find(key) {
|
||||
value
|
||||
} else if let Some(outer) = self.outer {
|
||||
outer.get_ref(key)
|
||||
} else {
|
||||
P::default_ref()
|
||||
}
|
||||
self.get_ref_impl(key, 0)
|
||||
}
|
||||
|
||||
/// Get the (folded) value of any style property.
|
||||
@ -238,35 +247,87 @@ impl<'a> StyleChain<'a> {
|
||||
/// Returns the property's default value if no map in the chain contains an
|
||||
/// entry for it.
|
||||
pub fn get_cloned<P: Property>(self, key: P) -> P::Value {
|
||||
if let Some(value) = self.find(key).cloned() {
|
||||
if !P::FOLDABLE {
|
||||
return value;
|
||||
}
|
||||
self.get_impl(key, 0)
|
||||
}
|
||||
|
||||
match self.outer {
|
||||
Some(outer) => P::fold(value, outer.get_cloned(key)),
|
||||
None => P::fold(value, P::default()),
|
||||
/// Insert a barrier into the style chain.
|
||||
///
|
||||
/// Barriers interact with [scoped](StyleMap::scoped) styles: A scoped style
|
||||
/// can still be read through a single barrier (the one of the node it
|
||||
/// _should_ apply to), but a second barrier will make it invisible.
|
||||
pub fn barred<'b>(&'b self, node: TypeId) -> StyleChain<'b> {
|
||||
if self.needs_barrier(node) {
|
||||
StyleChain {
|
||||
first: Link::Barrier(node),
|
||||
outer: Some(self),
|
||||
}
|
||||
} else {
|
||||
*self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StyleChain<'a> {
|
||||
fn get_impl<P: Property>(self, key: P, depth: usize) -> P::Value {
|
||||
let (value, depth) = self.process(key, depth);
|
||||
if let Some(value) = value.cloned() {
|
||||
if P::FOLDABLE {
|
||||
if let Some(outer) = self.outer {
|
||||
P::fold(value, outer.get_cloned(key))
|
||||
} else {
|
||||
P::fold(value, P::default())
|
||||
}
|
||||
} else {
|
||||
value
|
||||
}
|
||||
} else if let Some(outer) = self.outer {
|
||||
outer.get_cloned(key)
|
||||
outer.get_impl(key, depth)
|
||||
} else {
|
||||
P::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Find a property directly in the localmost map.
|
||||
fn find<P: Property>(self, _: P) -> Option<&'a P::Value> {
|
||||
self.inner
|
||||
.0
|
||||
.iter()
|
||||
.find(|entry| entry.is::<P>())
|
||||
.and_then(|entry| entry.downcast::<P>())
|
||||
fn get_ref_impl<P: Property>(self, key: P, depth: usize) -> &'a P::Value
|
||||
where
|
||||
P: Nonfolding,
|
||||
{
|
||||
let (value, depth) = self.process(key, depth);
|
||||
if let Some(value) = value {
|
||||
value
|
||||
} else if let Some(outer) = self.outer {
|
||||
outer.get_ref_impl(key, depth)
|
||||
} else {
|
||||
P::default_ref()
|
||||
}
|
||||
}
|
||||
|
||||
fn process<P: Property>(self, _: P, depth: usize) -> (Option<&'a P::Value>, usize) {
|
||||
match self.first {
|
||||
Link::Map(map) => (
|
||||
map.0
|
||||
.iter()
|
||||
.find(|entry| entry.is::<P>() && (!entry.scoped || depth <= 1))
|
||||
.and_then(|entry| entry.downcast::<P>()),
|
||||
depth,
|
||||
),
|
||||
Link::Barrier(node) => (None, depth + (P::node_id() == node) as usize),
|
||||
}
|
||||
}
|
||||
|
||||
fn needs_barrier(self, node: TypeId) -> bool {
|
||||
if let Link::Map(map) = self.first {
|
||||
if map.0.iter().any(|entry| entry.is_of_same(node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
self.outer.map_or(false, |outer| outer.needs_barrier(node))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for StyleChain<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.inner.fmt(f)?;
|
||||
self.first.fmt(f)?;
|
||||
if let Some(outer) = self.outer {
|
||||
outer.fmt(f)?;
|
||||
}
|
||||
@ -274,14 +335,67 @@ impl Debug for StyleChain<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A unique identifier for a style property.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct StyleId(TypeId);
|
||||
impl Debug for Link<'_> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Map(map) => map.fmt(f),
|
||||
Self::Barrier(id) => writeln!(f, "Barrier({:?})", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleId {
|
||||
/// The style id of the property.
|
||||
pub fn of<P: Property>() -> Self {
|
||||
Self(TypeId::of::<P>())
|
||||
/// An entry for a single style property.
|
||||
#[derive(Clone)]
|
||||
struct Entry {
|
||||
p: Rc<dyn Bounds>,
|
||||
scoped: bool,
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
fn new<P: Property>(key: P, value: P::Value) -> Self {
|
||||
Self { p: Rc::new((key, value)), scoped: false }
|
||||
}
|
||||
|
||||
fn is<P: Property>(&self) -> bool {
|
||||
self.p.style_id() == TypeId::of::<P>()
|
||||
}
|
||||
|
||||
fn is_same(&self, other: &Self) -> bool {
|
||||
self.p.style_id() == other.p.style_id()
|
||||
}
|
||||
|
||||
fn is_of<T: 'static>(&self) -> bool {
|
||||
self.p.node_id() == TypeId::of::<T>()
|
||||
}
|
||||
|
||||
fn is_of_same(&self, node: TypeId) -> bool {
|
||||
self.p.node_id() == node
|
||||
}
|
||||
|
||||
fn downcast<P: Property>(&self) -> Option<&P::Value> {
|
||||
self.p.as_any().downcast_ref()
|
||||
}
|
||||
|
||||
fn fold(&self, outer: &Self) -> Self {
|
||||
self.p.fold(outer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Entry {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.p.dyn_fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Entry {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.p.dyn_eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Entry {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.p.hash64());
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,6 +412,12 @@ pub trait Property: Copy + 'static {
|
||||
/// The name of the property, used for debug printing.
|
||||
const NAME: &'static str;
|
||||
|
||||
/// Whether the property needs folding.
|
||||
const FOLDABLE: bool = false;
|
||||
|
||||
/// The type id of the node this property belongs to.
|
||||
fn node_id() -> TypeId;
|
||||
|
||||
/// The default value of the property.
|
||||
fn default() -> Self::Value;
|
||||
|
||||
@ -308,9 +428,6 @@ pub trait Property: Copy + 'static {
|
||||
/// recreated all the time.
|
||||
fn default_ref() -> &'static Self::Value;
|
||||
|
||||
/// Whether the property needs folding.
|
||||
const FOLDABLE: bool = false;
|
||||
|
||||
/// Fold the property with an outer value.
|
||||
///
|
||||
/// For example, this would fold a relative font size with an outer
|
||||
@ -324,74 +441,21 @@ pub trait Property: Copy + 'static {
|
||||
/// Marker trait that indicates that a property doesn't need folding.
|
||||
pub trait Nonfolding {}
|
||||
|
||||
/// An entry for a single style property.
|
||||
#[derive(Clone)]
|
||||
struct Entry(Rc<dyn Bounds>);
|
||||
|
||||
impl Entry {
|
||||
fn new<P: Property>(key: P, value: P::Value) -> Self {
|
||||
Self(Rc::new((key, value)))
|
||||
}
|
||||
|
||||
fn style_id(&self) -> StyleId {
|
||||
self.0.style_id()
|
||||
}
|
||||
|
||||
fn is<P: Property>(&self) -> bool {
|
||||
self.style_id() == StyleId::of::<P>()
|
||||
}
|
||||
|
||||
fn downcast<P: Property>(&self) -> Option<&P::Value> {
|
||||
self.0.as_any().downcast_ref()
|
||||
}
|
||||
|
||||
fn fold(&self, outer: &Self) -> Self {
|
||||
self.0.fold(outer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Entry {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
self.0.dyn_fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Entry {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.dyn_eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Entry {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
state.write_u64(self.0.hash64());
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is implemented for pairs of zero-sized property keys and their
|
||||
/// value types below. Although it is zero-sized, the property `P` must be part
|
||||
/// of the implementing type so that we can use it in the methods (it must be a
|
||||
/// constrained type parameter).
|
||||
trait Bounds: 'static {
|
||||
fn style_id(&self) -> StyleId;
|
||||
fn fold(&self, outer: &Entry) -> Entry;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn dyn_fmt(&self, f: &mut Formatter) -> fmt::Result;
|
||||
fn dyn_eq(&self, other: &Entry) -> bool;
|
||||
fn hash64(&self) -> u64;
|
||||
fn node_id(&self) -> TypeId;
|
||||
fn style_id(&self) -> TypeId;
|
||||
fn fold(&self, outer: &Entry) -> Entry;
|
||||
}
|
||||
|
||||
impl<P: Property> Bounds for (P, P::Value) {
|
||||
fn style_id(&self) -> StyleId {
|
||||
StyleId::of::<P>()
|
||||
}
|
||||
|
||||
fn fold(&self, outer: &Entry) -> Entry {
|
||||
let outer = outer.downcast::<P>().unwrap();
|
||||
let combined = P::fold(self.1.clone(), outer.clone());
|
||||
Entry::new(self.0, combined)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
&self.1
|
||||
}
|
||||
@ -401,7 +465,7 @@ impl<P: Property> Bounds for (P, P::Value) {
|
||||
}
|
||||
|
||||
fn dyn_eq(&self, other: &Entry) -> bool {
|
||||
self.style_id() == other.style_id()
|
||||
self.style_id() == other.p.style_id()
|
||||
&& if let Some(other) = other.downcast::<P>() {
|
||||
&self.1 == other
|
||||
} else {
|
||||
@ -415,4 +479,18 @@ impl<P: Property> Bounds for (P, P::Value) {
|
||||
self.1.hash(&mut state);
|
||||
state.finish()
|
||||
}
|
||||
|
||||
fn node_id(&self) -> TypeId {
|
||||
P::node_id()
|
||||
}
|
||||
|
||||
fn style_id(&self) -> TypeId {
|
||||
TypeId::of::<P>()
|
||||
}
|
||||
|
||||
fn fold(&self, outer: &Entry) -> Entry {
|
||||
let outer = outer.downcast::<P>().unwrap();
|
||||
let combined = P::fold(self.1.clone(), outer.clone());
|
||||
Entry::new(self.0, combined)
|
||||
}
|
||||
}
|
||||
|
@ -197,6 +197,8 @@ impl Layout for PackedNode {
|
||||
regions: &Regions,
|
||||
styles: StyleChain,
|
||||
) -> Vec<Constrained<Rc<Frame>>> {
|
||||
let styles = styles.barred(self.node.as_any().type_id());
|
||||
|
||||
#[cfg(not(feature = "layout-cache"))]
|
||||
return self.node.layout(ctx, regions, styles);
|
||||
|
||||
|
BIN
tests/ref/style/construct.png
Normal file
BIN
tests/ref/style/construct.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 710 B |
10
tests/typ/style/construct.typ
Normal file
10
tests/typ/style/construct.typ
Normal file
@ -0,0 +1,10 @@
|
||||
// Test class construction.
|
||||
|
||||
---
|
||||
// Ensure that constructor styles aren't passed down the tree.
|
||||
#set par(spacing: 2pt)
|
||||
#list(
|
||||
body-indent: 20pt,
|
||||
[First],
|
||||
list([A], [B])
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user