summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/class.rs12
-rw-r--r--src/eval/mod.rs4
-rw-r--r--src/eval/node.rs30
-rw-r--r--src/eval/styles.rs398
4 files changed, 267 insertions, 177 deletions
diff --git a/src/eval/class.rs b/src/eval/class.rs
index c4393b8a..15e1f249 100644
--- a/src/eval/class.rs
+++ b/src/eval/class.rs
@@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::marker::PhantomData;
use std::rc::Rc;
-use super::{Args, EvalContext, Node, Styles};
+use super::{Args, EvalContext, Node, StyleMap};
use crate::diag::TypResult;
use crate::util::EcoString;
@@ -66,7 +66,7 @@ impl Class {
/// This parses both property and data arguments (in this order) and styles
/// the node constructed from the data with the style properties.
pub fn construct(&self, ctx: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
- let mut styles = Styles::new();
+ let mut styles = StyleMap::new();
self.set(args, &mut styles)?;
let node = self.0.shim.construct(ctx, args)?;
Ok(node.styled(styles))
@@ -76,7 +76,7 @@ impl Class {
///
/// This parses property arguments and writes the resulting styles into the
/// given style map. There are no further side effects.
- pub fn set(&self, args: &mut Args, styles: &mut Styles) -> TypResult<()> {
+ pub fn set(&self, args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
self.0.shim.set(args, styles)
}
}
@@ -113,14 +113,14 @@ pub trait Construct {
pub trait Set {
/// Parse the arguments and insert style properties of this class into the
/// given style map.
- fn set(args: &mut Args, styles: &mut Styles) -> TypResult<()>;
+ fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()>;
}
/// Rewires the operations available on a class in an object-safe way. This is
/// only implemented by the zero-sized `Shim` struct.
trait Bounds {
fn construct(&self, ctx: &mut EvalContext, args: &mut Args) -> TypResult<Node>;
- fn set(&self, args: &mut Args, styles: &mut Styles) -> TypResult<()>;
+ fn set(&self, args: &mut Args, styles: &mut StyleMap) -> TypResult<()>;
}
struct Shim<T>(PhantomData<T>);
@@ -133,7 +133,7 @@ where
T::construct(ctx, args)
}
- fn set(&self, args: &mut Args, styles: &mut Styles) -> TypResult<()> {
+ fn set(&self, args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
T::set(args, styles)
}
}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 17cc46ef..06218752 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -86,7 +86,7 @@ pub struct EvalContext<'a> {
/// The active scopes.
pub scopes: Scopes<'a>,
/// The active styles.
- pub styles: Styles,
+ pub styles: StyleMap,
}
impl<'a> EvalContext<'a> {
@@ -99,7 +99,7 @@ impl<'a> EvalContext<'a> {
route: vec![source],
modules: HashMap::new(),
scopes: Scopes::new(Some(&ctx.std)),
- styles: Styles::new(),
+ styles: StyleMap::new(),
}
}
diff --git a/src/eval/node.rs b/src/eval/node.rs
index ffa1eee6..54d4104d 100644
--- a/src/eval/node.rs
+++ b/src/eval/node.rs
@@ -5,7 +5,7 @@ use std::iter::Sum;
use std::mem;
use std::ops::{Add, AddAssign};
-use super::Styles;
+use super::StyleMap;
use crate::diag::StrResult;
use crate::geom::SpecAxis;
use crate::layout::{Layout, PackedNode, RootNode};
@@ -63,7 +63,7 @@ pub enum Node {
/// nesting doesn't hurt since we can just recurse into the nested sequences
/// during packing. Also, in theory, this allows better complexity when
/// adding (large) sequence nodes (just like for a text rope).
- Sequence(Vec<(Self, Styles)>),
+ Sequence(Vec<(Self, StyleMap)>),
}
impl Node {
@@ -89,7 +89,7 @@ impl Node {
}
/// Style this node.
- pub fn styled(self, styles: Styles) -> Self {
+ pub fn styled(self, styles: StyleMap) -> Self {
match self {
Self::Inline(inline) => Self::Inline(inline.styled(styles)),
Self::Block(block) => Self::Block(block.styled(styles)),
@@ -100,7 +100,7 @@ impl Node {
/// Style this node in monospace.
pub fn monospaced(self) -> Self {
- self.styled(Styles::one(TextNode::MONOSPACE, true))
+ self.styled(StyleMap::with(TextNode::MONOSPACE, true))
}
/// Lift to a type-erased block-level node.
@@ -109,7 +109,7 @@ impl Node {
packed
} else {
let mut packer = Packer::new(false);
- packer.walk(self, Styles::new());
+ packer.walk(self, StyleMap::new());
packer.into_block()
}
}
@@ -117,7 +117,7 @@ impl Node {
/// Lift to a root layout tree node.
pub fn into_root(self) -> RootNode {
let mut packer = Packer::new(true);
- packer.walk(self, Styles::new());
+ packer.walk(self, StyleMap::new());
packer.into_root()
}
@@ -127,7 +127,7 @@ impl Node {
.map_err(|_| format!("cannot repeat this template {} times", n))?;
// TODO(style): Make more efficient.
- Ok(Self::Sequence(vec![(self.clone(), Styles::new()); count]))
+ Ok(Self::Sequence(vec![(self.clone(), StyleMap::new()); count]))
}
}
@@ -142,7 +142,7 @@ impl Add for Node {
fn add(self, rhs: Self) -> Self::Output {
// TODO(style): Make more efficient.
- Self::Sequence(vec![(self, Styles::new()), (rhs, Styles::new())])
+ Self::Sequence(vec![(self, StyleMap::new()), (rhs, StyleMap::new())])
}
}
@@ -154,7 +154,7 @@ impl AddAssign for Node {
impl Sum for Node {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
- Self::Sequence(iter.map(|n| (n, Styles::new())).collect())
+ Self::Sequence(iter.map(|n| (n, StyleMap::new())).collect())
}
}
@@ -194,7 +194,7 @@ impl Packer {
}
/// Consider a node with the given styles.
- fn walk(&mut self, node: Node, styles: Styles) {
+ fn walk(&mut self, node: Node, styles: StyleMap) {
match node {
Node::Space => {
// A text space is "soft", meaning that it can be eaten up by
@@ -321,7 +321,7 @@ impl Packer {
}
/// Advance to the next paragraph.
- fn parbreak(&mut self, break_styles: Option<Styles>) {
+ fn parbreak(&mut self, break_styles: Option<StyleMap>) {
// Erase any styles that will be inherited anyway.
let Builder { mut children, styles, .. } = mem::take(&mut self.par);
for child in &mut children {
@@ -366,7 +366,7 @@ impl Packer {
/// Break to a new paragraph if the `styles` contain paragraph styles that
/// are incompatible with the current paragraph.
- fn make_par_compatible(&mut self, styles: &Styles) {
+ fn make_par_compatible(&mut self, styles: &StyleMap) {
if self.par.children.is_empty() {
self.par.styles = styles.clone();
return;
@@ -383,7 +383,7 @@ impl Packer {
/// Break to a new page if the `styles` contain page styles that are
/// incompatible with the current flow.
- fn make_flow_compatible(&mut self, styles: &Styles) {
+ fn make_flow_compatible(&mut self, styles: &StyleMap) {
if self.flow.children.is_empty() && self.par.children.is_empty() {
self.flow.styles = styles.clone();
return;
@@ -402,7 +402,7 @@ impl Packer {
/// Container for building a flow or paragraph.
struct Builder<T> {
/// The intersection of the style properties of all `children`.
- styles: Styles,
+ styles: StyleMap,
/// The accumulated flow or paragraph children.
children: Vec<T>,
/// The kind of thing that was last added.
@@ -412,7 +412,7 @@ struct Builder<T> {
impl<T> Default for Builder<T> {
fn default() -> Self {
Self {
- styles: Styles::new(),
+ styles: StyleMap::new(),
children: vec![],
last: Last::None,
}
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
index 1c4b17ae..f8413feb 100644
--- a/src/eval/styles.rs
+++ b/src/eval/styles.rs
@@ -10,23 +10,21 @@ use std::rc::Rc;
/// A map of style properties.
#[derive(Default, Clone, Hash)]
-pub struct Styles {
- map: Vec<(StyleId, Entry)>,
-}
+pub struct StyleMap(Vec<Entry>);
-impl Styles {
+impl StyleMap {
/// Create a new, empty style map.
pub fn new() -> Self {
- Self { map: vec![] }
+ Self(vec![])
}
/// Whether this map contains no styles.
pub fn is_empty(&self) -> bool {
- self.map.is_empty()
+ self.0.is_empty()
}
- /// Create a style map with a single property-value pair.
- pub fn one<P: Property>(key: P, value: P::Value) -> Self {
+ /// Create a style map from a single property-value pair.
+ pub fn with<P: Property>(key: P, value: P::Value) -> Self {
let mut styles = Self::new();
styles.set(key, value);
styles
@@ -34,17 +32,16 @@ impl Styles {
/// Set the value for a style property.
pub fn set<P: Property>(&mut self, key: P, value: P::Value) {
- let id = StyleId::of::<P>();
- for pair in &mut self.map {
- if pair.0 == id {
- let prev = pair.1.downcast::<P::Value>().unwrap();
- let folded = P::combine(value, prev.clone());
- pair.1 = Entry::new(key, folded);
+ for entry in &mut self.0 {
+ if entry.is::<P>() {
+ let prev = entry.downcast::<P>().unwrap();
+ let folded = P::fold(value, prev.clone());
+ *entry = Entry::new(key, folded);
return;
}
}
- self.map.push((id, Entry::new(key, value)));
+ self.0.push(Entry::new(key, value));
}
/// Set a value for a style property if it is `Some(_)`.
@@ -54,81 +51,63 @@ impl Styles {
}
}
- /// Toggle a boolean style property.
+ /// Toggle a boolean style property, removing it if it exists and inserting
+ /// it with `true` if it doesn't.
pub fn toggle<P: Property<Value = bool>>(&mut self, key: P) {
- let id = StyleId::of::<P>();
- for (i, pair) in self.map.iter_mut().enumerate() {
- if pair.0 == id {
- self.map.swap_remove(i);
+ for (i, entry) in self.0.iter_mut().enumerate() {
+ if entry.is::<P>() {
+ self.0.swap_remove(i);
return;
}
}
- self.map.push((id, Entry::new(key, true)));
- }
-
- /// Get the value of a copyable style property.
- ///
- /// Returns the property's default value if the map does not contain an
- /// entry for it.
- pub fn get<P: Property>(&self, key: P) -> P::Value
- where
- P::Value: Copy,
- {
- self.get_direct(key)
- .map(|&v| P::combine(v, P::default()))
- .unwrap_or_else(P::default)
- }
-
- /// Get a reference to a style property.
- ///
- /// Returns a reference to the property's default value if the map does not
- /// contain an entry for it.
- pub fn get_ref<P: Property>(&self, key: P) -> &P::Value {
- self.get_direct(key).unwrap_or_else(|| P::default_ref())
+ self.0.push(Entry::new(key, true));
}
- /// Get a reference to a style directly in this map (no default value).
- fn get_direct<P: Property>(&self, _: P) -> Option<&P::Value> {
- self.map
- .iter()
- .find(|pair| pair.0 == StyleId::of::<P>())
- .and_then(|pair| pair.1.downcast())
- }
-
- /// Create new styles combining `self` with `outer`.
+ /// Make `self` the first link of the style chain `outer`.
///
- /// Properties from `self` take precedence over the ones from `outer`.
- pub fn chain(&self, outer: &Self) -> Self {
- let mut styles = self.clone();
- styles.apply(outer);
- styles
+ /// The resulting style chain contains styles from `self` as well as
+ /// `outer`. The ones from `self` take precedence over the ones from
+ /// `outer`. For folded properties `self` contributes the inner value.
+ pub fn chain<'a>(&'a self, outer: &'a StyleChain<'a>) -> StyleChain<'a> {
+ if self.is_empty() {
+ // No need to chain an empty map.
+ *outer
+ } else {
+ StyleChain { inner: self, outer: Some(outer) }
+ }
}
- /// Apply styles from `outer` in-place.
+ /// Apply styles from `outer` in-place. The resulting style map is
+ /// equivalent to the style chain created by
+ /// `self.chain(StyleChain::new(outer))`.
///
- /// Properties from `self` take precedence over the ones from `outer`.
+ /// This is useful in the evaluation phase while building nodes and their
+ /// style maps, whereas `chain` would be used during layouting to combine
+ /// immutable style maps from different levels of the hierarchy.
pub fn apply(&mut self, outer: &Self) {
- 'outer: for pair in &outer.map {
- for (id, entry) in &mut self.map {
- if pair.0 == *id {
- entry.apply(&pair.1);
+ 'outer: for outer in &outer.0 {
+ for inner in &mut self.0 {
+ if inner.style_id() == outer.style_id() {
+ inner.fold(outer);
continue 'outer;
}
}
- self.map.push(pair.clone());
+ self.0.push(outer.clone());
}
}
- /// Keep only those styles that are not also in `other`.
+ /// Subtract `other` from `self` in-place, keeping only styles that are in
+ /// `self` but not in `other`.
pub fn erase(&mut self, other: &Self) {
- self.map.retain(|a| other.map.iter().all(|b| a != b));
+ self.0.retain(|x| !other.0.contains(x));
}
- /// Keep only those styles that are also in `other`.
+ /// Intersect `self` with `other` in-place, keeping only styles that are
+ /// both in `self` and `other`.
pub fn intersect(&mut self, other: &Self) {
- self.map.retain(|a| other.map.iter().any(|b| a == b));
+ self.0.retain(|x| other.0.contains(x));
}
/// Whether two style maps are equal when filtered down to the given
@@ -137,113 +116,136 @@ impl Styles {
where
F: Fn(StyleId) -> bool,
{
- // TODO(style): Filtered length + one direction equal should suffice.
- let f = |e: &&(StyleId, Entry)| filter(e.0);
- self.map.iter().filter(f).all(|pair| other.map.contains(pair))
- && other.map.iter().filter(f).all(|pair| self.map.contains(pair))
+ let f = |entry: &&Entry| filter(entry.style_id());
+ self.0.iter().filter(f).count() == other.0.iter().filter(f).count()
+ && self.0.iter().filter(f).all(|x| other.0.contains(x))
}
}
-impl Debug for Styles {
+impl Debug for StyleMap {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- if f.alternate() {
- for pair in &self.map {
- writeln!(f, "{:#?}", pair.1)?;
- }
- Ok(())
- } else {
- f.write_str("Styles ")?;
- f.debug_set().entries(self.map.iter().map(|pair| &pair.1)).finish()
+ for entry in &self.0 {
+ writeln!(f, "{:#?}", entry)?;
}
+ Ok(())
}
}
-impl PartialEq for Styles {
+impl PartialEq for StyleMap {
fn eq(&self, other: &Self) -> bool {
self.compatible(other, |_| true)
}
}
-/// An entry for a single style property.
-#[derive(Clone)]
-pub(crate) struct Entry(Rc<dyn Bounds>);
-
-impl Entry {
- fn new<P: Property>(key: P, value: P::Value) -> Self {
- Self(Rc::new((key, value)))
- }
-
- fn downcast<T: 'static>(&self) -> Option<&T> {
- self.0.as_any().downcast_ref()
- }
-
- fn apply(&mut self, outer: &Self) {
- *self = self.0.combine(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)
- }
+/// A chain of style maps, similar to a linked list.
+///
+/// A style chain allows to conceptually merge (and fold) properties from
+/// multiple style maps in a node hierarchy in a non-allocating way. Rather than
+/// eagerly merging the maps, each access walks the hierarchy from the innermost
+/// to the outermost map, trying to find a match and then folding it with
+/// matches further up the chain.
+#[derive(Clone, Copy, Hash)]
+pub struct StyleChain<'a> {
+ inner: &'a StyleMap,
+ outer: Option<&'a Self>,
}
-impl Hash for Entry {
- fn hash<H: Hasher>(&self, state: &mut H) {
- state.write_u64(self.0.hash64());
+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 }
}
-}
-
-trait Bounds: 'static {
- 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 combine(&self, outer: &Entry) -> Entry;
-}
-// `P` is always zero-sized. We only implement the trait for a pair of key and
-// associated value so that `P` is a constrained type parameter that we can use
-// in `dyn_fmt` to access the property's name. This way, we can effectively
-// store the property's name in its vtable instead of having an actual runtime
-// string somewhere in `Entry`.
-impl<P: Property> Bounds for (P, P::Value) {
- fn as_any(&self) -> &dyn Any {
- &self.1
+ /// Get the (folded) value of a copyable style property.
+ ///
+ /// Returns the property's default value if no map in the chain contains an
+ /// entry for it.
+ pub fn get<P>(self, key: P) -> P::Value
+ where
+ P: Property,
+ P::Value: Copy,
+ {
+ // This exists separately to `get_cloned` for `Copy` types so that
+ // people don't just naively use `get` / `get_cloned` where they should
+ // use `get_ref`.
+ self.get_cloned(key)
}
- fn dyn_fmt(&self, f: &mut Formatter) -> fmt::Result {
- if f.alternate() {
- write!(f, "#[{} = {:?}]", P::NAME, self.1)
+ /// Get a reference to a style property's value.
+ ///
+ /// This is naturally only possible for properties that don't need folding.
+ /// Prefer `get` if possible or resort to `get_cloned` for non-`Copy`
+ /// properties that need folding.
+ ///
+ /// Returns a reference to the property's default value if no map in the
+ /// chain contains an entry for it.
+ pub fn get_ref<P>(self, key: P) -> &'a P::Value
+ where
+ P: Property + Nonfolding,
+ {
+ if let Some(value) = self.get_locally(key) {
+ value
+ } else if let Some(outer) = self.outer {
+ outer.get_ref(key)
} else {
- write!(f, "{}: {:?}", P::NAME, self.1)
+ P::default_ref()
}
}
- fn dyn_eq(&self, other: &Entry) -> bool {
- if let Some(other) = other.downcast::<P::Value>() {
- &self.1 == other
+ /// Get the (folded) value of any style property.
+ ///
+ /// While this works for all properties, you should prefer `get` or
+ /// `get_ref` where possible. This is only needed for non-`Copy` properties
+ /// that need folding.
+ pub fn get_cloned<P>(self, key: P) -> P::Value
+ where
+ P: Property,
+ {
+ if let Some(value) = self.get_locally(key).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)
} else {
- false
+ P::default()
}
}
- fn hash64(&self) -> u64 {
- // No need to hash the TypeId since there's only one
- // valid value type per property.
- fxhash::hash64(&self.1)
+ /// Find a property directly in the most local map.
+ fn get_locally<P: Property>(&self, _: P) -> Option<&'a P::Value> {
+ self.inner
+ .0
+ .iter()
+ .find(|entry| entry.is::<P>())
+ .and_then(|entry| entry.downcast::<P>())
}
+}
- fn combine(&self, outer: &Entry) -> Entry {
- let outer = outer.downcast::<P::Value>().unwrap();
- let combined = P::combine(self.1.clone(), outer.clone());
- Entry::new(self.0, combined)
+impl Debug for StyleChain<'_> {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ self.inner.fmt(f)?;
+ if let Some(outer) = self.outer {
+ outer.fmt(f)?;
+ }
+ Ok(())
+ }
+}
+
+/// A unique identifier for a style property.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct StyleId(TypeId);
+
+impl StyleId {
+ /// The style id of the property.
+ pub fn of<P: Property>() -> Self {
+ Self(TypeId::of::<P>())
}
}
@@ -270,23 +272,111 @@ 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 combine a relative font size with an outer
+ /// For example, this would fold a relative font size with an outer
/// absolute font size.
#[allow(unused_variables)]
- fn combine(inner: Self::Value, outer: Self::Value) -> Self::Value {
+ fn fold(inner: Self::Value, outer: Self::Value) -> Self::Value {
inner
}
}
-/// A unique identifier for a style property.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub struct StyleId(TypeId);
+/// Marker trait that indicates that a property doesn't need folding.
+pub trait Nonfolding {}
-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(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(&mut 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;
+}
+
+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
+ }
+
+ fn dyn_fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "#[{} = {:?}]", P::NAME, self.1)
+ }
+
+ fn dyn_eq(&self, other: &Entry) -> bool {
+ self.style_id() == other.style_id()
+ && if let Some(other) = other.downcast::<P>() {
+ &self.1 == other
+ } else {
+ false
+ }
+ }
+
+ fn hash64(&self) -> u64 {
+ let mut state = fxhash::FxHasher64::default();
+ self.style_id().hash(&mut state);
+ self.1.hash(&mut state);
+ state.finish()
}
}