summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval/mod.rs2
-rw-r--r--src/eval/ops.rs23
-rw-r--r--src/eval/value.rs6
-rw-r--r--src/geom/spec.rs11
-rw-r--r--src/layout/mod.rs18
-rw-r--r--src/library/align.rs20
-rw-r--r--src/library/image.rs5
-rw-r--r--src/library/mod.rs9
-rw-r--r--src/library/par.rs10
-rw-r--r--src/library/placed.rs11
-rw-r--r--src/library/shape.rs24
-rw-r--r--src/library/sized.rs10
-rw-r--r--src/library/transform.rs5
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)
})))
}