diff options
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/document.rs | 22 | ||||
| -rw-r--r-- | src/layout/fixed.rs | 27 | ||||
| -rw-r--r-- | src/layout/mod.rs | 114 | ||||
| -rw-r--r-- | src/layout/node.rs | 40 | ||||
| -rw-r--r-- | src/layout/pad.rs | 55 | ||||
| -rw-r--r-- | src/layout/spacing.rs | 9 | ||||
| -rw-r--r-- | src/layout/text.rs | 36 |
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, + )] } } |
