diff options
Diffstat (limited to 'src/eval/mod.rs')
| -rw-r--r-- | src/eval/mod.rs | 154 |
1 files changed, 99 insertions, 55 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 62ce6c20..71a09749 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -22,7 +22,7 @@ use fontdock::FontStyle; use crate::diag::Diag; use crate::diag::{Deco, Feedback, Pass}; -use crate::geom::{Gen, Length, Relative}; +use crate::geom::{Align, Dir, Gen, Length, Linear, Relative, Sides, Size}; use crate::layout::{ Document, Expansion, LayoutNode, Pad, Pages, Par, Softness, Spacing, Stack, Text, }; @@ -113,64 +113,36 @@ impl EvalContext { self.inner.push(node); } - /// Start a layouting group. - /// - /// All further calls to [`push`] will collect nodes for this group. - /// The given metadata will be returned alongside the collected nodes - /// in a matching call to [`end_group`]. - /// - /// [`push`]: #method.push - /// [`end_group`]: #method.end_group - pub fn start_group<T: 'static>(&mut self, meta: T) { - self.groups.push((Box::new(meta), std::mem::take(&mut self.inner))); - } - - /// End a layouting group started with [`start_group`]. - /// - /// This returns the stored metadata and the collected nodes. - /// - /// [`start_group`]: #method.start_group - pub fn end_group<T: 'static>(&mut self) -> (T, Vec<LayoutNode>) { - if let Some(&LayoutNode::Spacing(spacing)) = self.inner.last() { - if spacing.softness == Softness::Soft { - self.inner.pop(); - } - } - - let (any, outer) = self.groups.pop().expect("no pushed group"); - let group = *any.downcast::<T>().expect("bad group type"); - (group, std::mem::replace(&mut self.inner, outer)) - } - - /// Start a page run group based on the active page state. + /// Start a page group based on the active page state. /// /// If `hard` is false, empty page runs will be omitted from the output. /// /// This also starts an inner paragraph. pub fn start_page_group(&mut self, hard: bool) { - let size = self.state.page.size; - let margins = self.state.page.margins(); - let dirs = self.state.dirs; - let aligns = self.state.aligns; - self.start_group((size, margins, dirs, aligns, hard)); + self.start_group(PageGroup { + size: self.state.page.size, + padding: self.state.page.margins(), + dirs: self.state.dirs, + aligns: self.state.aligns, + hard, + }); self.start_par_group(); } - /// End a page run group and push it to its parent group. + /// End a page group and push it to the finished page runs. /// /// This also ends an inner paragraph. pub fn end_page_group(&mut self) { self.end_par_group(); - let ((size, padding, dirs, aligns, hard), children) = self.end_group(); - let hard: bool = hard; - if hard || !children.is_empty() { + let (group, children) = self.end_group::<PageGroup>(); + if group.hard || !children.is_empty() { self.runs.push(Pages { - size, + size: group.size, child: LayoutNode::dynamic(Pad { - padding, + padding: group.padding, child: LayoutNode::dynamic(Stack { - dirs, - aligns, + dirs: group.dirs, + aligns: group.aligns, expansion: Gen::new(Expansion::Fill, Expansion::Fill), children, }), @@ -179,32 +151,78 @@ impl EvalContext { } } + /// Start a content group. + /// + /// This also starts an inner paragraph. + pub fn start_content_group(&mut self) { + self.start_group(ContentGroup); + self.start_par_group(); + } + + /// End a content group and return the resulting nodes. + /// + /// This also ends an inner paragraph. + pub fn end_content_group(&mut self) -> Vec<LayoutNode> { + self.end_par_group(); + self.end_group::<ContentGroup>().1 + } + /// Start a paragraph group based on the active text state. pub fn start_par_group(&mut self) { - let dirs = self.state.dirs; let em = self.state.font.font_size(); - let line_spacing = self.state.par.line_spacing.eval(em); - let aligns = self.state.aligns; - self.start_group((dirs, line_spacing, aligns)); + self.start_group(ParGroup { + dirs: self.state.dirs, + aligns: self.state.aligns, + line_spacing: self.state.par.line_spacing.eval(em), + }); } - /// End a paragraph group and push it to its parent group if its not empty. + /// End a paragraph group and push it to its parent group if it's not empty. pub fn end_par_group(&mut self) { - let ((dirs, line_spacing, aligns), children) = self.end_group(); + let (group, children) = self.end_group::<ParGroup>(); if !children.is_empty() { - // FIXME: This is a hack and should be superseded by constraints - // having min and max size. + // FIXME: This is a hack and should be superseded by something + // better. let cross_expansion = Expansion::fill_if(self.groups.len() <= 1); self.push(Par { - dirs, - aligns, + dirs: group.dirs, + aligns: group.aligns, cross_expansion, - line_spacing, + line_spacing: group.line_spacing, children, }); } } + /// Start a layouting group. + /// + /// All further calls to [`push`] will collect nodes for this group. + /// The given metadata will be returned alongside the collected nodes + /// in a matching call to [`end_group`]. + /// + /// [`push`]: #method.push + /// [`end_group`]: #method.end_group + fn start_group<T: 'static>(&mut self, meta: T) { + self.groups.push((Box::new(meta), std::mem::take(&mut self.inner))); + } + + /// End a layouting group started with [`start_group`]. + /// + /// This returns the stored metadata and the collected nodes. + /// + /// [`start_group`]: #method.start_group + fn end_group<T: 'static>(&mut self) -> (T, Vec<LayoutNode>) { + if let Some(&LayoutNode::Spacing(spacing)) = self.inner.last() { + if spacing.softness == Softness::Soft { + self.inner.pop(); + } + } + + let (any, outer) = self.groups.pop().expect("no pushed group"); + let group = *any.downcast::<T>().expect("bad group type"); + (group, std::mem::replace(&mut self.inner, outer)) + } + /// Construct a text node from the given string based on the active text /// state. pub fn make_text_node(&self, text: String) -> Text { @@ -233,6 +251,25 @@ impl EvalContext { } } +/// A group for page runs. +struct PageGroup { + size: Size, + padding: Sides<Linear>, + dirs: Gen<Dir>, + aligns: Gen<Align>, + hard: bool, +} + +/// A group for generic content. +struct ContentGroup; + +/// A group for paragraphs. +struct ParGroup { + dirs: Gen<Dir>, + aligns: Gen<Align>, + line_spacing: Length, +} + /// Evaluate an item. /// /// _Note_: Evaluation is not necessarily pure, it may change the active state. @@ -320,9 +357,16 @@ impl Eval for NodeRaw { families.list.insert(0, "monospace".to_string()); families.flatten(); + let em = ctx.state.font.font_size(); + let line_spacing = ctx.state.par.line_spacing.eval(em); + let mut children = vec![]; for line in &self.lines { children.push(LayoutNode::Text(ctx.make_text_node(line.clone()))); + children.push(LayoutNode::Spacing(Spacing { + amount: line_spacing, + softness: Softness::Hard, + })); } ctx.push(Stack { |
