diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-12-09 13:29:04 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-12-09 13:29:04 +0100 |
| commit | 7e980224354880cfda1797136a1ff886d6642662 (patch) | |
| tree | c0137dcca82526faa71fd1d980a90c68dac798c8 /src/layout | |
| parent | 64f938b449b7ff5e53b6a06ed943bf9dedc1014b (diff) | |
Bad stack layouter 🚑
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/actions.rs | 16 | ||||
| -rw-r--r-- | src/layout/mod.rs | 24 | ||||
| -rw-r--r-- | src/layout/stack.rs | 178 | ||||
| -rw-r--r-- | src/layout/tree.rs | 38 |
4 files changed, 192 insertions, 64 deletions
diff --git a/src/layout/actions.rs b/src/layout/actions.rs index a8abf9f0..01abc0ba 100644 --- a/src/layout/actions.rs +++ b/src/layout/actions.rs @@ -15,8 +15,7 @@ pub enum LayoutAction { /// Write text starting at the current position. WriteText(String), /// Visualize a box for debugging purposes. - /// The arguments are position and size. - DebugBox(Size2D, Size2D), + DebugBox(Size2D), } impl Serialize for LayoutAction { @@ -25,14 +24,7 @@ impl Serialize for LayoutAction { MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()), SetFont(i, s) => write!(f, "f {} {}", i, s.to_pt()), WriteText(s) => write!(f, "w {}", s), - DebugBox(p, s) => write!( - f, - "b {} {} {} {}", - p.x.to_pt(), - p.y.to_pt(), - s.x.to_pt(), - s.y.to_pt() - ), + DebugBox(s) => write!(f, "b {} {}", s.x.to_pt(), s.y.to_pt()), } } } @@ -44,7 +36,7 @@ impl Display for LayoutAction { MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y), SetFont(i, s) => write!(f, "font {} {}", i, s), WriteText(s) => write!(f, "write \"{}\"", s), - DebugBox(p, s) => write!(f, "box {} {}", p, s), + DebugBox(s) => write!(f, "box {}", s), } } } @@ -87,8 +79,6 @@ impl LayoutActions { pub fn add(&mut self, action: LayoutAction) { match action { MoveAbsolute(pos) => self.next_pos = Some(self.origin + pos), - DebugBox(pos, size) => self.actions.push(DebugBox(self.origin + pos, size)), - SetFont(index, size) => { self.next_font = Some((index, size)); } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 7f9b9b95..c34d881e 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -248,6 +248,16 @@ impl Axis { } } + /// The inverse axis. + pub fn inv(&self) -> Axis { + match self { + Axis::LeftToRight => Axis::RightToLeft, + Axis::RightToLeft => Axis::LeftToRight, + Axis::TopToBottom => Axis::BottomToTop, + Axis::BottomToTop => Axis::TopToBottom, + } + } + /// The direction factor for this axis. /// /// - 1 if the axis is positive. @@ -363,7 +373,7 @@ enum LastSpacing { } impl LastSpacing { - #[allow(dead_code)] + /// The size of the soft space if this is a soft space or zero otherwise. fn soft_or_zero(&self) -> Size { match self { LastSpacing::Soft(space, _) => *space, @@ -372,18 +382,6 @@ impl LastSpacing { } } -/// The specialized anchor position for an item with the given alignment in a -/// container with a given size along the given axis. -#[allow(dead_code)] -fn anchor(axis: Axis, size: Size, alignment: Alignment) -> Size { - use Alignment::*; - match (axis.is_positive(), alignment) { - (true, Origin) | (false, End) => Size::zero(), - (_, Center) => size / 2, - (true, End) | (false, Origin) => size, - } -} - /// Layout components that can be serialized. pub trait Serialize { /// Serialize the data structure into an output writable. diff --git a/src/layout/stack.rs b/src/layout/stack.rs index 3f9af350..b11aee79 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -37,7 +37,7 @@ struct Space { /// Whether to add the layout for this space even if it would be empty. hard: bool, /// The so-far accumulated subspaces. - spaces: Vec<Subspace>, + subs: Vec<Subspace>, } /// A part of a space with fixed axes and secondary alignment. @@ -88,8 +88,7 @@ impl StackLayouter { /// Add a layout to the stack. pub fn add(&mut self, layout: Layout) -> LayoutResult<()> { if layout.alignment.secondary != self.sub.alignment { - // self.finish_subspace(); - // finish sub and start new with layout's alignment + self.finish_subspace(layout.alignment.secondary); } // Add a cached soft space if there is one. @@ -174,13 +173,13 @@ impl StackLayouter { } } - /// Change the layouting axis used by this layouter. + /// Change the layouting axes used by this layouter. /// /// This starts a new subspace (if the axes are actually different from the /// current ones). pub fn set_axes(&mut self, axes: LayoutAxes) { if axes != self.ctx.axes { - self.finish_subspace(); + self.finish_subspace(Alignment::Origin); let (origin, usable) = self.remaining_subspace(); self.sub = Subspace::new(axes, Alignment::Origin, origin, usable); @@ -226,9 +225,7 @@ impl StackLayouter { /// Whether the current layout space (not subspace) is empty. pub fn space_is_empty(&self) -> bool { - self.sub.layouts.is_empty() - && self.sub.size == Size2D::zero() - && self.space.spaces.is_empty() + self.subspace_is_empty() && self.space.subs.is_empty() } /// Whether the current layout space is the last is the followup list. @@ -244,12 +241,127 @@ impl StackLayouter { self.layouts } - pub fn finish_space(&mut self, _hard: bool) { - unimplemented!() - } + /// Finish the current space and start a new one. + pub fn finish_space(&mut self, hard: bool) { + self.finish_subspace(Alignment::Origin); + + println!(); + println!("FINISHING SPACE:"); + println!(); + + let space = self.ctx.spaces[self.space.index]; + let mut subs = std::mem::replace(&mut self.space.subs, vec![]); + + // --------------------------------------------------------------------- + // Compute the size of the whole space. + let usable = space.usable(); + let mut max = Size2D { + x: if space.expand.0 { usable.x } else { Size::zero() }, + y: if space.expand.1 { usable.y } else { Size::zero() }, + }; + + // The total size is determined by the maximum position + extent of one + // of the boxes. + for sub in &subs { + max.max_eq(sub.origin + sub.axes.specialize(sub.size)); + } + + let dimensions = max.padded(space.padding); + + println!("WITH DIMENSIONS: {}", dimensions); + + println!("SUBS: {:#?}", subs); - fn finish_subspace(&mut self) { - unimplemented!() + // --------------------------------------------------------------------- + // Justify the boxes according to their alignment and give each box + // the appropriate origin and usable space. + + // use Alignment::*; + + for sub in &mut subs { + // The usable width should not exceed the total usable width + // (previous value) or the maximum width of the layout as a whole. + sub.usable.x = crate::size::min( + sub.usable.x, + sub.axes.specialize(max - sub.origin).x, + ); + + sub.usable.y = sub.size.y; + } + + // if space.expand.1 { + // let height = subs.iter().map(|sub| sub.size.y).sum(); + // let centers = subs.iter() + // .filter(|sub| sub.alignment == Alignment::Center) + // .count() + // .max(1); + + // let grow = max.y - height; + // let center_grow = grow / (centers as i32); + + // println!("center grow = {}", center_grow); + + // let mut offset = Size::zero(); + // for sub in &mut subs { + // sub.origin.y += offset; + // if sub.alignment == Center { + // sub.usable.y += center_grow; + // offset += center_grow; + // } + // } + + // if let Some(last) = subs.last_mut() { + // last.usable.y += grow - offset; + // } + // } + + // --------------------------------------------------------------------- + // Do the thing + + // Add a debug box with this boxes size. + let mut actions = LayoutActions::new(); + actions.add(LayoutAction::DebugBox(dimensions)); + + for sub in subs { + let LayoutAxes { primary, secondary } = sub.axes; + + // The factor is +1 if the axis is positive and -1 otherwise. + let factor = sub.axes.secondary.factor(); + + // The anchor is the position of the origin-most point of the + // layout. + let anchor = + sub.usable.y.anchor(sub.alignment, secondary.is_positive()) + - factor * sub.size.y.anchor(sub.alignment, true); + + for entry in sub.layouts { + let layout = entry.layout; + let alignment = layout.alignment.primary; + let size = sub.axes.generalize(layout.dimensions); + + let x = + sub.usable.x.anchor(alignment, primary.is_positive()) + - size.x.anchor(alignment, primary.is_positive()); + + let y = anchor + + factor * entry.offset + - size.y.anchor(Alignment::Origin, secondary.is_positive()); + + let pos = sub.origin + sub.axes.specialize(Size2D::new(x, y)); + actions.add_layout(pos, layout); + } + } + + // --------------------------------------------------------------------- + + self.layouts.push(Layout { + dimensions, + baseline: None, + alignment: self.ctx.alignment, + actions: actions.to_vec(), + }); + + self.start_space(self.next_space(), hard); } /// Start a new space with the given index. @@ -263,13 +375,45 @@ impl StackLayouter { self.sub = Subspace::new(axes, Alignment::Origin, space.start(), space.usable()); } + /// The index of the next space. + fn next_space(&self) -> usize { + (self.space.index + 1).min(self.ctx.spaces.len() - 1) + } + + /// Finish the current subspace. + fn finish_subspace(&mut self, new_alignment: Alignment) { + let empty = self.subspace_is_empty(); + + let axes = self.ctx.axes; + let (origin, usable) = self.remaining_subspace(); + let new_sub = Subspace::new(axes, new_alignment, origin, usable); + let sub = std::mem::replace(&mut self.sub, new_sub); + + if !empty { + self.space.subs.push(sub); + } + } + /// The remaining sub fn remaining_subspace(&self) -> (Size2D, Size2D) { - unimplemented!() + let offset = self.sub.size.y + self.sub.last_spacing.soft_or_zero(); + + let new_origin = self.sub.origin + match self.ctx.axes.secondary.is_positive() { + true => self.ctx.axes.specialize(Size2D::with_y(offset)), + false => Size2D::zero(), + }; + + let new_usable = self.ctx.axes.specialize(Size2D { + x: self.sub.usable.x, + y: self.sub.usable.y - offset, + }); + + (new_origin, new_usable) } - fn next_space(&self) -> usize { - (self.space.index + 1).min(self.ctx.spaces.len() - 1) + /// Whether the current layout space (not subspace) is empty. + fn subspace_is_empty(&self) -> bool { + self.sub.layouts.is_empty() && self.sub.size == Size2D::zero() } } @@ -278,7 +422,7 @@ impl Space { Space { index, hard, - spaces: vec![], + subs: vec![], } } } diff --git a/src/layout/tree.rs b/src/layout/tree.rs index f36516c1..94a50eea 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -11,7 +11,7 @@ pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiL #[derive(Debug, Clone)] struct TreeLayouter<'a, 'p> { ctx: LayoutContext<'a, 'p>, - flex: FlexLayouter, + stack: StackLayouter, style: LayoutStyle, } @@ -19,11 +19,10 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { /// Create a new syntax tree layouter. fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> { TreeLayouter { - flex: FlexLayouter::new(FlexContext { + stack: StackLayouter::new(StackContext { spaces: ctx.spaces.clone(), axes: ctx.axes, alignment: ctx.alignment, - flex_spacing: flex_spacing(&ctx.style.text), }), style: ctx.style.clone(), ctx, @@ -56,25 +55,22 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { alignment: self.ctx.alignment, })?; - Ok(self.flex.add(layout)) + self.stack.add(layout) } fn layout_space(&mut self) { - self.flex.add_primary_space( - word_spacing(&self.style.text), - SPACE_KIND, - ); + } fn layout_paragraph(&mut self) -> LayoutResult<()> { - self.flex.add_secondary_space( + Ok(self.stack.add_spacing( paragraph_spacing(&self.style.text), PARAGRAPH_KIND, - ) + )) } fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> { - let spaces = self.flex.remaining(); + let spaces = self.stack.remaining(); let commands = func.0.layout(LayoutContext { loader: self.ctx.loader, @@ -97,16 +93,16 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { match command { LayoutTree(tree) => self.layout(tree)?, - Add(layout) => self.flex.add(layout), - AddMultiple(layouts) => self.flex.add_multiple(layouts), + Add(layout) => self.stack.add(layout)?, + AddMultiple(layouts) => self.stack.add_multiple(layouts)?, AddSpacing(space, kind, axis) => match axis { - GenericAxisKind::Primary => self.flex.add_primary_space(space, kind), - GenericAxisKind::Secondary => self.flex.add_secondary_space(space, kind)?, + GenericAxisKind::Primary => {}, + GenericAxisKind::Secondary => self.stack.add_spacing(space, kind), } - FinishLine => self.flex.add_break(), - FinishRun => { self.flex.finish_run()?; }, - FinishSpace => self.flex.finish_space(true)?, + FinishLine => {}, + FinishRun => {}, + FinishSpace => self.stack.finish_space(true), BreakParagraph => self.layout_paragraph()?, SetTextStyle(style) => self.style.text = style, @@ -116,7 +112,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { } self.style.page = style; - self.flex.set_spaces(smallvec