summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-11-16 14:03:27 +0100
committerLaurenz <laurmaedje@gmail.com>2019-11-16 14:03:27 +0100
commit796c2486cee82f6e5aefc2d7d02f82a7eeb35a07 (patch)
treee52175e709102affcedbfc7b94479a3e3b16262f /src/layout
parentac4d501945e9b63f6b5f11c4c1a2ec0738d0b058 (diff)
Axes updating for stack layouter 📐
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/flex.rs2
-rw-r--r--src/layout/stacked.rs148
-rw-r--r--src/layout/tree.rs12
3 files changed, 97 insertions, 65 deletions
diff --git a/src/layout/flex.rs b/src/layout/flex.rs
index a65391da..20686e9f 100644
--- a/src/layout/flex.rs
+++ b/src/layout/flex.rs
@@ -142,7 +142,7 @@ impl FlexLayouter {
Err(LayoutError::NotEnoughSpace("cannot fix box into flex run"))?;
}
- self.stack.finish_layout(true);
+ self.stack.add_break(true);
self.usable = self.stack.usable().x;
}
diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs
index 91bb09b5..d80ebf7a 100644
--- a/src/layout/stacked.rs
+++ b/src/layout/stacked.rs
@@ -8,13 +8,16 @@ use super::*;
pub struct StackLayouter {
ctx: StackContext,
layouts: MultiLayout,
- /// Offset on secondary axis, anchor of the layout and the layout itself.
- boxes: Vec<(Size, Size2D, Layout)>,
+ merged_actions: LayoutActionList,
+ merged_dimensions: Size2D,
+
+ // Offset on secondary axis, anchor of the layout and the layout itself.
+ boxes: Vec<(Size, Size2D, Layout)>,
usable: Size2D,
dimensions: Size2D,
active_space: usize,
- include_empty: bool,
+ hard: bool,
}
/// The context for stack layouting.
@@ -29,23 +32,28 @@ pub struct StackContext {
impl StackLayouter {
/// Create a new stack layouter.
pub fn new(ctx: StackContext) -> StackLayouter {
- let usable = ctx.axes.generalize(ctx.spaces[0].usable());
+ let space = ctx.spaces[0];
+ let usable = ctx.axes.generalize(space.usable());
+
StackLayouter {
ctx,
layouts: MultiLayout::new(),
- boxes: vec![],
+ merged_actions: LayoutActionList::new(),
+ merged_dimensions: space.start(),
+
+ boxes: vec![],
usable,
active_space: 0,
- dimensions: start_dimensions(usable, ctx.axes),
- include_empty: true,
+ dimensions: Size2D::zero(),
+ hard: true,
}
}
/// Add a sublayout.
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
let size = self.ctx.axes.generalize(layout.dimensions);
- let mut new_dimensions = self.size_with(size);
+ let mut new_dimensions = merge_sizes(self.dimensions, size);
// Search for a suitable space to insert the box.
while !self.usable.fits(new_dimensions) {
@@ -53,13 +61,13 @@ impl StackLayouter {
Err(LayoutError::NotEnoughSpace("cannot fit box into stack"))?;
}
- self.finish_layout(true);
- new_dimensions = self.size_with(size);
+ self.add_break(true);
+ new_dimensions = merge_sizes(self.dimensions, size);
}
- let ofset = self.dimensions.y;
+ let offset = self.dimensions.y;
let anchor = self.ctx.axes.anchor(size);
- self.boxes.push((ofset, anchor, layout));
+ self.boxes.push((offset, anchor, layout));
self.dimensions.y += size.y;
@@ -77,74 +85,96 @@ impl StackLayouter {
/// Add space after the last layout.
pub fn add_space(&mut self, space: Size) {
if self.dimensions.y + space > self.usable.y {
- self.finish_layout(false);
+ self.add_break(false);
} else {
self.dimensions.y += space;
}
}
+ /// Finish the current layout and start a new one in a new space.
+ pub fn add_break(&mut self, hard: bool) {
+ self.finish_layout();
+ self.start_new_space(hard);
+ }
+
/// Finish the layouting.
///
/// The layouter is not consumed by this to prevent ownership problems.
/// Nevertheless, it should not be used further.
pub fn finish(&mut self) -> MultiLayout {
- if self.include_empty || !self.boxes.is_empty() {
- self.finish_boxes();
+ if self.hard || !self.boxes.is_empty() {
+ self.finish_layout();
}
std::mem::replace(&mut self.layouts, MultiLayout::new())
}
- /// Finish the current layout and start a new one in a new space.
- ///
- /// If `include_empty` is true, the followup layout will even be
- /// part of the finished multi-layout if it would be empty.
- pub fn finish_layout(&mut self, include_empty: bool) {
+ fn finish_layout(&mut self) {
self.finish_boxes();
- self.start_new_space(include_empty);
+
+ let space = self.ctx.spaces[self.active_space];
+ let actions = std::mem::replace(&mut self.merged_actions, LayoutActionList::new());
+
+ self.layouts.add(Layout {
+ dimensions: if space.shrink_to_fit {
+ self.merged_dimensions.padded(space.padding)
+ } else {
+ space.dimensions
+ },
+ actions: actions.into_vec(),
+ debug_render: true,
+ });
}
/// Compose all cached boxes into a layout.
fn finish_boxes(&mut self) {
- let mut actions = LayoutActionList::new();
-
let space = self.ctx.spaces[self.active_space];
+ let start = space.start() + Size2D::with_y(self.merged_dimensions.y);
+
let anchor = self.ctx.axes.anchor(self.usable);
let factor = if self.ctx.axes.secondary.axis.is_positive() { 1 } else { -1 };
- let start = space.start();
for (offset, layout_anchor, layout) in self.boxes.drain(..) {
let general_position = anchor - layout_anchor + Size2D::with_y(offset * factor);
let position = start + self.ctx.axes.specialize(general_position);
- actions.add_layout(position, layout);
+ self.merged_actions.add_layout(position, layout);
}
- self.layouts.add(Layout {
- dimensions: if space.shrink_to_fit {
- self.dimensions.padded(space.padding)
- } else {
- space.dimensions
- },
- actions: actions.into_vec(),
- debug_render: true,
- });
+ let mut dimensions = self.ctx.axes.specialize(self.dimensions);
+ let usable = self.ctx.axes.specialize(self.usable);
+
+ if needs_expansion(self.ctx.axes.primary) {
+ dimensions.x = usable.x;
+ }
+
+ if needs_expansion(self.ctx.axes.secondary) {
+ dimensions.y = usable.y;
+ }
+
+ self.merged_dimensions = merge_sizes(self.merged_dimensions, dimensions);
}
- /// Set up layouting in the next space. Should be preceded by `finish_layout`.
- ///
- /// If `include_empty` is true, the new empty layout will always be added when
- /// finishing this stack. Otherwise, the new layout only appears if new
- /// content is added to it.
- fn start_new_space(&mut self, include_empty: bool) {
- self.active_space = self.next_space();
- self.usable = self.ctx.axes.generalize(self.ctx.spaces[self.active_space].usable());
- self.dimensions = start_dimensions(self.usable, self.ctx.axes);
- self.include_empty = include_empty;
+ /// Set up layouting in the next space.
+ fn start_new_space(&mut self, hard: bool) {
+ let next_space = self.next_space();
+ let space = self.ctx.spaces[next_space];
+
+ self.merged_dimensions = Size2D::zero();
+
+ self.usable = self.ctx.axes.generalize(space.usable());
+ self.dimensions = Size2D::zero();
+ self.active_space = next_space;
+ self.hard = hard;
}
/// Update the axes in use by this stack layouter.
pub fn set_axes(&self, axes: LayoutAxes) {
-
+ if axes != self.ctx.axes {
+ self.finish_boxes();
+ self.usable = self.remains();
+ self.dimensions = Size2D::zero();
+ self.ctx.axes = axes;
+ }
}
/// This layouter's context.
@@ -159,9 +189,8 @@ impl StackLayouter {
/// The remaining spaces for new layouts in the current space.
pub fn remaining(&self, shrink_to_fit: bool) -> LayoutSpaces {
- let remains = Size2D::new(self.usable.x, self.usable.y - self.dimensions.y);
let mut spaces = smallvec![LayoutSpace {
- dimensions: self.ctx.axes.specialize(remains),
+ dimensions: self.ctx.axes.specialize(self.remains()),
padding: SizeBox::zero(),
shrink_to_fit,
}];
@@ -173,6 +202,10 @@ impl StackLayouter {
spaces
}
+ fn remains(&self) -> Size2D {
+ Size2D::new(self.usable.x, self.usable.y - self.dimensions.y)
+ }
+
/// Whether this layouter is in its last space.
pub fn in_last_space(&self) -> bool {
self.active_space == self.ctx.spaces.len() - 1
@@ -181,19 +214,18 @@ impl StackLayouter {
fn next_space(&self) -> usize {
(self.active_space + 1).min(self.ctx.spaces.len() - 1)
}
+}
- /// The combined size of the so-far included boxes with the other size.
- fn size_with(&self, other: Size2D) -> Size2D {
- Size2D {
- x: crate::size::max(self.dimensions.x, other.x),
- y: self.dimensions.y + other.y,
- }
+fn merge_sizes(a: Size2D, b: Size2D) -> Size2D {
+ Size2D {
+ x: crate::size::max(a.x, b.x),
+ y: a.y + b.y
}
}
-fn start_dimensions(usable: Size2D, axes: LayoutAxes) -> Size2D {
- Size2D::with_x(match axes.primary.alignment {
- Alignment::Origin => Size::zero(),
- Alignment::Center | Alignment::End => usable.x,
- })
+fn needs_expansion(axis: AlignedAxis) -> bool {
+ match (axis.axis.is_positive(), axis.alignment) {
+ (true, Alignment::Origin) | (false, Alignment::End) => false,
+ _ => true,
+ }
}
diff --git a/src/layout/tree.rs b/src/layout/tree.rs
index 2957420d..9cbcd641 100644
--- a/src/layout/tree.rs
+++ b/src/layout/tree.rs
@@ -94,9 +94,9 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Command::Add(layout) => self.flex.add(layout),
Command::AddMultiple(layouts) => self.flex.add_multiple(layouts),
- Command::FinishFlexRun => self.flex.add_break(),
- Command::FinishFlexLayout => self.finish_paragraph()?,
- Command::FinishLayout => self.finish_layout(true)?,
+ Command::BreakFlex => self.flex.add_break(),
+ Command::FinishFlex => self.finish_paragraph()?,
+ Command::BreakStack => self.finish_layout()?,
Command::SetStyle(style) => *self.style.to_mut() = style,
Command::SetAxes(axes) => {
@@ -115,10 +115,10 @@ impl<'a, 'p> TreeLayouter<'a, 'p> {
Ok(self.stack.finish())
}
- /// Finish the current stack layout.
- fn finish_layout(&mut self, include_empty: bool) -> LayoutResult<()> {
+ /// Finish the current stack layout with a hard break.
+ fn finish_layout(&mut self) -> LayoutResult<()> {
self.finish_flex()?;
- self.stack.finish_layout(include_empty);
+ self.stack.add_break(true);
self.start_new_flex();
Ok(())
}