diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-10-07 17:07:44 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-10-07 17:07:44 +0200 |
| commit | 537545e7f8351d7677c396456e46568f5a5e2a7a (patch) | |
| tree | f4c7614293246db06c7fa7496458da01b15c3b84 /src/layout/tree.rs | |
| parent | ca1256c924f3672feb76dbc2bc2e309eb4fc4cf5 (diff) | |
Evaluation and node-based layouting 🚀
Diffstat (limited to 'src/layout/tree.rs')
| -rw-r--r-- | src/layout/tree.rs | 234 |
1 files changed, 0 insertions, 234 deletions
diff --git a/src/layout/tree.rs b/src/layout/tree.rs deleted file mode 100644 index 4e53cedc..00000000 --- a/src/layout/tree.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! Layouting of syntax trees. - -use fontdock::FontStyle; - -use super::*; -use crate::eval::Eval; -use crate::shaping; -use crate::syntax::*; -use crate::DynFuture; - -/// Layout a syntax tree in a given context. -pub async fn layout_tree(tree: &SynTree, ctx: &mut LayoutContext) -> Vec<BoxLayout> { - let mut layouter = TreeLayouter::new(ctx); - layouter.layout_tree(tree).await; - layouter.finish() -} - -/// Layouts trees. -struct TreeLayouter<'a> { - ctx: &'a mut LayoutContext, - constraints: LayoutConstraints, - layouter: LineLayouter, -} - -impl<'a> TreeLayouter<'a> { - fn new(ctx: &'a mut LayoutContext) -> Self { - let layouter = LineLayouter::new(LineContext { - spaces: ctx.constraints.spaces.clone(), - dirs: ctx.state.dirs, - repeat: ctx.constraints.repeat, - line_spacing: ctx.state.text.line_spacing(), - }); - - Self { - layouter, - constraints: ctx.constraints.clone(), - ctx, - } - } - - fn finish(self) -> Vec<BoxLayout> { - self.layouter.finish() - } - - fn layout_tree<'t>(&'t mut self, tree: &'t SynTree) -> DynFuture<'t, ()> { - Box::pin(async move { - for node in tree { - self.layout_node(node).await; - } - }) - } - - async fn layout_node(&mut self, node: &Spanned<SynNode>) { - let decorate = |this: &mut Self, deco: Deco| { - this.ctx.f.decos.push(deco.span_with(node.span)); - }; - - match &node.v { - SynNode::Space => self.layout_space(), - SynNode::Text(text) => { - if self.ctx.state.text.emph { - decorate(self, Deco::Emph); - } - if self.ctx.state.text.strong { - decorate(self, Deco::Strong); - } - self.layout_text(text).await; - } - - SynNode::Linebreak => self.layouter.finish_line(), - SynNode::Parbreak => self.layout_parbreak(), - SynNode::Emph => { - self.ctx.state.text.emph ^= true; - decorate(self, Deco::Emph); - } - SynNode::Strong => { - self.ctx.state.text.strong ^= true; - decorate(self, Deco::Strong); - } - - SynNode::Heading(heading) => self.layout_heading(heading).await, - SynNode::Raw(raw) => self.layout_raw(raw).await, - - SynNode::Expr(expr) => { - self.layout_expr(expr.span_with(node.span)).await; - } - } - } - - fn layout_space(&mut self) { - self.layouter - .add_cross_spacing(self.ctx.state.text.word_spacing(), SpacingKind::WORD); - } - - fn layout_parbreak(&mut self) { - self.layouter - .add_main_spacing(self.ctx.state.text.par_spacing(), SpacingKind::PARAGRAPH); - } - - async fn layout_text(&mut self, text: &str) { - let mut variant = self.ctx.state.text.variant; - - if self.ctx.state.text.strong { - variant.weight = variant.weight.thicken(300); - } - - if self.ctx.state.text.emph { - variant.style = match variant.style { - FontStyle::Normal => FontStyle::Italic, - FontStyle::Italic => FontStyle::Normal, - FontStyle::Oblique => FontStyle::Normal, - } - } - - let boxed = shaping::shape( - text, - self.ctx.state.text.font_size(), - self.ctx.state.dirs.cross, - &mut self.ctx.loader.borrow_mut(), - &self.ctx.state.text.fallback, - variant, - ) - .await; - - self.layouter.add(boxed, self.ctx.state.aligns); - } - - async fn layout_heading(&mut self, heading: &NodeHeading) { - let style = self.ctx.state.text.clone(); - - let factor = 1.5 - 0.1 * heading.level.v as f64; - self.ctx.state.text.font_size.scale *= factor; - self.ctx.state.text.strong = true; - - self.layout_parbreak(); - self.layout_tree(&heading.contents).await; - self.layout_parbreak(); - - self.ctx.state.text = style; - } - - async fn layout_raw(&mut self, raw: &NodeRaw) { - if !raw.inline { - self.layout_parbreak(); - } - - // TODO: Make this more efficient. - let fallback = self.ctx.state.text.fallback.clone(); - self.ctx.state.text.fallback.list.insert(0, "monospace".to_string()); - self.ctx.state.text.fallback.flatten(); - - let mut first = true; - for line in &raw.lines { - if !first { - self.layouter.finish_line(); - } - first = false; - self.layout_text(line).await; - } - - self.ctx.state.text.fallback = fallback; - - if !raw.inline { - self.layout_parbreak(); - } - } - - async fn layout_expr(&mut self, expr: Spanned<&Expr>) { - self.ctx.constraints = LayoutConstraints { - root: false, - base: self.constraints.base, - spaces: self.layouter.remaining(), - repeat: self.constraints.repeat, - }; - - let val = expr.v.eval(self.ctx).await; - let commands = val.span_with(expr.span).into_commands(); - for command in commands { - self.execute_command(command, expr.span).await; - } - } - - async fn execute_command(&mut self, command: Command, span: Span) { - use Command::*; - match command { - LayoutSyntaxTree(tree) => self.layout_tree(&tree).await, - - Add(layout, aligns) => self.layouter.add(layout, aligns), - AddSpacing(space, kind, axis) => match axis { - GenAxis::Main => self.layouter.add_main_spacing(space, kind), - GenAxis::Cross => self.layouter.add_cross_spacing(space, kind), - }, - - BreakLine => self.layouter.finish_line(), - BreakPage => { - if self.constraints.root { - self.layouter.finish_space(true) - } else { - self.ctx.diag(error!( - span, - "page break can only be issued from root context", - )); - } - } - - SetTextState(style) => { - self.layouter.set_line_spacing(style.line_spacing()); - self.ctx.state.text = style; - } - SetPageState(style) => { - if self.constraints.root { - self.ctx.state.page = style; - - // The line layouter has no idea of page styles and thus we - // need to recompute the layouting space resulting of the - // new page style and update it within the layouter. - let space = LayoutSpace { - size: style.size, - insets: style.insets(), - expansion: Spec2::new(true, true), - }; - self.constraints.base = space.usable(); - self.layouter.set_spaces(vec![space], true); - } else { - self.ctx.diag(error!( - span, - "page style can only be changed from root context", - )); - } - } - SetAlignment(aligns) => self.ctx.state.aligns = aligns, - } - } -} |
