diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/mod.rs | 2 | ||||
| -rw-r--r-- | src/eval/ops.rs | 23 | ||||
| -rw-r--r-- | src/eval/value.rs | 6 | ||||
| -rw-r--r-- | src/geom/spec.rs | 11 | ||||
| -rw-r--r-- | src/layout/mod.rs | 18 | ||||
| -rw-r--r-- | src/library/align.rs | 20 | ||||
| -rw-r--r-- | src/library/image.rs | 5 | ||||
| -rw-r--r-- | src/library/mod.rs | 9 | ||||
| -rw-r--r-- | src/library/par.rs | 10 | ||||
| -rw-r--r-- | src/library/placed.rs | 11 | ||||
| -rw-r--r-- | src/library/shape.rs | 24 | ||||
| -rw-r--r-- | src/library/sized.rs | 10 | ||||
| -rw-r--r-- | src/library/transform.rs | 5 |
13 files changed, 84 insertions, 70 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 1ff497e8..6b28e5ab 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::<Args>() { + if let Some(args) = dynamic.downcast::<Args>() { items.extend(args.items.iter().cloned()); continue; } diff --git a/src/eval/ops.rs b/src/eval/ops.rs index e40fa78d..6fac354d 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<Value> { (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::<Align>(), b.downcast::<Align>()) + { + 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 dec5c6c0..16e8b810 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<T: 'static>(&self) -> Option<&T> { + pub fn downcast<T: 'static>(&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::<Self>() { + if let Some(other) = other.downcast::<Self>() { 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 576c1c89..02263481 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -123,7 +123,7 @@ impl<T: Debug> Debug for Spec<T> { } /// 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 8f46c049..be4e994c 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<Linear>, h: Option<Linear>) -> Self { - if w.is_some() || h.is_some() { - SizedNode { child: self, sizing: Spec::new(w, h) }.pack() + pub fn sized(self, sizing: Spec<Option<Linear>>) -> 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<Align>, y: Option<Align>) -> Self { - if x.is_some() || y.is_some() { - AlignNode { child: self, aligns: Spec::new(x, y) }.pack() + pub fn aligned(self, aligns: Spec<Option<Align>>) -> 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<Linear>, dy: Option<Linear>) -> Self { - if dx.is_some() || dy.is_some() { - MoveNode { child: self, offset: Spec::new(dx, dy) }.pack() + pub fn moved(self, offset: Spec<Option<Linear>>) -> 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 97196aa7..7ce749d1 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<Value> { - let Spec { x, y } = parse_aligns(args)?; + let aligns = args.expect::<Spec<_>>("alignment")?; let body = args.expect::<Template>("body")?; Ok(Value::Template(Template::from_block(move |style| { let mut style = style.clone(); - if let Some(x) = x { + if let Some(x) = aligns.x { style.par_mut().align = x; } - body.pack(&style).aligned(x, y) + body.pack(&style).aligned(aligns) }))) } -/// Parse alignment arguments with shorthand. -pub(super) fn parse_aligns(args: &mut Args) -> TypResult<Spec<Option<Align>>> { - let mut x = args.named("horizontal")?; - let mut y = args.named("vertical")?; - for Spanned { v, span } in args.all::<Spanned<Align>>() { - match v.axis() { - SpecAxis::Horizontal if x.is_none() => x = Some(v), - SpecAxis::Vertical if y.is_none() => y = Some(v), - _ => bail!(span, "unexpected argument"), - } - } - Ok(Spec::new(x, y)) -} - /// A node that aligns its child. #[derive(Debug, Hash)] pub struct AlignNode { diff --git a/src/library/image.rs b/src/library/image.rs index b0d66a63..a53eacc5 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -7,8 +7,7 @@ use crate::image::ImageId; /// `image`: An image. pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let path = args.expect::<Spanned<EcoString>>("path to image file")?; - let width = args.named("width")?; - let height = args.named("height")?; + let sizing = Spec::new(args.named("width")?, args.named("height")?); let fit = args.named("fit")?.unwrap_or_default(); // Load the image. @@ -21,7 +20,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { })?; Ok(Value::Template(Template::from_inline(move |_| { - ImageNode { id, fit }.pack().sized(width, height) + ImageNode { id, fit }.pack().sized(sizing) }))) } diff --git a/src/library/mod.rs b/src/library/mod.rs index 9fe15a0f..9e7f6f28 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -142,6 +142,15 @@ dynamic! { } dynamic! { + Spec<Option<Align>>: "2d alignment", + @align: Align => { + let mut aligns = Spec::default(); + aligns.set(align.axis(), Some(*align)); + aligns + }, +} + +dynamic! { FontFamily: "font family", Value::Str(string) => Self::Named(string.to_lowercase()), } diff --git a/src/library/par.rs b/src/library/par.rs index 4d291fdc..5edffeaa 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -24,20 +24,18 @@ pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { }); if let Some(Spanned { v, span }) = args.named::<Spanned<Dir>>("dir")? { - if v.axis() == SpecAxis::Horizontal { - dir = Some(v); - } else { + if v.axis() != SpecAxis::Horizontal { bail!(span, "must be horizontal"); } + dir = Some(v); } let mut align = None; if let Some(Spanned { v, span }) = args.named::<Spanned<Align>>("align")? { - if v.axis() == SpecAxis::Horizontal { - align = Some(v); - } else { + if v.axis() != SpecAxis::Horizontal { bail!(span, "must be horizontal"); } + align = Some(v); } ctx.template.modify(move |style| { diff --git a/src/library/placed.rs b/src/library/placed.rs index 0d92fc35..e2c2e9e8 100644 --- a/src/library/placed.rs +++ b/src/library/placed.rs @@ -1,18 +1,13 @@ -use super::parse_aligns; use super::prelude::*; /// `place`: Place content at an absolute position. pub fn place(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let Spec { x, y } = parse_aligns(args)?; - let dx = args.named("dx")?; - let dy = args.named("dy")?; + let aligns = args.find().unwrap_or(Spec::new(Some(Align::Left), None)); + let offset = Spec::new(args.named("dx")?, args.named("dy")?); let body: Template = args.expect("body")?; Ok(Value::Template(Template::from_block(move |style| { PlacedNode { - child: body - .pack(style) - .moved(dx, dy) - .aligned(Some(x.unwrap_or(Align::Left)), y), + child: body.pack(style).moved(offset).aligned(aligns), } }))) } diff --git a/src/library/shape.rs b/src/library/shape.rs index f47da82f..061b4d25 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -5,9 +5,8 @@ use crate::util::RcExt; /// `rect`: A rectangle with optional content. pub fn rect(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let width = args.named("width")?; - let height = args.named("height")?; - shape_impl(args, ShapeKind::Rect, width, height) + let sizing = Spec::new(args.named("width")?, args.named("height")?); + shape_impl(args, ShapeKind::Rect, sizing) } /// `square`: A square with optional content. @@ -21,14 +20,14 @@ pub fn square(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { None => args.named("height")?, size => size, }; - shape_impl(args, ShapeKind::Square, width, height) + let sizing = Spec::new(width, height); + shape_impl(args, ShapeKind::Square, sizing) } /// `ellipse`: An ellipse with optional content. pub fn ellipse(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let width = args.named("width")?; - let height = args.named("height")?; - shape_impl(args, ShapeKind::Ellipse, width, height) + let sizing = Spec::new(args.named("width")?, args.named("height")?); + shape_impl(args, ShapeKind::Ellipse, sizing) } /// `circle`: A circle with optional content. @@ -42,14 +41,14 @@ pub fn circle(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { None => args.named("height")?, diameter => diameter, }; - shape_impl(args, ShapeKind::Circle, width, height) + let sizing = Spec::new(width, height); + shape_impl(args, ShapeKind::Circle, sizing) } fn shape_impl( args: &mut Args, kind: ShapeKind, - width: Option<Linear>, - height: Option<Linear>, + sizing: Spec<Option<Linear>>, ) -> TypResult<Value> { // The default appearance of a shape. let default = Stroke { @@ -67,7 +66,10 @@ fn shape_impl( }), }; + // Shorthand for padding. let padding = Sides::splat(args.named("padding")?.unwrap_or_default()); + + // The shape's contents. let body = args.find::<Template>(); Ok(Value::Template(Template::from_inline(move |style| { @@ -78,7 +80,7 @@ fn shape_impl( child: body.as_ref().map(|body| body.pack(style).padded(padding)), } .pack() - .sized(width, height) + .sized(sizing) }))) } diff --git a/src/library/sized.rs b/src/library/sized.rs index 3089f90b..8d69afac 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -2,21 +2,19 @@ use super::prelude::*; /// `box`: Size content and place it into a paragraph. pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let width = args.named("width")?; - let height = args.named("height")?; + let sizing = Spec::new(args.named("width")?, args.named("height")?); let body: Template = args.find().unwrap_or_default(); Ok(Value::Template(Template::from_inline(move |style| { - body.pack(style).sized(width, height) + body.pack(style).sized(sizing) }))) } /// `block`: Size content and place it into the flow. pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let width = args.named("width")?; - let height = args.named("height")?; + let sizing = Spec::new(args.named("width")?, args.named("height")?); let body: Template = args.find().unwrap_or_default(); Ok(Value::Template(Template::from_block(move |style| { - body.pack(style).sized(width, height) + body.pack(style).sized(sizing) }))) } diff --git a/src/library/transform.rs b/src/library/transform.rs index 10752358..7553bef2 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -2,11 +2,10 @@ use super::prelude::*; /// `move`: Move content without affecting layout. pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let dx = args.named("dx")?; - let dy = args.named("dy")?; + let offset = Spec::new(args.named("x")?, args.named("y")?); let body: Template = args.expect("body")?; Ok(Value::Template(Template::from_inline(move |style| { - body.pack(style).moved(dx, dy) + body.pack(style).moved(offset) }))) } |
