diff --git a/src/exec/context.rs b/src/exec/context.rs index 2b0ef9faf..d8ce65282 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -35,76 +35,11 @@ impl ExecContext { } } - /// Execute a template and return the result as a stack node. - pub fn exec_template_stack(&mut self, template: &Template) -> StackNode { - self.exec_stack(|ctx| template.exec(ctx)) - } - - /// Execute a syntax tree with a map and return the result as a stack node. - pub fn exec_tree_stack(&mut self, tree: &SyntaxTree, map: &ExprMap) -> StackNode { - self.exec_stack(|ctx| tree.exec_with_map(ctx, map)) - } - - /// Execute something and return the result as a stack node. - pub fn exec_stack(&mut self, f: impl FnOnce(&mut Self)) -> StackNode { - let snapshot = self.state.clone(); - let page = self.page.take(); - let stack = mem::replace(&mut self.stack, StackBuilder::new(&self.state)); - - f(self); - - self.state = snapshot; - self.page = page; - mem::replace(&mut self.stack, stack).build() - } - - /// Push text into the active paragraph. - /// - /// The text is split into lines at newlines. - pub fn push_text(&mut self, text: impl Into) { - self.stack.par.push(self.make_text_node(text)); - } - - /// Push text, but in monospace. - pub fn push_monospace_text(&mut self, text: impl Into) { - let prev = Rc::clone(&self.state.font); - self.state.font_mut().monospace = true; - self.push_text(text); - self.state.font = prev; - } - /// Push a word space into the active paragraph. - pub fn push_word_space(&mut self) { + pub fn space(&mut self) { self.stack.par.push_soft(self.make_text_node(' ')); } - /// Push any node into the active paragraph. - pub fn push_into_par(&mut self, node: impl Into) { - let align = self.state.aligns.cross; - self.stack.par.push(ParChild::Any(node.into(), align)); - } - - /// Push any node into the active stack. - pub fn push_into_stack(&mut self, node: impl Into) { - self.parbreak(); - let aligns = self.state.aligns; - self.stack.push(StackChild::Any(node.into(), aligns)); - self.parbreak(); - } - - /// Push spacing into the active paragraph or stack depending on the `axis`. - pub fn push_spacing(&mut self, axis: GenAxis, amount: Linear) { - match axis { - GenAxis::Main => { - self.stack.finish_par(&self.state); - self.stack.push_hard(StackChild::Spacing(amount)); - } - GenAxis::Cross => { - self.stack.par.push_hard(ParChild::Spacing(amount)); - } - } - } - /// Apply a forced line break. pub fn linebreak(&mut self) { self.stack.par.push_hard(self.make_text_node('\n')); @@ -126,6 +61,71 @@ impl ExecContext { } } + /// Push text into the active paragraph. + /// + /// The text is split into lines at newlines. + pub fn text(&mut self, text: impl Into) { + self.stack.par.push(self.make_text_node(text)); + } + + /// Push text, but in monospace. + pub fn text_mono(&mut self, text: impl Into) { + let prev = Rc::clone(&self.state.font); + self.state.font_mut().monospace = true; + self.text(text); + self.state.font = prev; + } + + /// Push an inline node into the active paragraph. + pub fn inline(&mut self, node: impl Into) { + let align = self.state.aligns.cross; + self.stack.par.push(ParChild::Any(node.into(), align)); + } + + /// Push a block node into the active stack, finishing the active paragraph. + pub fn block(&mut self, node: impl Into) { + self.parbreak(); + let aligns = self.state.aligns; + self.stack.push(StackChild::Any(node.into(), aligns)); + self.parbreak(); + } + + /// Push spacing into the active paragraph or stack depending on the `axis`. + pub fn spacing(&mut self, axis: GenAxis, amount: Linear) { + match axis { + GenAxis::Main => { + self.stack.finish_par(&self.state); + self.stack.push_hard(StackChild::Spacing(amount)); + } + GenAxis::Cross => { + self.stack.par.push_hard(ParChild::Spacing(amount)); + } + } + } + + /// Execute a template and return the result as a stack node. + pub fn exec_template(&mut self, template: &Template) -> StackNode { + self.exec_to_stack(|ctx| template.exec(ctx)) + } + + /// Execute a syntax tree with a map and return the result as a stack node. + pub fn exec_tree(&mut self, tree: &SyntaxTree, map: &ExprMap) -> StackNode { + self.exec_to_stack(|ctx| tree.exec_with_map(ctx, map)) + } + + /// Execute something and return the result as a stack node. + pub fn exec_to_stack(&mut self, f: impl FnOnce(&mut Self)) -> StackNode { + let snapshot = self.state.clone(); + let page = self.page.take(); + let stack = mem::replace(&mut self.stack, StackBuilder::new(&self.state)); + + f(self); + + self.state = snapshot; + self.page = page; + mem::replace(&mut self.stack, stack).build() + } + /// Finish execution and return the created layout tree. pub fn finish(mut self) -> LayoutTree { assert!(self.page.is_some()); @@ -133,6 +133,8 @@ impl ExecContext { self.tree } + /// Construct a text node with the given text and settings from the active + /// state. fn make_text_node(&self, text: impl Into) -> ParChild { ParChild::Text( text.into(), diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 63e479959..16fd75f83 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -51,8 +51,8 @@ impl ExecWithMap for SyntaxTree { impl ExecWithMap for SyntaxNode { fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) { match self { - Self::Space => ctx.push_word_space(), - Self::Text(text) => ctx.push_text(text), + Self::Space => ctx.space(), + Self::Text(text) => ctx.text(text), Self::Linebreak(_) => ctx.linebreak(), Self::Parbreak(_) => ctx.parbreak(), Self::Strong(_) => ctx.state.font_mut().strong ^= true, @@ -72,7 +72,7 @@ impl Exec for RawNode { ctx.parbreak(); } - ctx.push_monospace_text(&self.text); + ctx.text_mono(&self.text); if self.block { ctx.parbreak(); @@ -112,9 +112,9 @@ impl ExecWithMap for EnumItem { } fn exec_item(ctx: &mut ExecContext, label: EcoString, body: &SyntaxTree, map: &ExprMap) { - let label = ctx.exec_stack(|ctx| ctx.push_text(label)); - let body = ctx.exec_tree_stack(body, map); - ctx.push_into_stack(StackNode { + let label = ctx.exec_to_stack(|ctx| ctx.text(label)); + let body = ctx.exec_tree(body, map); + ctx.block(StackNode { dirs: Gen::new(ctx.state.dirs.main, ctx.state.dirs.cross), aspect: None, children: vec![ @@ -129,13 +129,13 @@ impl Exec for Value { fn exec(&self, ctx: &mut ExecContext) { match self { Value::None => {} - Value::Int(v) => ctx.push_text(v.to_string()), - Value::Float(v) => ctx.push_text(v.to_string()), - Value::Str(v) => ctx.push_text(v), + Value::Int(v) => ctx.text(v.to_string()), + Value::Float(v) => ctx.text(v.to_string()), + Value::Str(v) => ctx.text(v), Value::Template(v) => v.exec(ctx), // For values which can't be shown "naturally", we print the // representation in monospace. - other => ctx.push_monospace_text(other.to_string()), + other => ctx.text_mono(other.to_string()), } } } @@ -153,7 +153,7 @@ impl Exec for TemplateNode { match self { Self::Tree(v) => v.exec(ctx), Self::Func(v) => v.exec(ctx), - Self::Str(v) => ctx.push_text(v), + Self::Str(v) => ctx.text(v), } } } diff --git a/src/layout/frame.rs b/src/layout/frame.rs index 6d5cc2f34..2e3b838e6 100644 --- a/src/layout/frame.rs +++ b/src/layout/frame.rs @@ -19,37 +19,12 @@ pub struct Frame { children: Vec<(Point, Child)>, } -/// An iterator over all elements in a frame, alongside with their positions. -#[derive(Debug, Clone)] -pub struct ElementIter<'a> { - stack: Vec<(usize, Point, &'a Frame)>, -} - -impl<'a> Iterator for ElementIter<'a> { - type Item = (Point, &'a Element); - - /// Get the next element, if any. - fn next(&mut self) -> Option { - let (cursor, offset, frame) = self.stack.last_mut()?; - match frame.children.get(*cursor) { - Some((pos, Child::Frame(f))) => { - let new_offset = *offset + *pos; - self.stack.push((0, new_offset, f.as_ref())); - self.next() - } - Some((pos, Child::Element(e))) => { - *cursor += 1; - Some((*offset + *pos, e)) - } - None => { - self.stack.pop(); - if let Some((cursor, _, _)) = self.stack.last_mut() { - *cursor += 1; - } - self.next() - } - } - } +/// A frame can contain multiple children: elements or other frames, complete +/// with their children. +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +enum Child { + Element(Element), + Frame(Rc), } impl Frame { @@ -86,23 +61,46 @@ impl Frame { } } - /// Wraps the frame with constraints. + /// Wrap the frame with constraints. pub fn constrain(self, constraints: Constraints) -> Constrained> { Constrained { item: Rc::new(self), constraints } } - /// Returns an iterator over all elements in the frame and its children. - pub fn elements(&self) -> ElementIter { - ElementIter { stack: vec![(0, Point::zero(), self)] } + /// An iterator over all elements in the frame and its children. + pub fn elements(&self) -> Elements { + Elements { stack: vec![(0, Point::zero(), self)] } } } -/// A frame can contain multiple children: elements or other frames, complete -/// with their children. -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -enum Child { - Element(Element), - Frame(Rc), +/// An iterator over all elements in a frame, alongside with their positions. +pub struct Elements<'a> { + stack: Vec<(usize, Point, &'a Frame)>, +} + +impl<'a> Iterator for Elements<'a> { + type Item = (Point, &'a Element); + + fn next(&mut self) -> Option { + let (cursor, offset, frame) = self.stack.last_mut()?; + match frame.children.get(*cursor) { + Some((pos, Child::Frame(f))) => { + let new_offset = *offset + *pos; + self.stack.push((0, new_offset, f.as_ref())); + self.next() + } + Some((pos, Child::Element(e))) => { + *cursor += 1; + Some((*offset + *pos, e)) + } + None => { + self.stack.pop(); + if let Some((cursor, _, _)) = self.stack.last_mut() { + *cursor += 1; + } + self.next() + } + } + } } /// The building block frames are composed of. @@ -130,17 +128,6 @@ pub struct Text { pub glyphs: Vec, } -/// A glyph in a run of shaped text. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct Glyph { - /// The glyph's index in the face. - pub id: u16, - /// The advance width of the glyph. - pub x_advance: Length, - /// The horizontal offset of the glyph. - pub x_offset: Length, -} - impl Text { /// Encode the glyph ids into a big-endian byte buffer. pub fn encode_glyphs_be(&self) -> Vec { @@ -153,6 +140,17 @@ impl Text { } } +/// A glyph in a run of shaped text. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Glyph { + /// The glyph's index in the face. + pub id: u16, + /// The advance width of the glyph. + pub x_advance: Length, + /// The horizontal offset of the glyph. + pub x_offset: Length, +} + /// A geometric shape. #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum Geometry { diff --git a/src/library/elements.rs b/src/library/elements.rs index 1ad56a81a..f45770841 100644 --- a/src/library/elements.rs +++ b/src/library/elements.rs @@ -24,7 +24,7 @@ pub fn image(ctx: &mut EvalContext, args: &mut Arguments) -> TypResult { })?; Ok(Value::template(move |ctx| { - ctx.push_into_par(ImageNode { id, width, height }) + ctx.inline(ImageNode { id, width, height }) })) } @@ -62,19 +62,19 @@ fn rect_impl( body: Template, ) -> Value { Value::template(move |ctx| { - let mut stack = ctx.exec_template_stack(&body); + let mut stack = ctx.exec_template(&body); stack.aspect = aspect; let fixed = FixedNode { width, height, child: stack.into() }; if let Some(fill) = fill { - ctx.push_into_par(BackgroundNode { + ctx.inline(BackgroundNode { shape: BackgroundShape::Rect, fill: Paint::Color(fill), child: fixed.into(), }); } else { - ctx.push_into_par(fixed); + ctx.inline(fixed); } }) } @@ -117,7 +117,7 @@ fn ellipse_impl( // perfectly into the ellipse. const PAD: f64 = 0.5 - SQRT_2 / 4.0; - let mut stack = ctx.exec_template_stack(&body); + let mut stack = ctx.exec_template(&body); stack.aspect = aspect; let fixed = FixedNode { @@ -131,13 +131,13 @@ fn ellipse_impl( }; if let Some(fill) = fill { - ctx.push_into_par(BackgroundNode { + ctx.inline(BackgroundNode { shape: BackgroundShape::Ellipse, fill: Paint::Color(fill), child: fixed.into(), }); } else { - ctx.push_into_par(fixed); + ctx.inline(fixed); } }) } diff --git a/src/library/layout.rs b/src/library/layout.rs index 67e58606c..e910139c1 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -82,7 +82,7 @@ pub fn pagebreak(_: &mut EvalContext, _: &mut Arguments) -> TypResult { pub fn h(_: &mut EvalContext, args: &mut Arguments) -> TypResult { let spacing = args.expect("spacing")?; Ok(Value::template(move |ctx| { - ctx.push_spacing(GenAxis::Cross, spacing); + ctx.spacing(GenAxis::Cross, spacing); })) } @@ -90,7 +90,7 @@ pub fn h(_: &mut EvalContext, args: &mut Arguments) -> TypResult { pub fn v(_: &mut EvalContext, args: &mut Arguments) -> TypResult { let spacing = args.expect("spacing")?; Ok(Value::template(move |ctx| { - ctx.push_spacing(GenAxis::Main, spacing); + ctx.spacing(GenAxis::Main, spacing); })) } @@ -134,8 +134,8 @@ pub fn boxed(_: &mut EvalContext, args: &mut Arguments) -> TypResult { let height = args.named("height")?; let body = args.eat().unwrap_or_default(); Ok(Value::template(move |ctx| { - let child = ctx.exec_template_stack(&body).into(); - ctx.push_into_par(FixedNode { width, height, child }); + let child = ctx.exec_template(&body).into(); + ctx.inline(FixedNode { width, height, child }); })) } @@ -143,8 +143,8 @@ pub fn boxed(_: &mut EvalContext, args: &mut Arguments) -> TypResult { pub fn block(_: &mut EvalContext, args: &mut Arguments) -> TypResult { let body = args.expect("body")?; Ok(Value::template(move |ctx| { - let block = ctx.exec_template_stack(&body); - ctx.push_into_stack(block); + let block = ctx.exec_template(&body); + ctx.block(block); })) } @@ -165,8 +165,8 @@ pub fn pad(_: &mut EvalContext, args: &mut Arguments) -> TypResult { ); Ok(Value::template(move |ctx| { - let child = ctx.exec_template_stack(&body).into(); - ctx.push_into_stack(PadNode { padding, child }); + let child = ctx.exec_template(&body).into(); + ctx.block(PadNode { padding, child }); })) } @@ -179,7 +179,7 @@ pub fn stack(_: &mut EvalContext, args: &mut Arguments) -> TypResult { let children = children .iter() .map(|child| { - let child = ctx.exec_template_stack(child).into(); + let child = ctx.exec_template(child).into(); StackChild::Any(child, ctx.state.aligns) }) .collect(); @@ -192,7 +192,7 @@ pub fn stack(_: &mut EvalContext, args: &mut Arguments) -> TypResult { dirs.cross = ctx.state.dirs.main; } - ctx.push_into_stack(StackNode { dirs, aspect: None, children }); + ctx.block(StackNode { dirs, aspect: None, children }); })) } @@ -220,10 +220,8 @@ pub fn grid(_: &mut EvalContext, args: &mut Arguments) -> TypResult { ); Ok(Value::template(move |ctx| { - let children = children - .iter() - .map(|child| ctx.exec_template_stack(child).into()) - .collect(); + let children = + children.iter().map(|child| ctx.exec_template(child).into()).collect(); let mut dirs = Gen::new(column_dir, row_dir).unwrap_or(ctx.state.dirs); @@ -243,7 +241,7 @@ pub fn grid(_: &mut EvalContext, args: &mut Arguments) -> TypResult { }; } - ctx.push_into_stack(GridNode { + ctx.block(GridNode { dirs, tracks: tracks.clone(), gutter: gutter.clone(),