summaryrefslogtreecommitdiff
path: root/src/eval/styles.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-12-20 14:18:29 +0100
committerLaurenz <laurmaedje@gmail.com>2021-12-21 00:20:24 +0100
commit11565a40b315212474f52eb576a9fd92b11f1132 (patch)
treec6b7afb35103065bc92b407094ca905bb75cfc73 /src/eval/styles.rs
parent958f74f77707340f34ee36d09492bdb74523aa2a (diff)
Set Rules Episode IX: The Rise of Testing
Diffstat (limited to 'src/eval/styles.rs')
-rw-r--r--src/eval/styles.rs90
1 files changed, 47 insertions, 43 deletions
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
index 2396646f..5304e0ad 100644
--- a/src/eval/styles.rs
+++ b/src/eval/styles.rs
@@ -20,6 +20,11 @@ impl Styles {
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<P: Property>(key: P, value: P::Value) -> Self {
let mut styles = Self::new();
@@ -27,11 +32,6 @@ impl Styles {
styles
}
- /// Whether this map contains no styles.
- pub fn is_empty(&self) -> bool {
- self.map.is_empty()
- }
-
/// Set the value for a style property.
pub fn set<P: Property>(&mut self, key: P, value: P::Value) {
let id = StyleId::of::<P>();
@@ -47,6 +47,13 @@ impl Styles {
self.map.push((id, Entry::new(key, value)));
}
+ /// Set a value for a style property if it is `Some(_)`.
+ pub fn set_opt<P: Property>(&mut self, key: P, value: Option<P::Value>) {
+ if let Some(value) = value {
+ self.set(key, value);
+ }
+ }
+
/// Toggle a boolean style property.
pub fn toggle<P: Property<Value = bool>>(&mut self, key: P) {
let id = StyleId::of::<P>();
@@ -82,13 +89,22 @@ impl Styles {
}
/// Get a reference to a style directly in this map (no default value).
- pub fn get_direct<P: Property>(&self, _: P) -> Option<&P::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`.
+ ///
+ /// 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`.
@@ -105,13 +121,9 @@ impl Styles {
}
}
- /// 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
+ /// 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`.
@@ -119,18 +131,13 @@ impl Styles {
self.map.retain(|a| other.map.iter().any(|b| a == b));
}
- /// 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));
- }
-
/// 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,
{
- // TODO(set): Filtered length + one direction equal should suffice.
+ // 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))
@@ -177,7 +184,7 @@ impl Entry {
impl Debug for Entry {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
+ self.0.dyn_fmt(f)
}
}
@@ -195,22 +202,23 @@ impl Hash for Entry {
trait Bounds: 'static {
fn as_any(&self) -> &dyn Any;
- fn fmt(&self, f: &mut Formatter) -> fmt::Result;
+ 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;
}
-impl<P> Bounds for (P, P::Value)
-where
- P: Property,
- P::Value: Debug + Hash + PartialEq + 'static,
-{
+// `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
}
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ fn dyn_fmt(&self, f: &mut Formatter) -> fmt::Result {
if f.alternate() {
write!(f, "#[{} = {:?}]", P::NAME, self.1)
} else {
@@ -242,11 +250,12 @@ where
/// Style property keys.
///
/// This trait is not intended to be implemented manually, but rather through
-/// the `properties!` macro.
+/// the `#[properties]` proc-macro.
pub trait Property: Copy + 'static {
- /// The type of this property, for example, this could be
- /// [`Length`](crate::geom::Length) for a `WIDTH` property.
- type Value: Debug + Clone + Hash + PartialEq + '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;
@@ -257,12 +266,16 @@ pub trait Property: Copy + 'static {
/// 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
+ /// `#[properties]` macro. This way, expensive defaults don't need to be
/// recreated all the time.
fn default_ref() -> &'static Self::Value;
- /// Combine the property with an outer value.
- fn combine(inner: Self::Value, _: Self::Value) -> 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
}
}
@@ -277,12 +290,3 @@ impl StyleId {
Self(TypeId::of::<P>())
}
}
-
-/// Set a style property to a value if the value is `Some`.
-macro_rules! set {
- ($styles:expr, $target:expr => $value:expr) => {
- if let Some(v) = $value {
- $styles.set($target, v);
- }
- };
-}