use std::any::{Any, TypeId}; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::rc::Rc; // TODO(style): Possible optimizations: // - Ref-count map for cheaper cloning and smaller footprint // - Store map in `Option` to make empty maps non-allocating // - Store small properties inline /// A map of style properties. #[derive(Default, Clone, Hash)] pub struct Styles { map: Vec<(StyleId, Entry)>, } impl Styles { /// Create a new, empty style map. pub fn new() -> Self { Self { map: vec![] } } /// Whether this map contains no styles. pub fn is_empty(&self) -> bool { self.map.is_empty() } /// Create a style map with a single property-value pair. pub fn one(key: P, value: P::Value) -> Self { let mut styles = Self::new(); styles.set(key, value); styles } /// Set the value for a style property. pub fn set(&mut self, key: P, value: P::Value) { let id = StyleId::of::

(); for pair in &mut self.map { if pair.0 == id { let prev = pair.1.downcast::().unwrap(); let folded = P::combine(value, prev.clone()); pair.1 = Entry::new(key, folded); return; } } self.map.push((id, Entry::new(key, value))); } /// Set a value for a style property if it is `Some(_)`. pub fn set_opt(&mut self, key: P, value: Option) { if let Some(value) = value { self.set(key, value); } } /// Toggle a boolean style property. pub fn toggle>(&mut self, key: P) { let id = StyleId::of::

(); for (i, pair) in self.map.iter_mut().enumerate() { if pair.0 == id { self.map.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(&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(&self, key: P) -> &P::Value { self.get_direct(key).unwrap_or_else(|| P::default_ref()) } /// Get a reference to a style directly in this map (no default value). fn get_direct(&self, _: P) -> Option<&P::Value> { self.map .iter() .find(|pair| pair.0 == StyleId::of::

()) .and_then(|pair| pair.1.downcast()) } /// Create new styles combining `self` with `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 } /// Apply styles from `outer` in-place. /// /// Properties from `self` take precedence over the ones from `outer`. 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); continue 'outer; } } self.map.push(pair.clone()); } } /// Keep only those styles that are not also in `other`. pub fn erase(&mut self, other: &Self) { self.map.retain(|a| other.map.iter().all(|b| a != b)); } /// Keep only those styles that are also in `other`. pub fn intersect(&mut self, other: &Self) { self.map.retain(|a| other.map.iter().any(|b| a == b)); } /// Whether two style maps are equal when filtered down to the given /// properties. pub fn compatible(&self, other: &Self, filter: F) -> bool 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)) } } impl Debug for Styles { 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() } } } impl PartialEq for Styles { fn eq(&self, other: &Self) -> bool { self.compatible(other, |_| true) } } /// An entry for a single style property. #[derive(Clone)] pub(crate) struct Entry(Rc); impl Entry { fn new(key: P, value: P::Value) -> Self { Self(Rc::new((key, value))) } fn downcast(&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) } } impl Hash for Entry { fn hash(&self, state: &mut H) { state.write_u64(self.0.hash64()); } } 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 Bounds for (P, P::Value) { fn as_any(&self) -> &dyn Any { &self.1 } fn dyn_fmt(&self, f: &mut Formatter) -> fmt::Result { if f.alternate() { write!(f, "#[{} = {:?}]", P::NAME, self.1) } else { write!(f, "{}: {:?}", P::NAME, self.1) } } fn dyn_eq(&self, other: &Entry) -> bool { if let Some(other) = other.downcast::() { &self.1 == other } else { false } } fn hash64(&self) -> u64 { // No need to hash the TypeId since there's only one // valid value type per property. fxhash::hash64(&self.1) } fn combine(&self, outer: &Entry) -> Entry { let outer = outer.downcast::().unwrap(); let combined = P::combine(self.1.clone(), outer.clone()); Entry::new(self.0, combined) } } /// Style property keys. /// /// This trait is not intended to be implemented manually, but rather through /// the `#[properties]` proc-macro. pub trait Property: Copy + 'static { /// The type of value that is returned when getting this property from a /// style map. For example, this could be [`Length`](crate::geom::Length) /// for a `WIDTH` property. type Value: Debug + Clone + PartialEq + Hash + 'static; /// The name of the property, used for debug printing. const NAME: &'static str; /// The default value of the property. fn default() -> Self::Value; /// A static reference to the default value of the property. /// /// This is automatically implemented through lazy-initialization in the /// `#[properties]` macro. This way, expensive defaults don't need to be /// recreated all the time. fn default_ref() -> &'static Self::Value; /// Fold the property with an outer value. /// /// For example, this would combine a relative font size with an outer /// absolute font size. #[allow(unused_variables)] fn combine(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); impl StyleId { /// The style id of the property. pub fn of() -> Self { Self(TypeId::of::

()) } }