diff options
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/image.rs | 18 | ||||
| -rw-r--r-- | src/library/mod.rs | 8 | ||||
| -rw-r--r-- | src/library/par.rs | 2 | ||||
| -rw-r--r-- | src/library/placed.rs | 5 | ||||
| -rw-r--r-- | src/library/shape.rs | 21 | ||||
| -rw-r--r-- | src/library/sized.rs | 10 | ||||
| -rw-r--r-- | src/library/transform.rs | 66 |
7 files changed, 86 insertions, 44 deletions
diff --git a/src/library/image.rs b/src/library/image.rs index a53eacc5..17822619 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -7,7 +7,8 @@ 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 sizing = Spec::new(args.named("width")?, args.named("height")?); + let width = args.named("width")?; + let height = args.named("height")?; let fit = args.named("fit")?.unwrap_or_default(); // Load the image. @@ -20,7 +21,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { })?; Ok(Value::Template(Template::from_inline(move |_| { - ImageNode { id, fit }.pack().sized(sizing) + ImageNode { id, fit }.pack().sized(Spec::new(width, height)) }))) } @@ -81,13 +82,12 @@ impl Layout for ImageNode { // Create a clipping group if the image mode is `cover`. if self.fit == ImageFit::Cover { - let group = Group { - frame: Rc::new(frame), - clips: self.fit == ImageFit::Cover, - }; - - frame = Frame::new(canvas, canvas.h); - frame.push(Point::zero(), Element::Group(group)); + let mut wrapper = Frame::new(canvas, canvas.h); + wrapper.push( + Point::zero(), + Element::Group(Group::new(Rc::new(frame)).clips(true)), + ); + frame = wrapper; } let mut cts = Constraints::new(regions.expand); diff --git a/src/library/mod.rs b/src/library/mod.rs index 9e7f6f28..4d730a7e 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -76,12 +76,14 @@ pub fn new() -> Scope { std.def_func("box", box_); std.def_func("block", block); std.def_func("flow", flow); + std.def_func("stack", stack); + std.def_func("grid", grid); + std.def_func("pad", pad); std.def_func("align", align); std.def_func("place", place); std.def_func("move", move_); - std.def_func("stack", stack); - std.def_func("pad", pad); - std.def_func("grid", grid); + std.def_func("scale", scale); + std.def_func("rotate", rotate); // Elements. std.def_func("image", image); diff --git a/src/library/par.rs b/src/library/par.rs index 46dc304a..7f3f1088 100644 --- a/src/library/par.rs +++ b/src/library/par.rs @@ -628,7 +628,7 @@ impl<'a> LineStack<'a> { for line in self.lines.drain(..) { let frame = line.build(ctx, self.size.w); - let pos = Point::new(Length::zero(), offset); + let pos = Point::with_y(offset); if first { output.baseline = pos.y + frame.baseline; first = false; diff --git a/src/library/placed.rs b/src/library/placed.rs index e2c2e9e8..4f1c5b85 100644 --- a/src/library/placed.rs +++ b/src/library/placed.rs @@ -3,11 +3,12 @@ use super::prelude::*; /// `place`: Place content at an absolute position. pub fn place(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let aligns = args.find().unwrap_or(Spec::new(Some(Align::Left), None)); - let offset = Spec::new(args.named("dx")?, args.named("dy")?); + let tx = args.named("dx")?.unwrap_or_default(); + let ty = args.named("dy")?.unwrap_or_default(); let body: Template = args.expect("body")?; Ok(Value::Template(Template::from_block(move |style| { PlacedNode { - child: body.pack(style).moved(offset).aligned(aligns), + child: body.pack(style).moved(Point::new(tx, ty)).aligned(aligns), } }))) } diff --git a/src/library/shape.rs b/src/library/shape.rs index 061b4d25..5d3504d0 100644 --- a/src/library/shape.rs +++ b/src/library/shape.rs @@ -5,8 +5,9 @@ use crate::util::RcExt; /// `rect`: A rectangle with optional content. pub fn rect(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let sizing = Spec::new(args.named("width")?, args.named("height")?); - shape_impl(args, ShapeKind::Rect, sizing) + let width = args.named("width")?; + let height = args.named("height")?; + shape_impl(args, ShapeKind::Rect, width, height) } /// `square`: A square with optional content. @@ -20,14 +21,14 @@ pub fn square(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { None => args.named("height")?, size => size, }; - let sizing = Spec::new(width, height); - shape_impl(args, ShapeKind::Square, sizing) + shape_impl(args, ShapeKind::Square, width, height) } /// `ellipse`: An ellipse with optional content. pub fn ellipse(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let sizing = Spec::new(args.named("width")?, args.named("height")?); - shape_impl(args, ShapeKind::Ellipse, sizing) + let width = args.named("width")?; + let height = args.named("height")?; + shape_impl(args, ShapeKind::Ellipse, width, height) } /// `circle`: A circle with optional content. @@ -41,14 +42,14 @@ pub fn circle(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { None => args.named("height")?, diameter => diameter, }; - let sizing = Spec::new(width, height); - shape_impl(args, ShapeKind::Circle, sizing) + shape_impl(args, ShapeKind::Circle, width, height) } fn shape_impl( args: &mut Args, kind: ShapeKind, - sizing: Spec<Option<Linear>>, + width: Option<Linear>, + height: Option<Linear>, ) -> TypResult<Value> { // The default appearance of a shape. let default = Stroke { @@ -80,7 +81,7 @@ fn shape_impl( child: body.as_ref().map(|body| body.pack(style).padded(padding)), } .pack() - .sized(sizing) + .sized(Spec::new(width, height)) }))) } diff --git a/src/library/sized.rs b/src/library/sized.rs index 8d69afac..d137c51e 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -2,19 +2,21 @@ use super::prelude::*; /// `box`: Size content and place it into a paragraph. pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let sizing = Spec::new(args.named("width")?, args.named("height")?); + let width = args.named("width")?; + let height = args.named("height")?; let body: Template = args.find().unwrap_or_default(); Ok(Value::Template(Template::from_inline(move |style| { - body.pack(style).sized(sizing) + body.pack(style).sized(Spec::new(width, height)) }))) } /// `block`: Size content and place it into the flow. pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let sizing = Spec::new(args.named("width")?, args.named("height")?); + let width = args.named("width")?; + let height = args.named("height")?; let body: Template = args.find().unwrap_or_default(); Ok(Value::Template(Template::from_block(move |style| { - body.pack(style).sized(sizing) + body.pack(style).sized(Spec::new(width, height)) }))) } diff --git a/src/library/transform.rs b/src/library/transform.rs index 7553bef2..8d1c6132 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -1,24 +1,54 @@ use super::prelude::*; +use crate::geom::Transform; /// `move`: Move content without affecting layout. pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let offset = Spec::new(args.named("x")?, args.named("y")?); + let tx = args.named("x")?.unwrap_or_default(); + let ty = args.named("y")?.unwrap_or_default(); + let transform = Transform::translation(tx, ty); + transform_impl(args, transform) +} + +/// `scale`: Scale content without affecting layout. +pub fn scale(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + let all = args.find(); + let sx = args.named("x")?.or(all).unwrap_or(Relative::one()); + let sy = args.named("y")?.or(all).unwrap_or(Relative::one()); + let transform = Transform::scaling(sx, sy); + transform_impl(args, transform) +} + +/// `rotate`: Rotate content without affecting layout. +pub fn rotate(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + let angle = args.expect("angle")?; + let transform = Transform::rotation(angle); + transform_impl(args, transform) +} + +fn transform_impl(args: &mut Args, transform: Transform) -> TypResult<Value> { let body: Template = args.expect("body")?; + let origin = args + .named("origin")? + .unwrap_or(Spec::splat(None)) + .unwrap_or(Spec::new(Align::Center, Align::Horizon)); + Ok(Value::Template(Template::from_inline(move |style| { - body.pack(style).moved(offset) + body.pack(style).transformed(transform, origin) }))) } -/// A node that moves its child without affecting layout. +/// A node that transforms its child without affecting layout. #[derive(Debug, Hash)] -pub struct MoveNode { - /// The node whose contents should be moved. +pub struct TransformNode { + /// The node whose contents should be transformed. pub child: PackedNode, - /// How much to move the contents. - pub offset: Spec<Option<Linear>>, + /// Transformation to apply to the contents. + pub transform: Transform, + /// The origin of the transformation. + pub origin: Spec<Align>, } -impl Layout for MoveNode { +impl Layout for TransformNode { fn layout( &self, ctx: &mut LayoutContext, @@ -26,13 +56,19 @@ impl Layout for MoveNode { ) -> Vec<Constrained<Rc<Frame>>> { let mut frames = self.child.layout(ctx, regions); - for (Constrained { item: frame, .. }, (_, base)) in - frames.iter_mut().zip(regions.iter()) - { - Rc::make_mut(frame).translate(Point::new( - self.offset.x.map(|x| x.resolve(base.w)).unwrap_or_default(), - self.offset.y.map(|y| y.resolve(base.h)).unwrap_or_default(), - )); + for Constrained { item: frame, .. } in frames.iter_mut() { + let x = self.origin.x.resolve(frame.size.w); + let y = self.origin.y.resolve(frame.size.h); + let transform = Transform::translation(x, y) + .pre_concat(self.transform) + .pre_concat(Transform::translation(-x, -y)); + + let mut wrapper = Frame::new(frame.size, frame.baseline); + wrapper.push( + Point::zero(), + Element::Group(Group::new(std::mem::take(frame)).transform(transform)), + ); + *frame = Rc::new(wrapper); } frames |
