diff --git a/src/library/layout.rs b/src/library/layout.rs index f7fb2882c..e5650664d 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -143,7 +143,7 @@ pub fn v(_: &mut EvalContext, args: &mut Args) -> TypResult { } /// `box`: Place content in a rectangular box. -pub fn boxed(_: &mut EvalContext, args: &mut Args) -> TypResult { +pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult { let width = args.named("width")?; let height = args.named("height")?; let fill = args.named("fill")?; @@ -191,6 +191,48 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult { }))) } +/// `move`: Move content without affecting layout. +pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult { + #[derive(Debug, Hash)] + struct MoveNode { + offset: Spec>, + child: ShapeNode, + } + + impl InlineLevel for MoveNode { + fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame { + let offset = 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(), + ); + + let mut frame = self.child.layout(ctx, space, base); + for (point, _) in &mut frame.children { + *point += offset; + } + + frame + } + } + + let x = args.named("x")?; + let y = args.named("y")?; + let body: Template = args.expect("body")?; + + Ok(Value::Template(Template::from_inline(move |style| { + MoveNode { + offset: Spec::new(x, y), + child: ShapeNode { + shape: ShapeKind::Rect, + width: None, + height: None, + fill: None, + child: Some(body.to_stack(style).pack()), + }, + } + }))) +} + /// `stack`: Stack children along an axis. pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult { enum Child { diff --git a/src/library/mod.rs b/src/library/mod.rs index ac2085eb9..6d3de11d5 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -20,7 +20,7 @@ use crate::diag::{At, TypResult}; use crate::eval::{Args, Array, EvalContext, Scope, Str, Template, Value}; use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric}; use crate::geom::*; -use crate::layout::{BlockLevel, Spacing}; +use crate::layout::{BlockLevel, Frame, InlineLevel, LayoutContext, Spacing}; use crate::style::Style; use crate::syntax::{Span, Spanned}; @@ -43,9 +43,10 @@ pub fn new() -> Scope { std.def_func("h", h); std.def_func("v", v); std.def_func("align", align); - std.def_func("box", boxed); + std.def_func("box", box_); std.def_func("block", block); std.def_func("pad", pad); + std.def_func("move", move_); std.def_func("stack", stack); std.def_func("grid", grid); diff --git a/tests/ref/layout/move.png b/tests/ref/layout/move.png new file mode 100644 index 000000000..dc2e7ab3e Binary files /dev/null and b/tests/ref/layout/move.png differ diff --git a/tests/typ/layout/move.typ b/tests/typ/layout/move.typ new file mode 100644 index 000000000..c1f97e159 --- /dev/null +++ b/tests/typ/layout/move.typ @@ -0,0 +1,11 @@ +#let size = 11pt +#let tex = [{ + [T] + h(-0.14 * size) + move(y: 0.22 * size)[E] + h(-0.12 * size) + [X] +}] + +#font("Latin Modern Math", size) +Not #tex!