summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-11-21 16:03:15 +0100
committerLaurenz <laurmaedje@gmail.com>2019-11-21 16:03:15 +0100
commit863a1a7a00a67185660a14fb216e615db2b3eead (patch)
treea6caa0e6a6e0dcf08be5c29e3b84aa2d2a0d0bae
parentf24e9b44e0ceb19be6f4e16af2d22815e9ccf5b7 (diff)
Completed stack and flex refactoring 🔋
-rw-r--r--src/layout/actions.rs7
-rw-r--r--src/layout/flex.rs132
-rw-r--r--src/layout/stacked.rs143
-rw-r--r--src/layout/text.rs2
-rw-r--r--src/layout/tree.rs88
5 files changed, 195 insertions, 177 deletions
diff --git a/src/layout/actions.rs b/src/layout/actions.rs
index ed3bc182..c668cf75 100644
--- a/src/layout/actions.rs
+++ b/src/layout/actions.rs
@@ -133,9 +133,10 @@ impl LayoutActionList {
self.actions.is_empty()
}
- /// Return the list of actions as a vector.
- pub fn into_vec(self) -> Vec<LayoutAction> {
- self.actions
+ /// Return the list of actions as a vector, leaving an empty
+ /// vector in its position.
+ pub fn to_vec(&mut self) -> Vec<LayoutAction> {
+ std::mem::replace(&mut self.actions, vec![])
}
/// Append a cached move action if one is cached.
diff --git a/src/layout/flex.rs b/src/layout/flex.rs
index 64d48c7d..2f219640 100644
--- a/src/layout/flex.rs
+++ b/src/layout/flex.rs
@@ -2,13 +2,13 @@ use super::*;
#[derive(Debug, Clone)]
pub struct FlexLayouter {
- stack: StackLayouter,
-
axes: LayoutAxes,
flex_spacing: Size,
+ stack: StackLayouter,
units: Vec<FlexUnit>,
line: FlexLine,
+ part: PartialLine,
}
#[derive(Debug, Clone)]
@@ -24,7 +24,6 @@ struct FlexLine {
usable: Size,
actions: LayoutActionList,
combined_dimensions: Size2D,
- part: PartialLine,
}
impl FlexLine {
@@ -33,7 +32,6 @@ impl FlexLine {
usable,
actions: LayoutActionList::new(),
combined_dimensions: Size2D::zero(),
- part: PartialLine::new(usable),
}
}
}
@@ -44,6 +42,7 @@ struct PartialLine {
content: Vec<(Size, Layout)>,
dimensions: Size2D,
space: Option<Size>,
+ last_was_space: bool,
}
impl PartialLine {
@@ -53,6 +52,7 @@ impl PartialLine {
content: vec![],
dimensions: Size2D::zero(),
space: None,
+ last_was_space: false,
}
}
}
@@ -80,13 +80,13 @@ impl FlexLayouter {
let usable = stack.primary_usable();
FlexLayouter {
- stack,
-
axes: ctx.axes,
flex_spacing: ctx.flex_spacing,
+ stack,
units: vec![],
- line: FlexLine::new(usable)
+ line: FlexLine::new(usable),
+ part: PartialLine::new(usable),
}
}
@@ -105,7 +105,7 @@ impl FlexLayouter {
}
pub fn add_primary_space(&mut self, space: Size, soft: bool) {
- self.units.push(FlexUnit::Space(space, soft));
+ self.units.push(FlexUnit::Space(space, soft))
}
pub fn add_secondary_space(&mut self, space: Size, soft: bool) -> LayoutResult<()> {
@@ -122,7 +122,7 @@ impl FlexLayouter {
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
if replace_empty && self.run_is_empty() && self.stack.space_is_empty() {
self.stack.set_spaces(spaces, true);
- self.start_run();
+ self.start_line();
} else {
self.stack.set_spaces(spaces, false);
}
@@ -142,7 +142,6 @@ impl FlexLayouter {
Ok((flex_spaces, Some(stack_spaces)))
}
-
}
pub fn run_is_empty(&self) -> bool {
@@ -162,7 +161,9 @@ impl FlexLayouter {
if !self.run_is_empty() {
self.finish_run()?;
}
- Ok(self.stack.finish_space(hard))
+
+ self.stack.finish_space(hard);
+ Ok(self.start_line())
}
pub fn finish_run(&mut self) -> LayoutResult<Size2D> {
@@ -172,7 +173,7 @@ impl FlexLayouter {
FlexUnit::Boxed(boxed) => self.layout_box(boxed)?,
FlexUnit::Space(space, soft) => self.layout_space(space, soft),
FlexUnit::SetAxes(axes) => self.layout_set_axes(axes),
- FlexUnit::Break => self.layout_break(),
+ FlexUnit::Break => { self.finish_line()?; },
}
}
@@ -183,77 +184,91 @@ impl FlexLayouter {
self.finish_partial_line();
self.stack.add(Layout {
- dimensions: self.axes.specialize(self.line.combined_dimensions),
- actions: self.line.actions.into_vec(),
+ dimensions: self.axes.specialize(Size2D {
+ x: self.line.usable,
+ y: self.line.combined_dimensions.y + self.flex_spacing,
+ }),
+ actions: self.line.actions.to_vec(),
debug_render: false,
})?;
let remaining = self.axes.specialize(Size2D {
- x: self.line.usable - self.line.combined_dimensions.x,
+ x: self.part.usable
+ - self.part.dimensions.x
+ - self.part.space.unwrap_or(Size::zero()),
y: self.line.combined_dimensions.y,
});
- self.line = FlexLine::new(self.stack.primary_usable());
+ self.start_line();
Ok(remaining)
}
- fn finish_partial_line(&mut self) {
- let part = self.line.part;
+ fn start_line(&mut self) {
+ let usable = self.stack.primary_usable();
+ self.line = FlexLine::new(usable);
+ self.part = PartialLine::new(usable);
+ }
+ fn finish_partial_line(&mut self) {
let factor = self.axes.primary.axis.factor();
let anchor =
self.axes.primary.anchor(self.line.usable)
- - self.axes.primary.anchor(part.dimensions.x);
+ - self.axes.primary.anchor(self.part.dimensions.x);
- for (offset, layout) in part.content {
+ for (offset, layout) in self.part.content.drain(..) {
let pos = self.axes.specialize(Size2D::with_x(anchor + factor * offset));
self.line.actions.add_layout(pos, layout);
}
- self.line.combined_dimensions.x.max_eq(part.dimensions.x);
- self.line.part = PartialLine::new(self.line.usable - part.dimensions.x);
- }
-
- fn start_run(&mut self) {
- let usable = self.stack.primary_usable();
- self.line = FlexLine::new(usable);
+ self.line.combined_dimensions.x = anchor + factor * self.part.dimensions.x;
+ self.line.combined_dimensions.y.max_eq(self.part.dimensions.x);
}
fn layout_box(&mut self, boxed: Layout) -> LayoutResult<()> {
let size = self.axes.generalize(boxed.dimensions);
- if size.x > self.size_left() {
- self.space = None;
+ let new_dimension = self.part.dimensions.x
+ + self.part.space.unwrap_or(Size::zero());
+
+ if new_dimension > self.part.usable {
self.finish_line()?;
- while size.x > self.usable {
+ while size.x > self.line.usable {
if self.stack.space_is_last() {
- Err(LayoutError::NotEnoughSpace("cannot fix box into flex run"))?;
+ Err(LayoutError::NotEnoughSpace("failed to add box to flex run"))?;
}
- self.finish_space(true);
- self.total_usable = self.stack.primary_usable();
- self.usable = self.total_usable;
+ self.stack.finish_space(true);
}
}
- self.layout_space();
+ if let Some(space) = self.part.space.take() {
+ self.layout_space(space, false);
+ }
- let offset = self.run.size.x;
- self.run.content.push((offset, boxed));
+ let offset = self.part.dimensions.x;
+ self.part.content.push((offset, boxed));
- self.run.size.x += size.x;
- self.run.size.y = crate::size::max(self.run.size.y, size.y);
+ self.part.dimensions.x += size.x;
+ self.part.dimensions.y.max_eq(size.y);
+ self.part.last_was_space = false;
Ok(())
}
fn layout_space(&mut self, space: Size, soft: bool) {
- if let Some(space) = self.space.take() {
- if self.run.size.x > Size::zero() && self.run.size.x + space <= self.usable {
- self.run.size.x += space;
+ if soft {
+ if !self.part.last_was_space {
+ self.part.space = Some(space);
+ }
+ } else {
+ if self.part.dimensions.x + space > self.part.usable {
+ self.part.dimensions.x = self.part.usable;
+ } else {
+ self.part.dimensions.x += space;
}
+ self.part.last_was_space = true;
}
}
@@ -261,19 +276,17 @@ impl FlexLayouter {
if axes.primary != self.axes.primary {
self.finish_partial_line();
- // self.usable = match axes.primary.alignment {
- // Alignment::Origin =>
- // if self.max_extent == Size::zero() {
- // self.total_usable
- // } else {
- // Size::zero()
- // },
- // Alignment::Center => crate::size::max(
- // self.total_usable - 2 * self.max_extent,
- // Size::zero()
- // ),
- // Alignment::End => self.total_usable - self.max_extent,
- // };
+ let extent = self.line.combined_dimensions.x;
+ let usable = self.line.usable;
+
+ let new_usable = match axes.primary.alignment {
+ Alignment::Origin if extent == Size::zero() => usable,
+ Alignment::Center if extent < usable / 2 => usable - 2 * extent,
+ Alignment::End => usable - extent,
+ _ => Size::zero(),
+ };
+
+ self.part = PartialLine::new(new_usable);
}
if axes.secondary != self.axes.secondary {
@@ -282,13 +295,4 @@ impl FlexLayouter {
self.axes = axes;
}
-
- fn layout_break(&mut self) {
-
- }
-
- fn size_left(&self) -> Size {
- let space = self.space.unwrap_or(Size::zero());
- self.usable - (self.run.size.x + space)
- }
}
diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs
index cdc35580..a7473b6f 100644
--- a/src/layout/stacked.rs
+++ b/src/layout/stacked.rs
@@ -6,22 +6,38 @@ pub struct StackLayouter {
ctx: StackContext,
layouts: MultiLayout,
- space: usize,
+ space: Space,
+ sub: Subspace,
+}
+
+#[derive(Debug, Clone)]
+struct Space {
+ index: usize,
hard: bool,
actions: LayoutActionList,
- combined_dimensions: Size2D, // <- specialized
+ combined_dimensions: Size2D,
+}
- sub: Subspace,
+impl Space {
+ fn new(index: usize, hard: bool) -> Space {
+ Space {
+ index,
+ hard,
+ actions: LayoutActionList::new(),
+ combined_dimensions: Size2D::zero(),
+ }
+ }
}
#[derive(Debug, Clone)]
struct Subspace {
- origin: Size2D, // <- specialized
+ origin: Size2D,
usable: Size2D,
- anchor: Size2D, // <- generic
+ anchor: Size2D,
factor: i32,
- dimensions: Size2D, // <- generic
+ dimensions: Size2D,
space: Option<Size>,
+ last_was_space: bool,
}
impl Subspace {
@@ -33,6 +49,7 @@ impl Subspace {
factor: axes.secondary.axis.factor(),
dimensions: Size2D::zero(),
space: None,
+ last_was_space: false,
}
}
}
@@ -56,29 +73,30 @@ impl StackLayouter {
StackLayouter {
ctx,
layouts: MultiLayout::new(),
-
- space: 0,
- hard: true,
- actions: LayoutActionList::new(),
- combined_dimensions: Size2D::zero(),
-
+ space: Space::new(0, true),
sub: Subspace::new(space.start(), space.usable(), axes),
}
}
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
- self.layout_space();
+ if let Some(space) = self.sub.space.take() {
+ self.add_space(space, false);
+ }
let size = self.ctx.axes.generalize(layout.dimensions);
- let mut new_dimensions = merge(self.sub.dimensions, size);
+
+ let mut new_dimensions = Size2D {
+ x: crate::size::max(self.sub.dimensions.x, size.x),
+ y: self.sub.dimensions.y + size.y
+ };
while !self.sub.usable.fits(new_dimensions) {
- if self.space_is_empty() {
- Err(LayoutError::NotEnoughSpace("cannot fit box into stack"))?;
+ if self.space_is_last() && self.space_is_empty() {
+ Err(LayoutError::NotEnoughSpace("failed to add box to stack"))?;
}
self.finish_space(true);
- new_dimensions = merge(self.sub.dimensions, size);
+ new_dimensions = size;
}
let offset = self.sub.dimensions.y;
@@ -86,11 +104,12 @@ impl StackLayouter {
let pos = self.sub.origin + self.ctx.axes.specialize(
(self.sub.anchor - anchor)
- + Size2D::with_y(self.combined_dimensions.y + self.sub.factor * offset)
+ + Size2D::with_y(self.space.combined_dimensions.y + self.sub.factor * offset)
);
- self.actions.add_layout(pos, layout);
+ self.space.actions.add_layout(pos, layout);
self.sub.dimensions = new_dimensions;
+ self.sub.last_was_space = false;
Ok(())
}
@@ -103,24 +122,36 @@ impl StackLayouter {
}
pub fn add_space(&mut self, space: Size, soft: bool) {
- self.sub.space = Some(space);
- if !soft {
- self.layout_space();
+ if soft {
+ if !self.sub.last_was_space {
+ self.sub.space = Some(space);
+ }
+ } else {
+ if self.sub.dimensions.y + space > self.sub.usable.y {
+ self.sub.dimensions.y = self.sub.usable.y;
+ self.finish_space(false);
+ } else {
+ self.sub.dimensions.y += space;
+ }
+ self.sub.last_was_space = true;
}
}
pub fn set_axes(&mut self, axes: LayoutAxes) {
if axes != self.ctx.axes {
- self.finish_subspace(axes);
+ self.finish_subspace();
+ let (origin, usable) = self.remaining_subspace();
+ self.ctx.axes = axes;
+ self.sub = Subspace::new(origin, usable, axes);
}
}
pub fn set_spaces(&mut self, spaces: LayoutSpaces, replace_empty: bool) {
if replace_empty && self.space_is_empty() {
self.ctx.spaces = spaces;
- self.start_space(0, self.hard);
+ self.start_space(0, self.space.hard);
} else {
- self.ctx.spaces.truncate(self.space + 1);
+ self.ctx.spaces.truncate(self.space.index + 1);
self.ctx.spaces.extend(spaces);
}
}
@@ -143,34 +174,33 @@ impl StackLayouter {
}
pub fn space_is_empty(&self) -> bool {
- self.combined_dimensions == Size2D::zero()
+ self.space.combined_dimensions == Size2D::zero()
+ && self.space.actions.is_empty()
&& self.sub.dimensions == Size2D::zero()
- && self.actions.is_empty()
}
pub fn space_is_last(&self) -> bool {
- self.space == self.ctx.spaces.len() - 1
+ self.space.index == self.ctx.spaces.len() - 1
}
pub fn finish(mut self) -> MultiLayout {
- if self.hard || !self.space_is_empty() {
+ if self.space.hard || !self.space_is_empty() {
self.finish_space(false);
}
self.layouts
}
pub fn finish_space(&mut self, hard: bool) {
- self.finish_subspace(self.ctx.axes);
+ self.finish_subspace();
- let space = self.ctx.spaces[self.space];
- let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
+ let space = self.ctx.spaces[self.space.index];
self.layouts.add(Layout {
dimensions: match self.ctx.expand {
- true => self.combined_dimensions.padded(space.padding),
+ true => self.space.combined_dimensions.padded(space.padding),
false => space.dimensions,
},
- actions: actions.into_vec(),
+ actions: self.space.actions.to_vec(),
debug_render: true,
});
@@ -178,19 +208,17 @@ impl StackLayouter {
}
fn start_space(&mut self, space: usize, hard: bool) {
- self.space = space;
- let space = self.ctx.spaces[space];
+ self.space = Space::new(space, hard);
- self.hard = hard;
- self.combined_dimensions = Size2D::zero();
+ let space = self.ctx.spaces[space];
self.sub = Subspace::new(space.start(), space.usable(), self.ctx.axes);
}
fn next_space(&self) -> usize {
- (self.space + 1).min(self.ctx.spaces.len() - 1)
+ (self.space.index + 1).min(self.ctx.spaces.len() - 1)
}
- fn finish_subspace(&mut self, new_axes: LayoutAxes) {
+ fn finish_subspace(&mut self) {
if self.ctx.axes.primary.needs_expansion() {
self.sub.dimensions.x = self.sub.usable.x;
}
@@ -199,46 +227,23 @@ impl StackLayouter {
self.sub.dimensions.y = self.sub.usable.y;
}
- let (new_origin, new_usable) = self.remaining_subspace();
-
+ let space = self.ctx.spaces[self.space.index];
let origin = self.sub.origin;
let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
- let space = self.ctx.spaces[self.space];
- self.combined_dimensions.max_eq(origin - space.start() + dimensions);
-
- self.ctx.axes = new_axes;
- self.sub = Subspace::new(new_origin, new_usable, new_axes);
+ self.space.combined_dimensions.max_eq(origin - space.start() + dimensions);
}
fn remaining_subspace(&self) -> (Size2D, Size2D) {
- let used = self.ctx.axes.specialize(self.sub.usable);
- let dimensions = self.ctx.axes.specialize(self.sub.dimensions);
+ let new_origin = self.sub.origin + match self.ctx.axes.secondary.axis.is_positive() {
+ true => self.ctx.axes.specialize(Size2D::with_y(self.sub.dimensions.y)),
+ false => Size2D::zero(),
+ };
let new_usable = self.ctx.axes.specialize(Size2D {
x: self.sub.usable.x,
y: self.sub.usable.y - self.sub.dimensions.y,
});
- let new_origin = self.sub.origin
- + Size2D::with_y(self.ctx.axes.specialize(self.sub.dimensions).y);
-
(new_origin, new_usable)
}
-
- fn layout_space(&mut self) {
- if let Some(space) = self.sub.space.take() {
- if self.sub.dimensions.y + space > self.sub.usable.y {
- self.finish_space(false);
- } else {
- self.sub.dimensions.y += space;
- }
- }
- }
-}
-
-fn merge(a: Size2D, b: Size2D) -> Size2D {
- Size2D {
- x: crate::size::max(a.x, b.x),
- y: a.y + b.y
- }
}
diff --git a/src/layout/text.rs b/src/layout/text.rs
index 88d83ab7..fc7cd385 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -72,7 +72,7 @@ impl<'a, 'p> TextLayouter<'a, 'p> {
Ok(Layout {
dimensions: Size2D::new(self.width, self.ctx.style.font_size),
- actions: self.actions.into_vec(),
+ actions: self.actions.to_vec(),
debug_render: false,
})
}
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index d9f15618..2edec631 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -1,7 +1,6 @@
use super::*;
use smallvec::smallvec;
-/// Layouts syntax trees into boxes.
pub fn layout_tree(tree: &SyntaxTree, ctx: LayoutContext) -> LayoutResult<MultiLayout> {
let mut layouter = TreeLayouter::new(ctx);
layouter.layout(tree)?;
@@ -16,7 +15,7 @@ struct TreeLayouter<'a, 'p> {
}
impl<'a, 'p> TreeLayouter<'a, 'p> {
- /// Create a new layouter.
+ /// Create a new syntax tree layouter.
fn new(ctx: LayoutContext<'a, 'p>) -> TreeLayouter<'a, 'p> {
TreeLayouter {
flex: FlexLayouter::new(FlexContext {
@@ -30,28 +29,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
}
}
- /// Layout a syntax tree.
fn layout(&mut self, tree: &SyntaxTree) -> LayoutResult<()> {
for node in &tree.nodes {
match &node.val {
- Node::Text(text) => {
- self.flex.add(layout_text(text, TextContext {
- loader: &self.ctx.loader,
- style: &self.style,
- })?);
- }
+ Node::Text(text) => self.layout_text(text)?,
- Node::Space => {
- if !self.flex.run_is_empty() && !self.flex.run_last_is_space() {
- let space = self.style.word_spacing * self.style.font_size;
- self.flex.add_primary_space(space, true);
- }
- }
- Node::Newline => {
- if !self.flex.run_is_empty() {
- self.break_paragraph()?;
- }
- }
+ Node::Space => self.layout_space(),
+ Node::Newline => self.layout_paragraph()?,
Node::ToggleItalics => self.style.toggle_class(FontClass::Italic),
Node::ToggleBold => self.style.toggle_class(FontClass::Bold),
@@ -64,27 +48,53 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Ok(())
}
- /// Layout a function.
+ fn layout_text(&mut self, text: &str) -> LayoutResult<()> {
+ let layout = layout_text(text, TextContext {
+ loader: &self.ctx.loader,
+ style: &self.style,
+ })?;
+
+ Ok(self.flex.add(layout))
+ }
+
+ fn layout_space(&mut self) {
+ if !self.flex.run_is_empty() {
+ self.flex.add_primary_space(word_spacing(&self.style), true);
+ }
+ }
+
+ fn layout_paragraph(&mut self) -> LayoutResult<()> {
+ if !self.flex.run_is_empty() {
+ self.flex.add_secondary_space(paragraph_spacing(&self.style), true)?;
+ }
+ Ok(())
+ }
+
fn layout_func(&mut self, func: &FuncCall) -> LayoutResult<()> {
let (first, second) = self.flex.remaining()?;
- let ctx = |spaces| LayoutContext {
- loader: self.ctx.loader,
- top_level: false,
- text_style: &self.style,
- page_style: self.ctx.page_style,
- spaces,
- axes: self.ctx.axes.expanding(false),
- expand: false,
+ let ctx = |spaces| {
+ LayoutContext {
+ loader: self.ctx.loader,
+ top_level: false,
+ text_style: &self.style,
+ page_style: self.ctx.page_style,
+ spaces,
+ axes: self.ctx.axes.expanding(false),
+ expand: false,
+ }
};
let commands = match func.body.val.layout(ctx(first)) {
Ok(c) => c,
- Err(e) => match (e, second) {
- (LayoutError::NotEnoughSpace(_), Some(space))
- => func.body.val.layout(ctx(space))?,
- _ => Err(e)?,
- },
+ Err(e) => {
+ match (e, second) {
+ (LayoutError::NotEnoughSpace(_), Some(space)) => {
+ func.body.val.layout(ctx(space))?
+ }
+ (e, _) => Err(e)?,
+ }
+ }
};
for command in commands {
@@ -108,7 +118,7 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Command::FinishRun => { self.flex.finish_run()?; },
Command::FinishSpace => self.flex.finish_space(true)?,
- Command::BreakParagraph => self.break_paragraph()?,
+ Command::BreakParagraph => self.layout_paragraph()?,
Command::SetTextStyle(style) => self.style = style,
Command::SetPageStyle(style) => {
@@ -134,15 +144,13 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Ok(())
}
- /// Finish the layout.
fn finish(self) -> LayoutResult<MultiLayout> {
self.flex.finish()
}
+}
- /// Finish the current flex layout and add space after it.
- fn break_paragraph(&mut self) -> LayoutResult<()> {
- self.flex.add_secondary_space(paragraph_spacing(&self.style), true)
- }
+fn word_spacing(style: &TextStyle) -> Size {
+ style.word_spacing * style.font_size
}
fn flex_spacing(style: &TextStyle) -> Size {