diff --git a/src/layout/flex.rs b/src/layout/flex.rs index c3d88e1b6..053954a46 100644 --- a/src/layout/flex.rs +++ b/src/layout/flex.rs @@ -14,7 +14,7 @@ pub struct FlexLayouter { #[derive(Debug, Clone)] enum FlexUnit { Boxed(Layout), - Space(Size, bool), + Space(Size, SpaceKind), SetAxes(LayoutAxes), Break, } @@ -102,15 +102,15 @@ impl FlexLayouter { self.units.push(FlexUnit::Break); } - pub fn add_primary_space(&mut self, space: Size, soft: bool) { - self.units.push(FlexUnit::Space(space, soft)) + pub fn add_primary_space(&mut self, space: Size, kind: SpaceKind) { + self.units.push(FlexUnit::Space(space, kind)) } - pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> { + pub fn add_secondary_space(&mut self, space: Size, kind: SpaceKind) -> LayoutResult<()> { if !self.run_is_empty() { self.finish_run()?; } - Ok(self.stack.add_space(space, soft)) + Ok(self.stack.add_space(space, kind)) } pub fn set_axes(&mut self, axes: LayoutAxes) { @@ -169,7 +169,7 @@ impl FlexLayouter { for unit in units { match unit { FlexUnit::Boxed(boxed) => self.layout_box(boxed)?, - FlexUnit::Space(space, soft) => self.layout_space(space, soft), + FlexUnit::Space(space, kind) => self.layout_space(space, kind), FlexUnit::SetAxes(axes) => self.layout_set_axes(axes), FlexUnit::Break => { self.finish_line()?; }, } @@ -186,13 +186,13 @@ impl FlexLayouter { } self.stack.add(Layout { - dimensions: self.axes.specialize( - self.line.combined_dimensions+ Size2D::with_y(self.flex_spacing) - ), + dimensions: self.axes.specialize(self.line.combined_dimensions), actions: self.line.actions.to_vec(), debug_render: false, })?; + self.stack.add_space(self.flex_spacing, SpaceKind::Independent); + let remaining = self.axes.specialize(Size2D { x: self.part.usable - self.part.dimensions.x @@ -250,7 +250,7 @@ impl FlexLayouter { } if let SpaceState::Soft(space) = self.part.space { - self.layout_space(space, false); + self.layout_space(space, SpaceKind::Hard); } let offset = self.part.dimensions.x; @@ -263,8 +263,8 @@ impl FlexLayouter { Ok(()) } - fn layout_space(&mut self, space: Size, soft: bool) { - if soft { + fn layout_space(&mut self, space: Size, kind: SpaceKind) { + if kind == SpaceKind::Soft { if self.part.space != SpaceState::Forbidden { self.part.space = SpaceState::Soft(space); } @@ -275,7 +275,9 @@ impl FlexLayouter { self.part.dimensions.x += space; } - self.part.space = SpaceState::Forbidden; + if kind == SpaceKind::Hard { + self.part.space = SpaceState::Forbidden; + } } } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index e78e60673..2e6432956 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -308,7 +308,17 @@ pub enum Alignment { } #[derive(Debug, Copy, Clone, PartialEq)] -pub enum SpaceState { +pub enum SpaceKind { + /// Soft spaces are eaten up by hard spaces before or after them. + Soft, + /// Independent do not eat up soft spaces and are not eaten up by hard spaces. + Independent, + /// Hard spaces eat up soft spaces before or after them. + Hard, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +enum SpaceState { Soft(Size), Forbidden, Allowed, diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs index 55e1b2b8a..a6999dee6 100644 --- a/src/layout/stacked.rs +++ b/src/layout/stacked.rs @@ -32,10 +32,14 @@ impl Space { #[derive(Debug, Clone)] struct Subspace { origin: Size2D, - usable: Size2D, anchor: Size2D, factor: i32, + + boxes: Vec<(Size, Size, Layout)>, + + usable: Size2D, dimensions: Size2D, + space: SpaceState, } @@ -43,9 +47,10 @@ impl Subspace { fn new(origin: Size2D, usable: Size2D, axes: LayoutAxes) -> Subspace { Subspace { origin, - usable: axes.generalize(usable), anchor: axes.anchor(usable), factor: axes.secondary.axis.factor(), + boxes: vec![], + usable: axes.generalize(usable), dimensions: Size2D::zero(), space: SpaceState::Forbidden, } @@ -78,7 +83,7 @@ impl StackLayouter { pub fn add(&mut self, layout: Layout) -> LayoutResult<()> { if let SpaceState::Soft(space) = self.sub.space { - self.add_space(space, false); + self.add_space(space, SpaceKind::Hard); } let size = self.ctx.axes.generalize(layout.dimensions); @@ -90,9 +95,6 @@ impl StackLayouter { while !self.sub.usable.fits(new_dimensions) { if self.space_is_last() && self.space_is_empty() { - println!("usable: {}", self.sub.usable); - println!("dims: {}", new_dimensions); - println!("size: {}", size); Err(LayoutError::NotEnoughSpace("failed to add box to stack"))?; } @@ -101,14 +103,9 @@ impl StackLayouter { } let offset = self.sub.dimensions.y; - let anchor = self.ctx.axes.anchor(size); + let anchor = self.ctx.axes.primary.anchor(size.x); - let pos = self.sub.origin + self.ctx.axes.specialize( - (self.sub.anchor - anchor) - + Size2D::with_y(self.space.combined_dimensions.y + self.sub.factor * offset) - ); - - self.space.actions.add_layout(pos, layout); + self.sub.boxes.push((offset, anchor, layout)); self.sub.dimensions = new_dimensions; self.sub.space = SpaceState::Allowed; @@ -122,8 +119,8 @@ impl StackLayouter { Ok(()) } - pub fn add_space(&mut self, space: Size, soft: bool) { - if soft { + pub fn add_space(&mut self, space: Size, kind: SpaceKind) { + if kind == SpaceKind::Soft { if self.sub.space != SpaceState::Forbidden { self.sub.space = SpaceState::Soft(space); } @@ -134,7 +131,9 @@ impl StackLayouter { self.sub.dimensions.y += space; } - self.sub.space = SpaceState::Forbidden; + if kind == SpaceKind::Hard { + self.sub.space = SpaceState::Forbidden; + } } } @@ -220,6 +219,20 @@ impl StackLayouter { } fn finish_subspace(&mut self) { + let factor = self.ctx.axes.secondary.axis.factor(); + let anchor = + self.ctx.axes.anchor(self.sub.usable) + - self.ctx.axes.anchor(Size2D::with_y(self.sub.dimensions.y)); + + for (offset, layout_anchor, layout) in self.sub.boxes.drain(..) { + let pos = self.sub.origin + + self.ctx.axes.specialize( + anchor + Size2D::new(-layout_anchor, factor * offset) + ); + + self.space.actions.add_layout(pos, layout); + } + if self.ctx.axes.primary.needs_expansion() { self.sub.dimensions.x = self.sub.usable.x; } diff --git a/src/layout/tree.rs b/src/layout/tree.rs index f6a8f4087..f5ae24351 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -58,16 +58,19 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { } fn layout_space(&mut self) { - self.flex.add_primary_space(word_spacing(&self.style), true); + self.flex.add_primary_space(word_spacing(&self.style), SpaceKind::Soft); } fn layout_paragraph(&mut self) -> LayoutResult<()> { - self.flex.add_secondary_space(paragraph_spacing(&self.style), true) + self.flex.add_secondary_space(paragraph_spacing(&self.style), SpaceKind::Soft) } fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> { let (first, second) = self.flex.remaining()?; + let mut axes = self.ctx.axes.expanding(false); + axes.secondary.alignment = Alignment::Origin; + let ctx = |spaces| { LayoutContext { loader: self.ctx.loader, @@ -75,7 +78,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { text_style: &self.style, page_style: self.ctx.page_style, spaces, - axes: self.ctx.axes.expanding(false), + axes, expand: false, } }; @@ -106,8 +109,10 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { Command::Add(layout) => self.flex.add(layout), Command::AddMultiple(layouts) => self.flex.add_multiple(layouts), - Command::AddPrimarySpace(space) => self.flex.add_primary_space(space, false), - Command::AddSecondarySpace(space) => self.flex.add_secondary_space(space, false)?, + Command::AddPrimarySpace(space) + => self.flex.add_primary_space(space, SpaceKind::Hard), + Command::AddSecondarySpace(space) + => self.flex.add_secondary_space(space, SpaceKind::Hard)?, Command::FinishLine => self.flex.add_break(), Command::FinishRun => { self.flex.finish_run()?; }, diff --git a/src/library/axes.rs b/src/library/axes.rs index 09c2f7d29..29b624abd 100644 --- a/src/library/axes.rs +++ b/src/library/axes.rs @@ -4,6 +4,8 @@ use crate::func::prelude::*; #[derive(Debug, PartialEq)] pub struct Align { body: Option, + positional_1: Option, + positional_2: Option, primary: Option, secondary: Option, horizontal: Option, @@ -29,6 +31,8 @@ function! { let mut align = Align { body, + positional_1: None, + positional_2: None, primary: None, secondary: None, horizontal: None, @@ -36,7 +40,11 @@ function! { }; if let Some(arg) = args.get_pos_opt::()? { - align.primary = Some(parse_align_specifier(arg)?); + align.positional_1 = Some(parse_align_specifier(arg)?); + } + + if let Some(arg) = args.get_pos_opt::()? { + align.positional_2 = Some(parse_align_specifier(arg)?); } let mut parse_arg = |axis, target: &mut Option| { @@ -92,6 +100,16 @@ function! { Ok(()) }; + if let Some(spec) = this.positional_1 { + let positional = generic_alignment(spec, primary_horizontal).is_ok(); + set_axis(positional, this.positional_1)?; + } + + if let Some(spec) = this.positional_2 { + let positional = generic_alignment(spec, primary_horizontal).is_ok(); + set_axis(positional, this.positional_2)?; + } + set_axis(true, this.primary)?; set_axis(false, this.secondary)?; set_axis(primary_horizontal, this.horizontal)?; diff --git a/src/library/boxed.rs b/src/library/boxed.rs index e8debca4b..1cc4e0200 100644 --- a/src/library/boxed.rs +++ b/src/library/boxed.rs @@ -4,18 +4,34 @@ use crate::func::prelude::*; #[derive(Debug, PartialEq)] pub struct Boxed { body: SyntaxTree, + width: Option, + height: Option, } function! { data: Boxed, parse(args, body, ctx) { + let width = args.get_key_opt::("width")?.map(|a| a.val); + let height = args.get_key_opt::("height")?.map(|a| a.val); args.done()?; + let body = parse!(required: body, ctx); - Ok(Boxed { body }) + Ok(Boxed { + body, + width, + height, + }) } - layout(this, ctx) { + layout(this, mut ctx) { + if let Some(width) = this.width { + ctx.spaces[0].dimensions.x = width; + } + if let Some(height) = this.height { + ctx.spaces[0].dimensions.y = height; + } + Ok(commands![AddMultiple(layout_tree(&this.body, ctx)?)]) } }