summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/document.rs22
-rw-r--r--src/layout/fixed.rs27
-rw-r--r--src/layout/mod.rs114
-rw-r--r--src/layout/node.rs40
-rw-r--r--src/layout/pad.rs55
-rw-r--r--src/layout/spacing.rs9
-rw-r--r--src/layout/text.rs36
7 files changed, 176 insertions, 127 deletions
diff --git a/src/layout/document.rs b/src/layout/document.rs
index 69ac3d9d..a91dbbe9 100644
--- a/src/layout/document.rs
+++ b/src/layout/document.rs
@@ -3,6 +3,7 @@ use super::*;
/// The top-level layout node.
#[derive(Debug, Clone, PartialEq)]
pub struct Document {
+ /// The runs of pages with same properties.
pub runs: Vec<Pages>,
}
@@ -22,26 +23,17 @@ impl Document {
pub struct Pages {
/// The size of the pages.
pub size: Size,
- /// The layout node that produces the actual pages.
+ /// The layout node that produces the actual pages (typically a [stack]).
+ ///
+ /// [stack]: struct.Stack.html
pub child: LayoutNode,
}
impl Pages {
/// Layout the page run.
pub async fn layout(&self, ctx: &mut LayoutContext) -> Vec<BoxLayout> {
- let constraints = LayoutConstraints {
- spaces: vec![LayoutSpace { base: self.size, size: self.size }],
- repeat: true,
- };
-
- self.child
- .layout(ctx, constraints)
- .await
- .into_iter()
- .filter_map(|item| match item {
- Layouted::Spacing(_) => None,
- Layouted::Box(layout, _) => Some(layout),
- })
- .collect()
+ let areas = Areas::repeat(self.size);
+ let layouted = self.child.layout(ctx, &areas).await;
+ layouted.into_iter().filter_map(Layouted::into_boxed).collect()
}
}
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs
index 93947305..78a512e6 100644
--- a/src/layout/fixed.rs
+++ b/src/layout/fixed.rs
@@ -4,34 +4,25 @@ use crate::geom::Linear;
/// A node that can fix its child's width and height.
#[derive(Debug, Clone, PartialEq)]
pub struct Fixed {
+ /// The fixed width, if any.
pub width: Option<Linear>,
+ /// The fixed height, if any.
pub height: Option<Linear>,
+ /// The child node whose size to fix.
pub child: LayoutNode,
}
#[async_trait(?Send)]
impl Layout for Fixed {
- async fn layout(
- &self,
- ctx: &mut LayoutContext,
- constraints: LayoutConstraints,
- ) -> Vec<Layouted> {
- let space = constraints.spaces[0];
+ async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
+ let Area { rem, full } = areas.current;
let size = Size::new(
- self.width
- .map(|w| w.eval(space.base.width))
- .unwrap_or(space.size.width),
- self.height
- .map(|h| h.eval(space.base.height))
- .unwrap_or(space.size.height),
+ self.width.map(|w| w.eval(full.width)).unwrap_or(rem.width),
+ self.height.map(|h| h.eval(full.height)).unwrap_or(rem.height),
);
- self.child
- .layout(ctx, LayoutConstraints {
- spaces: vec![LayoutSpace { base: size, size }],
- repeat: false,
- })
- .await
+ let areas = Areas::once(size);
+ self.child.layout(ctx, &areas).await
}
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index d5ab24e7..a6ef4300 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -51,11 +51,86 @@ pub trait Layout {
/// constraints: LayoutConstraints,
/// ) -> Vec<LayoutItem>;
/// ```
- async fn layout(
- &self,
- ctx: &mut LayoutContext,
- constraints: LayoutConstraints,
- ) -> Vec<Layouted>;
+ async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted>;
+}
+
+/// A sequence of areas to layout into.
+#[derive(Debug, Clone, PartialEq)]
+pub struct Areas {
+ /// The current area.
+ pub current: Area,
+ /// The backlog of followup areas.
+ ///
+ /// _Note_: This works stack-like and not queue-like!
+ pub backlog: Vec<Size>,
+ /// The last area that is repeated when the backlog is empty.
+ pub last: Option<Size>,
+}
+
+impl Areas {
+ /// Create a new length-1 sequence of areas with just one `area`.
+ pub fn once(size: Size) -> Self {
+ Self {
+ current: Area::new(size),
+ backlog: vec![],
+ last: None,
+ }
+ }
+
+ /// Create a new sequence of areas that repeats `area` indefinitely.
+ pub fn repeat(size: Size) -> Self {
+ Self {
+ current: Area::new(size),
+ backlog: vec![],
+ last: Some(size),
+ }
+ }
+
+ /// Advance to the next area if there is any.
+ pub fn next(&mut self) {
+ if let Some(size) = self.backlog.pop().or(self.last) {
+ self.current = Area::new(size);
+ }
+ }
+
+ /// Whether `current` is a fully sized (untouched) copy of the last area.
+ ///
+ /// If this is false calling `next()` will have no effect.
+ pub fn in_full_last(&self) -> bool {
+ self.backlog.is_empty() && self.last.map_or(true, |size| self.current.rem == size)
+ }
+}
+
+/// The area into which content can be laid out.
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub struct Area {
+ /// The remaining size of this area.
+ pub rem: Size,
+ /// The full size this area once had (used for relative sizing).
+ pub full: Size,
+}
+
+impl Area {
+ /// Create a new area.
+ pub fn new(size: Size) -> Self {
+ Self { rem: size, full: size }
+ }
+}
+
+/// How to determine a container's size along an axis.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Expansion {
+ /// Fit the content.
+ Fit,
+ /// Fill the available space.
+ Fill,
+}
+
+impl Expansion {
+ /// Returns `Fill` if the condition is true and `Fit` otherwise.
+ pub fn fill_if(condition: bool) -> Self {
+ if condition { Self::Fill } else { Self::Fit }
+ }
}
/// An item that is produced by [layouting] a node.
@@ -65,27 +140,18 @@ pub trait Layout {
pub enum Layouted {
/// Spacing that should be added to the parent.
Spacing(Length),
- /// A box that should be aligned in the parent.
- Box(BoxLayout, Gen<Align>),
-}
-
-/// The constraints for layouting a single node.
-#[derive(Debug, Clone)]
-pub struct LayoutConstraints {
- /// The spaces to layout into.
- pub spaces: Vec<LayoutSpace>,
- /// Whether to spill over into copies of the last space or finish layouting
- /// when the last space is used up.
- pub repeat: bool,
+ /// A box that should be added to and aligned in the parent.
+ Boxed(BoxLayout, Gen<Align>),
}
-/// The space into which content is laid out.
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct LayoutSpace {
- /// The full size of this container (the base for relative sizes).
- pub base: Size,
- /// The maximum size of the rectangle to layout into.
- pub size: Size,
+impl Layouted {
+ /// Return the box if this if its a box variant.
+ pub fn into_boxed(self) -> Option<BoxLayout> {
+ match self {
+ Self::Spacing(_) => None,
+ Self::Boxed(boxed, _) => Some(boxed),
+ }
+ }
}
/// A finished box with content at fixed positions.
diff --git a/src/layout/node.rs b/src/layout/node.rs
index 0adbb145..31213b9d 100644
--- a/src/layout/node.rs
+++ b/src/layout/node.rs
@@ -18,33 +18,29 @@ pub enum LayoutNode {
}
impl LayoutNode {
- /// Create a new model node form a type implementing `DynNode`.
+ /// Create a new dynamic node.
pub fn dynamic<T: DynNode>(inner: T) -> Self {
Self::Dyn(Dynamic::new(inner))
}
}
-impl Debug for LayoutNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+#[async_trait(?Send)]
+impl Layout for LayoutNode {
+ async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
match self {
- Self::Spacing(spacing) => spacing.fmt(f),
- Self::Text(text) => text.fmt(f),
- Self::Dyn(boxed) => boxed.fmt(f),
+ Self::Spacing(spacing) => spacing.layout(ctx, areas).await,
+ Self::Text(text) => text.layout(ctx, areas).await,
+ Self::Dyn(boxed) => boxed.layout(ctx, areas).await,
}
}
}
-#[async_trait(?Send)]
-impl Layout for LayoutNode {
- async fn layout(
- &self,
- ctx: &mut LayoutContext,
- constraints: LayoutConstraints,
- ) -> Vec<Layouted> {
+impl Debug for LayoutNode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
- Self::Spacing(spacing) => spacing.layout(ctx, constraints).await,
- Self::Text(text) => text.layout(ctx, constraints).await,
- Self::Dyn(boxed) => boxed.layout(ctx, constraints).await,
+ Self::Spacing(spacing) => spacing.fmt(f),
+ Self::Text(text) => text.fmt(f),
+ Self::Dyn(boxed) => boxed.fmt(f),
}
}
}
@@ -80,6 +76,12 @@ impl Debug for Dynamic {
}
}
+impl From<Dynamic> for LayoutNode {
+ fn from(dynamic: Dynamic) -> Self {
+ Self::Dyn(dynamic)
+ }
+}
+
impl Clone for Dynamic {
fn clone(&self) -> Self {
Self(self.0.dyn_clone())
@@ -92,12 +94,6 @@ impl PartialEq for Dynamic {
}
}
-impl From<Dynamic> for LayoutNode {
- fn from(dynamic: Dynamic) -> Self {
- Self::Dyn(dynamic)
- }
-}
-
/// A dynamic node, which can implement custom layouting behaviour.
///
/// This trait just combines the requirements for types to qualify as dynamic
diff --git a/src/layout/pad.rs b/src/layout/pad.rs
index e7584dc8..2574fa16 100644
--- a/src/layout/pad.rs
+++ b/src/layout/pad.rs
@@ -4,45 +4,40 @@ use crate::geom::Linear;
/// A node that pads its child at the sides.
#[derive(Debug, Clone, PartialEq)]
pub struct Pad {
+ /// The amount of padding.
pub padding: Sides<Linear>,
+ /// The child node whose sides to pad.
pub child: LayoutNode,
}
#[async_trait(?Send)]
impl Layout for Pad {
- async fn layout(
- &self,
- ctx: &mut LayoutContext,
- constraints: LayoutConstraints,
- ) -> Vec<Layouted> {
- self.child
- .layout(ctx, LayoutConstraints {
- spaces: constraints
- .spaces
- .into_iter()
- .map(|space| LayoutSpace {
- base: space.base - self.padding.eval(space.base).size(),
- size: space.size - self.padding.eval(space.size).size(),
- })
- .collect(),
- repeat: constraints.repeat,
- })
- .await
- .into_iter()
- .map(|item| match item {
- Layouted::Box(boxed, align) => {
- let padding = self.padding.eval(boxed.size);
- let padded = boxed.size + padding.size();
+ async fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Vec<Layouted> {
+ let shrink = |size| size - self.padding.eval(size).size();
+ let areas = Areas {
+ current: Area {
+ rem: shrink(areas.current.rem),
+ full: shrink(areas.current.full),
+ },
+ backlog: areas.backlog.iter().copied().map(shrink).collect(),
+ last: areas.last.map(shrink),
+ };
- let mut outer = BoxLayout::new(padded);
- let start = Point::new(padding.left, padding.top);
- outer.push_layout(start, boxed);
+ let mut layouted = self.child.layout(ctx, &areas).await;
- Layouted::Box(outer, align)
+ for item in &mut layouted {
+ if let Layouted::Boxed(boxed, _) = item {
+ let padding = self.padding.eval(boxed.size);
+ let origin = Point::new(padding.left, padding.top);
+
+ boxed.size += padding.size();
+ for (point, _) in &mut boxed.elements {
+ *point += origin;
}
- item => item,
- })
- .collect()
+ }
+ }
+
+ layouted
}
}
diff --git a/src/layout/spacing.rs b/src/layout/spacing.rs
index 766ff4d2..427cb7b0 100644
--- a/src/layout/spacing.rs
+++ b/src/layout/spacing.rs
@@ -2,16 +2,21 @@ use std::fmt::{self, Debug, Formatter};
use super::*;
-/// A node that inserts spacing.
+/// A spacing node.
#[derive(Copy, Clone, PartialEq)]
pub struct Spacing {
+ /// The amount of spacing to insert.
pub amount: Length,
+ /// Spacing interaction, see [Softness's] documentation for more
+ /// information.
+ ///
+ /// [Softness's]: enum.Softness.html
pub softness: Softness,
}
#[async_trait(?Send)]
impl Layout for Spacing {
- async fn layout(&self, _: &mut LayoutContext, _: LayoutConstraints) -> Vec<Layouted> {
+ async fn layout(&self, _: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
vec![Layouted::Spacing(self.amount)]
}
}
diff --git a/src/layout/text.rs b/src/layout/text.rs
index 1c09b40a..7d8386a1 100644
--- a/src/layout/text.rs
+++ b/src/layout/text.rs
@@ -9,32 +9,36 @@ use crate::shaping;
/// A text node.
#[derive(Clone, PartialEq)]
pub struct Text {
+ /// The text.
pub text: String,
- pub size: Length,
+ /// The font size.
+ pub font_size: Length,
+ /// The text direction.
pub dir: Dir,
+ /// The families used for font fallback.
pub families: Rc<FallbackTree>,
+ /// The font variant,
pub variant: FontVariant,
+ /// How to align this text node in its parent.
pub aligns: Gen<Align>,
}
#[async_trait(?Send)]
impl Layout for Text {
- async fn layout(
- &self,
- ctx: &mut LayoutContext,
- _constraints: LayoutConstraints,
- ) -> Vec<Layouted> {
+ async fn layout(&self, ctx: &mut LayoutContext, _: &Areas) -> Vec<Layouted> {
let mut loader = ctx.loader.borrow_mut();
- let boxed = shaping::shape(
- &self.text,
- self.size,
- self.dir,
- &mut loader,
- &self.families,
- self.variant,
- )
- .await;
- vec![Layouted::Box(boxed, self.aligns)]
+ vec![Layouted::Boxed(
+ shaping::shape(
+ &mut loader,
+ &self.text,
+ self.font_size,
+ self.dir,
+ &self.families,
+ self.variant,
+ )
+ .await,
+ self.aligns,
+ )]
}
}