summaryrefslogtreecommitdiff
path: root/src/layout/stacked.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-10-16 21:31:14 +0200
committerLaurenz <laurmaedje@gmail.com>2019-10-16 21:31:14 +0200
commitf2f05e07b0ff2d98e3c822b2618d02281ed1078c (patch)
tree6f4f8fa046af49c319d68c012a078f3489ab92aa /src/layout/stacked.rs
parenta3c667895e4e5d5673931415397523b9615008d3 (diff)
Implement space extension (multipage) ➕
Diffstat (limited to 'src/layout/stacked.rs')
-rw-r--r--src/layout/stacked.rs135
1 files changed, 104 insertions, 31 deletions
diff --git a/src/layout/stacked.rs b/src/layout/stacked.rs
index bfca4e3e..367a03d7 100644
--- a/src/layout/stacked.rs
+++ b/src/layout/stacked.rs
@@ -5,44 +5,38 @@ use super::*;
/// The boxes are arranged vertically, each layout gettings it's own "line".
pub struct StackLayouter {
ctx: StackContext,
+ layouts: MultiLayout,
actions: LayoutActionList,
+
+ space: LayoutSpace,
usable: Size2D,
dimensions: Size2D,
cursor: Size2D,
+ in_extra_space: bool,
+ started: bool,
}
/// The context for stack layouting.
#[derive(Debug, Copy, Clone)]
pub struct StackContext {
- /// The space to layout the boxes in.
pub space: LayoutSpace,
+ pub extra_space: Option<LayoutSpace>,
}
impl StackLayouter {
/// Create a new stack layouter.
pub fn new(ctx: StackContext) -> StackLayouter {
- let space = ctx.space;
-
StackLayouter {
ctx,
+ layouts: MultiLayout::new(),
actions: LayoutActionList::new(),
+ space: ctx.space,
usable: ctx.space.usable(),
- dimensions: match ctx.space.alignment {
- Alignment::Left => Size2D::zero(),
- Alignment::Right => Size2D::with_x(space.usable().x),
- },
-
- cursor: Size2D::new(
- // If left-align, the cursor points to the top-left corner of
- // each box. If we right-align, it points to the top-right
- // corner.
- match ctx.space.alignment {
- Alignment::Left => space.padding.left,
- Alignment::Right => space.dimensions.x - space.padding.right,
- },
- space.padding.top,
- ),
+ dimensions: start_dimensions(ctx.space),
+ cursor: start_cursor(ctx.space),
+ in_extra_space: false,
+ started: true,
}
}
@@ -53,19 +47,30 @@ impl StackLayouter {
/// Add a sublayout to the bottom.
pub fn add(&mut self, layout: Layout) -> LayoutResult<()> {
+ if !self.started {
+ self.start_new_space()?;
+ }
+
let new_dimensions = Size2D {
x: crate::size::max(self.dimensions.x, layout.dimensions.x),
y: self.dimensions.y + layout.dimensions.y,
};
if self.overflows(new_dimensions) {
- return Err(LayoutError::NotEnoughSpace);
+ if self.ctx.extra_space.is_some() &&
+ !(self.in_extra_space && self.overflows(layout.dimensions))
+ {
+ self.finish_layout(true)?;
+ return self.add(layout);
+ } else {
+ return Err(LayoutError::NotEnoughSpace("cannot fit box into stack"));
+ }
}
// Determine where to put the box. When we right-align it, we want the
// cursor to point to the top-right corner of the box. Therefore, the
// position has to be moved to the left by the width of the box.
- let position = match self.ctx.space.alignment {
+ let position = match self.space.alignment {
Alignment::Left => self.cursor,
Alignment::Right => self.cursor - Size2D::with_x(layout.dimensions.x),
};
@@ -88,26 +93,74 @@ impl StackLayouter {
/// Add vertical space after the last layout.
pub fn add_space(&mut self, space: Size) -> LayoutResult<()> {
- if self.overflows(self.dimensions + Size2D::with_y(space)) {
- return Err(LayoutError::NotEnoughSpace);
+ if !self.started {
+ self.start_new_space()?;
}
- self.cursor.y += space;
- self.dimensions.y += space;
+ let new_dimensions = self.dimensions + Size2D::with_y(space);
+
+ if self.overflows(new_dimensions) {
+ if self.ctx.extra_space.is_some() {
+ self.finish_layout(false)?;
+ } else {
+ return Err(LayoutError::NotEnoughSpace("cannot fit space into stack"));
+ }
+ } else {
+ self.cursor.y += space;
+ self.dimensions.y += space;
+ }
Ok(())
}
/// Finish the layouting.
- pub fn finish(self) -> Layout {
- Layout {
- dimensions: if self.ctx.space.shrink_to_fit {
- self.dimensions.padded(self.ctx.space.padding)
+ ///
+ /// The layouter is not consumed by this to prevent ownership problems.
+ /// It should not be used further.
+ pub fn finish(&mut self) -> LayoutResult<MultiLayout> {
+ if self.started {
+ self.finish_layout(false)?;
+ }
+ Ok(std::mem::replace(&mut self.layouts, MultiLayout::new()))
+ }
+
+ /// Finish the current layout and start a new one in an extra space
+ /// (if there is an extra space).
+ ///
+ /// If `start_new_empty` is true, a new empty layout will be started. Otherwise,
+ /// the new layout only emerges when new content is added.
+ pub fn finish_layout(&mut self, start_new_empty: bool) -> LayoutResult<()> {
+ let actions = std::mem::replace(&mut self.actions, LayoutActionList::new());
+ self.layouts.add(Layout {
+ dimensions: if self.space.shrink_to_fit {
+ self.dimensions.padded(self.space.padding)
} else {
- self.ctx.space.dimensions
+ self.space.dimensions
},
- actions: self.actions.into_vec(),
+ actions: actions.into_vec(),
debug_render: true,
+ });
+
+ self.started = false;
+
+ if start_new_empty {
+ self.start_new_space()?;
+ }
+
+ Ok(())
+ }
+
+ pub fn start_new_space(&mut self) -> LayoutResult<()> {
+ if let Some(space) = self.ctx.extra_space {
+ self.started = true;
+ self.space = space;
+ self.usable = space.usable();
+ self.dimensions = start_dimensions(space);
+ self.cursor = start_cursor(space);
+ self.in_extra_space = true;
+ Ok(())
+ } else {
+ Err(LayoutError::NotEnoughSpace("no extra space to start"))
}
}
@@ -121,10 +174,30 @@ impl StackLayouter {
/// Whether this layouter contains any items.
pub fn is_empty(&self) -> bool {
- self.actions.is_empty()
+ self.layouts.is_empty() && self.actions.is_empty()
}
fn overflows(&self, dimensions: Size2D) -> bool {
!self.usable.fits(dimensions)
}
}
+
+fn start_dimensions(space: LayoutSpace) -> Size2D {
+ match space.alignment {
+ Alignment::Left => Size2D::zero(),
+ Alignment::Right => Size2D::with_x(space.usable().x),
+ }
+}
+
+fn start_cursor(space: LayoutSpace) -> Size2D {
+ Size2D {
+ // If left-align, the cursor points to the top-left corner of
+ // each box. If we right-align, it points to the top-right
+ // corner.
+ x: match space.alignment {
+ Alignment::Left => space.padding.left,
+ Alignment::Right => space.dimensions.x - space.padding.right,
+ },
+ y: space.padding.top,
+ }
+}