From d1c07260c0b67098a1b778262c76e6f31c5a5240 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 5 Oct 2020 13:39:33 +0200 Subject: [PATCH] =?UTF-8?q?Move=20align=20out=20of=20BoxLayout=20?= =?UTF-8?q?=F0=9F=8D=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layout/line.rs | 33 +++++++++++++-------------------- src/layout/mod.rs | 10 ++++------ src/layout/stack.rs | 31 ++++++++++++------------------- src/layout/tree.rs | 42 ++++++++++++++++++++++++++++-------------- src/library/boxed.rs | 3 ++- src/shaping.rs | 37 ++++++++++++------------------------- 6 files changed, 71 insertions(+), 85 deletions(-) diff --git a/src/layout/line.rs b/src/layout/line.rs index baef9fb01..19ec053df 100644 --- a/src/layout/line.rs +++ b/src/layout/line.rs @@ -23,14 +23,10 @@ pub struct LineLayouter { /// The context for line layouting. #[derive(Debug, Clone)] pub struct LineContext { - /// The spaces to layout into. - pub spaces: Vec, /// The initial layouting system, which can be updated through `set_sys`. pub sys: LayoutSystem, - /// The alignment of the _resulting_ layout. This does not effect the line - /// layouting itself, but rather how the finished layout will be positioned - /// in a parent layout. - pub align: LayoutAlign, + /// The spaces to layout into. + pub spaces: Vec, /// Whether to spill over into copies of the last space or finish layouting /// when the last space is used up. pub repeat: bool, @@ -45,7 +41,6 @@ impl LineLayouter { stack: StackLayouter::new(StackContext { spaces: ctx.spaces.clone(), sys: ctx.sys, - align: ctx.align, repeat: ctx.repeat, }), ctx, @@ -54,26 +49,26 @@ impl LineLayouter { } /// Add a layout. - pub fn add(&mut self, layout: BoxLayout) { + pub fn add(&mut self, layout: BoxLayout, align: LayoutAlign) { let sys = self.ctx.sys; - if let Some(align) = self.run.align { - if layout.align.secondary != align.secondary { + if let Some(prev) = self.run.align { + if align.secondary != prev.secondary { // TODO: Issue warning for non-fitting alignment in // non-repeating context. - let fitting = self.stack.is_fitting_alignment(layout.align); + let fitting = self.stack.is_fitting_alignment(align); if !fitting && self.ctx.repeat { self.finish_space(true); } else { self.finish_line(); } - } else if layout.align.primary < align.primary { + } else if align.primary < prev.primary { self.finish_line(); - } else if layout.align.primary > align.primary { + } else if align.primary > prev.primary { let mut rest_run = LineRun::new(); let usable = self.stack.usable().primary(sys); - rest_run.usable = Some(match layout.align.primary { + rest_run.usable = Some(match align.primary { GenAlign::Start => unreachable!("start > x"), GenAlign::Center => usable - 2.0 * self.run.size.width, GenAlign::End => usable - self.run.size.width, @@ -105,7 +100,7 @@ impl LineLayouter { } } - self.run.align = Some(layout.align); + self.run.align = Some(align); self.run.layouts.push((self.run.size.width, layout)); self.run.size.width += size.width; @@ -211,10 +206,8 @@ impl LineLayouter { /// Finish the active line and start a new one. pub fn finish_line(&mut self) { - let mut layout = BoxLayout::new( - self.run.size.specialized(self.ctx.sys), - self.run.align.unwrap_or_default(), - ); + let mut layout = BoxLayout::new(self.run.size.specialized(self.ctx.sys)); + let align = self.run.align.unwrap_or_default(); let layouts = std::mem::take(&mut self.run.layouts); for (offset, child) in layouts { @@ -227,7 +220,7 @@ impl LineLayouter { layout.push_layout(pos, child); } - self.stack.add(layout); + self.stack.add(layout, align); self.run = LineRun::new(); self.stack.add_spacing(self.ctx.line_spacing, SpacingKind::LINE); diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 52f7e0e61..7803c7476 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -54,16 +54,14 @@ pub async fn layout( pub struct BoxLayout { /// The size of the box. pub size: Size, - /// How to align this box in a parent container. - pub align: LayoutAlign, /// The elements composing this layout. pub elements: Vec<(Point, LayoutElement)>, } impl BoxLayout { - /// Create an new empty collection. - pub fn new(size: Size, align: LayoutAlign) -> Self { - Self { size, align, elements: vec![] } + /// Create a new empty collection. + pub fn new(size: Size) -> Self { + Self { size, elements: vec![] } } /// Add an element at a position. @@ -161,7 +159,7 @@ pub enum Command { LayoutSyntaxTree(SynTree), /// Add a finished layout. - Add(BoxLayout), + Add(BoxLayout, LayoutAlign), /// Add spacing of the given kind along the primary or secondary axis. The /// kind defines how the spacing interacts with surrounding spacing. AddSpacing(f64, SpacingKind, GenAxis), diff --git a/src/layout/stack.rs b/src/layout/stack.rs index dadc40b93..04c78da56 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -34,14 +34,10 @@ pub struct StackLayouter { /// The context for stack layouting. #[derive(Debug, Clone)] pub struct StackContext { - /// The spaces to layout into. - pub spaces: Vec, /// The initial layouting system, which can be updated through `set_sys`. pub sys: LayoutSystem, - /// The alignment of the _resulting_ layout. This does not effect the line - /// layouting itself, but rather how the finished layout will be positioned - /// in a parent layout. - pub align: LayoutAlign, + /// The spaces to layout into. + pub spaces: Vec, /// Whether to spill over into copies of the last space or finish layouting /// when the last space is used up. pub repeat: bool, @@ -59,11 +55,11 @@ impl StackLayouter { } /// Add a layout to the stack. - pub fn add(&mut self, layout: BoxLayout) { + pub fn add(&mut self, layout: BoxLayout, align: LayoutAlign) { // If the alignment cannot be fitted in this space, finish it. // TODO: Issue warning for non-fitting alignment in non-repeating // context. - if !self.update_rulers(layout.align) && self.ctx.repeat { + if !self.update_rulers(align) && self.ctx.repeat { self.finish_space(true); } @@ -84,7 +80,7 @@ impl StackLayouter { // Add the box to the vector and remember that spacings are allowed // again. - self.space.layouts.push((self.ctx.sys, layout)); + self.space.layouts.push((self.ctx.sys, align, layout)); self.space.last_spacing = LastSpacing::None; } @@ -100,10 +96,8 @@ impl StackLayouter { self.update_metrics(size); self.space.layouts.push(( self.ctx.sys, - BoxLayout::new( - size.specialized(self.ctx.sys), - LayoutAlign::default(), - ), + LayoutAlign::default(), + BoxLayout::new(size.specialized(self.ctx.sys)), )); self.space.last_spacing = LastSpacing::Hard; @@ -279,7 +273,7 @@ impl StackLayouter { y1: start.y + self.space.size.height, }; - for (sys, layout) in &self.space.layouts { + for (sys, _, layout) in &self.space.layouts { // First, we store the bounds calculated so far (which were reduced // by the predecessors of this layout) as the initial bounding box // of this layout. @@ -303,7 +297,7 @@ impl StackLayouter { let mut rotation = SpecAxis::Vertical; for (bound, entry) in bounds.iter_mut().zip(&self.space.layouts).rev() { - let (sys, layout) = entry; + let (sys, _, layout) = entry; // When the axes are rotated, the maximal primary size (`extent.x`) // dictates how much secondary extent the whole run had. This value @@ -331,12 +325,11 @@ impl StackLayouter { // Step 4: Align each layout in its bounding box and collect everything // into a single finished layout. - let mut layout = BoxLayout::new(size, self.ctx.align); + let mut layout = BoxLayout::new(size); let layouts = std::mem::take(&mut self.space.layouts); - for ((sys, child), bound) in layouts.into_iter().zip(bounds) { + for ((sys, align, child), bound) in layouts.into_iter().zip(bounds) { let size = child.size.specialized(sys); - let align = child.align; // The space in which this layout is aligned is given by the // distances between the borders of its bounding box. @@ -373,7 +366,7 @@ struct Space { /// Whether to include a layout for this space even if it would be empty. hard: bool, /// The so-far accumulated layouts. - layouts: Vec<(LayoutSystem, BoxLayout)>, + layouts: Vec<(LayoutSystem, LayoutAlign, BoxLayout)>, /// The specialized size of this space. size: Size, /// The specialized remaining space. diff --git a/src/layout/tree.rs b/src/layout/tree.rs index 4e15fb12b..fde7833e9 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -1,5 +1,7 @@ //! Layouting of syntax trees. +use fontdock::FontStyle; + use super::*; use crate::eval::Eval; use crate::shaping; @@ -25,7 +27,6 @@ impl<'a> TreeLayouter<'a> { let layouter = LineLayouter::new(LineContext { spaces: ctx.constraints.spaces.clone(), sys: ctx.state.sys, - align: ctx.state.align, repeat: ctx.constraints.repeat, line_spacing: ctx.state.text.line_spacing(), }); @@ -99,16 +100,31 @@ impl<'a> TreeLayouter<'a> { } async fn layout_text(&mut self, text: &str) { - self.layouter.add( - shaping::shape( - text, - self.ctx.state.sys.primary, - self.ctx.state.align, - &self.ctx.state.text, - &mut self.ctx.loader.borrow_mut(), - ) - .await, - ); + 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.sys.primary, + self.ctx.state.text.font_size(), + variant, + &self.ctx.state.text.fallback, + &mut self.ctx.loader.borrow_mut(), + ) + .await; + + self.layouter.add(boxed, self.ctx.state.align); } async fn layout_heading(&mut self, heading: &NodeHeading) { @@ -160,9 +176,7 @@ impl<'a> TreeLayouter<'a> { }; 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; } @@ -173,7 +187,7 @@ impl<'a> TreeLayouter<'a> { match command { LayoutSyntaxTree(tree) => self.layout_tree(&tree).await, - Add(layout) => self.layouter.add(layout), + Add(layout, align) => self.layouter.add(layout, align), AddSpacing(space, kind, axis) => match axis { GenAxis::Primary => self.layouter.add_primary_spacing(space, kind), GenAxis::Secondary => self.layouter.add_secondary_spacing(space, kind), diff --git a/src/library/boxed.rs b/src/library/boxed.rs index 085176e07..caeb206e2 100644 --- a/src/library/boxed.rs +++ b/src/library/boxed.rs @@ -12,6 +12,7 @@ pub async fn boxed(mut args: Args, ctx: &mut LayoutContext) -> Value { let height = args.get::<_, Linear>(ctx, "height"); args.done(ctx); + let align = ctx.state.align; let constraints = &mut ctx.constraints; constraints.base = constraints.spaces[0].size; constraints.spaces.truncate(1); @@ -34,5 +35,5 @@ pub async fn boxed(mut args: Args, ctx: &mut LayoutContext) -> Value { let layouted = layout_tree(&body, ctx).await; let layout = layouted.into_iter().next().unwrap(); - Value::Commands(vec![Add(layout)]) + Value::Commands(vec![Add(layout, align)]) } diff --git a/src/shaping.rs b/src/shaping.rs index f56fa4441..7c60e0d20 100644 --- a/src/shaping.rs +++ b/src/shaping.rs @@ -6,23 +6,23 @@ use std::fmt::{self, Debug, Formatter}; -use fontdock::{FaceId, FaceQuery, FallbackTree, FontStyle, FontVariant}; +use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant}; use ttf_parser::GlyphId; -use crate::eval::TextState; use crate::font::FontLoader; use crate::geom::{Point, Size}; -use crate::layout::{BoxLayout, Dir, LayoutAlign, LayoutElement}; +use crate::layout::{BoxLayout, Dir, LayoutElement}; /// Shape text into a box containing shaped runs. pub async fn shape( text: &str, dir: Dir, - align: LayoutAlign, - state: &TextState, + size: f64, + variant: FontVariant, + fallback: &FallbackTree, loader: &mut FontLoader, ) -> BoxLayout { - Shaper::new(text, dir, align, state, loader).shape().await + Shaper::new(text, dir, size, variant, fallback, loader).shape().await } /// A shaped run of text. @@ -86,32 +86,19 @@ impl<'a> Shaper<'a> { fn new( text: &'a str, dir: Dir, - align: LayoutAlign, - state: &'a TextState, + size: f64, + variant: FontVariant, + fallback: &'a FallbackTree, loader: &'a mut FontLoader, ) -> Self { - let mut variant = state.variant; - - if state.strong { - variant.weight = variant.weight.thicken(300); - } - - if state.emph { - variant.style = match variant.style { - FontStyle::Normal => FontStyle::Italic, - FontStyle::Italic => FontStyle::Normal, - FontStyle::Oblique => FontStyle::Normal, - } - } - Self { text, dir, variant, - fallback: &state.fallback, + fallback, loader, - shaped: Shaped::new(FaceId::MAX, state.font_size()), - layout: BoxLayout::new(Size::new(0.0, state.font_size()), align), + shaped: Shaped::new(FaceId::MAX, size), + layout: BoxLayout::new(Size::new(0.0, size)), offset: 0.0, } }