diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 1ff497e89..6b28e5abe 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -422,7 +422,7 @@ impl Eval for CallArgs { } v => { if let Value::Dyn(dynamic) = &v { - if let Some(args) = dynamic.downcast_ref::() { + if let Some(args) = dynamic.downcast::() { items.extend(args.items.iter().cloned()); continue; } diff --git a/src/eval/ops.rs b/src/eval/ops.rs index e40fa78de..6fac354d3 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -1,8 +1,9 @@ use std::cmp::Ordering; use std::convert::TryFrom; -use super::Value; +use super::{Dynamic, Value}; use crate::diag::StrResult; +use crate::geom::{Align, Get, Spec}; use crate::util::EcoString; use Value::*; @@ -87,7 +88,25 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult { (Template(a), Str(b)) => Template(a + b), (Str(a), Template(b)) => Template(a + b), - (a, b) => mismatch!("cannot add {} and {}", a, b), + (a, b) => { + if let (Dyn(a), Dyn(b)) = (&a, &b) { + // 1D alignments can be summed into 2D alignments. + if let (Some(&a), Some(&b)) = + (a.downcast::(), b.downcast::()) + { + if a.axis() == b.axis() { + return Err(format!("cannot add two {:?} alignments", a.axis())); + } + + let mut aligns = Spec::default(); + aligns.set(a.axis(), Some(a)); + aligns.set(b.axis(), Some(b)); + return Ok(Dyn(Dynamic::new(aligns))); + } + } + + mismatch!("cannot add {} and {}", a, b); + } }) } diff --git a/src/eval/value.rs b/src/eval/value.rs index dec5c6c0e..16e8b8103 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -188,7 +188,7 @@ impl Dynamic { } /// Try to downcast to a reference to a specific type. - pub fn downcast_ref(&self) -> Option<&T> { + pub fn downcast(&self) -> Option<&T> { self.0.as_any().downcast_ref() } @@ -225,7 +225,7 @@ where } fn dyn_eq(&self, other: &Dynamic) -> bool { - if let Some(other) = other.downcast_ref::() { + if let Some(other) = other.downcast::() { self == other } else { false @@ -334,7 +334,7 @@ macro_rules! castable { let found = match value { $($pattern => return Ok($out),)* $crate::eval::Value::Dyn(dynamic) => { - $(if let Some($dyn_in) = dynamic.downcast_ref::<$dyn_type>() { + $(if let Some($dyn_in) = dynamic.downcast::<$dyn_type>() { return Ok($dyn_out); })* dynamic.type_name() diff --git a/src/geom/spec.rs b/src/geom/spec.rs index 576c1c89b..02263481d 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -123,7 +123,7 @@ impl Debug for Spec { } /// The two specific layouting axes. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Eq, PartialEq, Hash)] pub enum SpecAxis { /// The horizontal layouting axis. Horizontal, @@ -150,3 +150,12 @@ impl SpecAxis { } } } + +impl Debug for SpecAxis { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad(match self { + Self::Horizontal => "horizontal", + Self::Vertical => "vertical", + }) + } +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 8f46c049a..be4e994cd 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -104,27 +104,27 @@ impl PackedNode { } /// Force a size for this node. - pub fn sized(self, w: Option, h: Option) -> Self { - if w.is_some() || h.is_some() { - SizedNode { child: self, sizing: Spec::new(w, h) }.pack() + pub fn sized(self, sizing: Spec>) -> Self { + if sizing.any(Option::is_some) { + SizedNode { child: self, sizing }.pack() } else { self } } /// Set alignments for this node. - pub fn aligned(self, x: Option, y: Option) -> Self { - if x.is_some() || y.is_some() { - AlignNode { child: self, aligns: Spec::new(x, y) }.pack() + pub fn aligned(self, aligns: Spec>) -> Self { + if aligns.any(Option::is_some) { + AlignNode { child: self, aligns }.pack() } else { self } } /// Move this node's contents without affecting layout. - pub fn moved(self, dx: Option, dy: Option) -> Self { - if dx.is_some() || dy.is_some() { - MoveNode { child: self, offset: Spec::new(dx, dy) }.pack() + pub fn moved(self, offset: Spec>) -> Self { + if offset.any(Option::is_some) { + MoveNode { child: self, offset }.pack() } else { self } diff --git a/src/library/align.rs b/src/library/align.rs index 97196aa78..7ce749d1f 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -2,32 +2,18 @@ use super::prelude::*; /// `align`: Configure the alignment along the layouting axes. pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult { - let Spec { x, y } = parse_aligns(args)?; + let aligns = args.expect::>("alignment")?; let body = args.expect::