summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-12-09 13:29:04 +0100
committerLaurenz <laurmaedje@gmail.com>2019-12-09 13:29:04 +0100
commit7e980224354880cfda1797136a1ff886d6642662 (patch)
treec0137dcca82526faa71fd1d980a90c68dac798c8 /src/layout
parent64f938b449b7ff5e53b6a06ed943bf9dedc1014b (diff)
Bad stack layouter 🚑
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/actions.rs16
-rw-r--r--src/layout/mod.rs24
-rw-r--r--src/layout/stack.rs178
-rw-r--r--src/layout/tree.rs38
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![
+ self.stack.set_spaces(smallvec![
LayoutSpace {
dimensions: style.dimensions,
padding: style.margins,
@@ -126,7 +122,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
}
SetAlignment(alignment) => self.ctx.alignment = alignment,
SetAxes(axes) => {
- self.flex.set_axes(axes);
+ self.stack.set_axes(axes);
self.ctx.axes = axes;
}
}
@@ -135,7 +131,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
}
fn finish(self) -> LayoutResult<MultiLayout> {
- self.flex.finish()
+ Ok(self.stack.finish())
}
}