diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-11-16 10:41:30 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-11-16 10:41:30 +0100 |
| commit | 0e0f340502beada1cd9ee23857f48b91a0d11a90 (patch) | |
| tree | e4e4087260720adf4bab57edeac6a3d3cb5f14cf /src/layout | |
| parent | bc118634aca5de415d211cab38c4eaa3d3cca25f (diff) | |
Revert page and inline levels
Diffstat (limited to 'src/layout')
| -rw-r--r-- | src/layout/constraints.rs | 14 | ||||
| -rw-r--r-- | src/layout/levels.rs | 199 | ||||
| -rw-r--r-- | src/layout/mod.rs | 109 |
3 files changed, 114 insertions, 208 deletions
diff --git a/src/layout/constraints.rs b/src/layout/constraints.rs index fdcda276..36cfa582 100644 --- a/src/layout/constraints.rs +++ b/src/layout/constraints.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use crate::frame::Frame; -use crate::geom::{Length, Size, Spec}; +use crate::geom::{Length, Linear, Size, Spec}; /// Constrain a frame with constraints. pub trait Constrain { @@ -68,6 +68,18 @@ impl Constraints { && verify(self.exact, current, Length::approx_eq) && verify(self.base, base, Length::approx_eq) } + + /// Set the appropriate base constraints for linear width and height sizing. + pub fn set_base_if_linear(&mut self, base: Size, sizing: Spec<Option<Linear>>) { + // The full sizes need to be equal if there is a relative component in + // the sizes. + if sizing.x.map_or(false, |l| l.is_relative()) { + self.base.x = Some(base.w); + } + if sizing.y.map_or(false, |l| l.is_relative()) { + self.base.y = Some(base.h); + } + } } /// Verify a single constraint. diff --git a/src/layout/levels.rs b/src/layout/levels.rs deleted file mode 100644 index a6b8d050..00000000 --- a/src/layout/levels.rs +++ /dev/null @@ -1,199 +0,0 @@ -use std::fmt::{self, Debug, Formatter}; -use std::hash::{Hash, Hasher}; -use std::rc::Rc; - -use super::*; -use crate::geom::{Length, Size}; - -/// Page-level nodes directly produce frames representing pages. -/// -/// Such nodes create their own regions instead of being supplied with them from -/// some parent. -pub trait PageLevel: Debug { - /// Layout the node, producing one frame per page. - fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>>; -} - -/// Layouts its children onto one or multiple pages. -#[derive(Debug)] -pub struct PageNode { - /// The size of the page. - pub size: Size, - /// The node that produces the actual pages. - pub child: BlockNode, -} - -impl PageLevel for PageNode { - fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> { - // When one of the lengths is infinite the page fits its content along - // that axis. - let expand = self.size.to_spec().map(Length::is_finite); - let regions = Regions::repeat(self.size, self.size, expand); - self.child.layout(ctx, ®ions).into_iter().map(|c| c.item).collect() - } -} - -impl<T> PageLevel for T -where - T: AsRef<[PageNode]> + Debug + ?Sized, -{ - fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> { - self.as_ref().iter().flat_map(|node| node.layout(ctx)).collect() - } -} - -/// Block-level nodes can be layouted into a sequence of regions. -/// -/// They return one frame per used region alongside constraints that define -/// whether the result is reusable in other regions. -pub trait BlockLevel: Debug { - /// Layout the node into the given regions, producing constrained frames. - fn layout( - &self, - ctx: &mut LayoutContext, - regions: &Regions, - ) -> Vec<Constrained<Rc<Frame>>>; - - /// Convert to a packed block-level node. - fn pack(self) -> BlockNode - where - Self: Sized + Hash + 'static, - { - BlockNode { - #[cfg(feature = "layout-cache")] - hash: hash_node(&self), - node: Rc::new(self), - } - } -} - -/// A packed [block-level](BlockLevel) layouting node with precomputed hash. -#[derive(Clone)] -pub struct BlockNode { - node: Rc<dyn BlockLevel>, - #[cfg(feature = "layout-cache")] - hash: u64, -} - -impl BlockLevel for BlockNode { - fn layout( - &self, - ctx: &mut LayoutContext, - regions: &Regions, - ) -> Vec<Constrained<Rc<Frame>>> { - #[cfg(not(feature = "layout-cache"))] - return self.node.layout(ctx, regions); - - #[cfg(feature = "layout-cache")] - ctx.layouts.get(self.hash, regions).unwrap_or_else(|| { - ctx.level += 1; - let frames = self.node.layout(ctx, regions); - ctx.level -= 1; - - let entry = FramesEntry::new(frames.clone(), ctx.level); - - #[cfg(debug_assertions)] - if !entry.check(regions) { - eprintln!("node: {:#?}", self.node); - eprintln!("regions: {:#?}", regions); - eprintln!( - "constraints: {:#?}", - frames.iter().map(|c| c.cts).collect::<Vec<_>>() - ); - panic!("constraints did not match regions they were created for"); - } - - ctx.layouts.insert(self.hash, entry); - frames - }) - } - - fn pack(self) -> BlockNode - where - Self: Sized + Hash + 'static, - { - self - } -} - -impl Hash for BlockNode { - fn hash<H: Hasher>(&self, _state: &mut H) { - #[cfg(feature = "layout-cache")] - _state.write_u64(self.hash); - #[cfg(not(feature = "layout-cache"))] - unimplemented!() - } -} - -impl Debug for BlockNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.node.fmt(f) - } -} - -/// Inline-level nodes are layouted as part of paragraph layout. -/// -/// They only know the width and not the height of the paragraph's region and -/// return only a single frame. -pub trait InlineLevel: Debug { - /// Layout the node into a frame. - fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame; - - /// Convert to a packed inline-level node. - fn pack(self) -> InlineNode - where - Self: Sized + Hash + 'static, - { - InlineNode { - #[cfg(feature = "layout-cache")] - hash: hash_node(&self), - node: Rc::new(self), - } - } -} - -/// A packed [inline-level](InlineLevel) layouting node with precomputed hash. -#[derive(Clone)] -pub struct InlineNode { - node: Rc<dyn InlineLevel>, - #[cfg(feature = "layout-cache")] - hash: u64, -} - -impl InlineLevel for InlineNode { - fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame { - self.node.layout(ctx, space, base) - } - - fn pack(self) -> InlineNode - where - Self: Sized + Hash + 'static, - { - self - } -} - -impl Hash for InlineNode { - fn hash<H: Hasher>(&self, _state: &mut H) { - #[cfg(feature = "layout-cache")] - _state.write_u64(self.hash); - #[cfg(not(feature = "layout-cache"))] - unimplemented!() - } -} - -impl Debug for InlineNode { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.node.fmt(f) - } -} - -/// Hash a node alongside its type id. -#[cfg(feature = "layout-cache")] -fn hash_node(node: &(impl Hash + 'static)) -> u64 { - use std::any::Any; - let mut state = fxhash::FxHasher64::default(); - node.type_id().hash(&mut state); - node.hash(&mut state); - state.finish() -} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 49ceccf6..7d2837de 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -1,29 +1,27 @@ -//! Layouting. +//! Layouting infrastructure. mod constraints; #[cfg(feature = "layout-cache")] mod incremental; -mod levels; mod regions; pub use constraints::*; #[cfg(feature = "layout-cache")] pub use incremental::*; -pub use levels::*; pub use regions::*; +use std::fmt::{self, Debug, Formatter}; +use std::hash::{Hash, Hasher}; use std::rc::Rc; use crate::font::FontStore; use crate::frame::Frame; use crate::image::ImageStore; +use crate::library::DocumentNode; use crate::Context; -/// Layout a page-level node into a collection of frames. -pub fn layout<T>(ctx: &mut Context, node: &T) -> Vec<Rc<Frame>> -where - T: PageLevel + ?Sized, -{ +/// Layout a document node into a collection of frames. +pub fn layout(ctx: &mut Context, node: &DocumentNode) -> Vec<Rc<Frame>> { let mut ctx = LayoutContext::new(ctx); node.layout(&mut ctx) } @@ -55,3 +53,98 @@ impl<'a> LayoutContext<'a> { } } } + +/// A node that can be layouted into a sequence of regions. +/// +/// Layout return one frame per used region alongside constraints that define +/// whether the result is reusable in other regions. +pub trait Layout: Debug { + /// Layout the node into the given regions, producing constrained frames. + fn layout( + &self, + ctx: &mut LayoutContext, + regions: &Regions, + ) -> Vec<Constrained<Rc<Frame>>>; + + /// Convert to a packed node. + fn pack(self) -> PackedNode + where + Self: Sized + Hash + 'static, + { + PackedNode { + #[cfg(feature = "layout-cache")] + hash: { + use std::any::Any; + let mut state = fxhash::FxHasher64::default(); + self.type_id().hash(&mut state); + self.hash(&mut state); + state.finish() + }, + node: Rc::new(self), + } + } +} + +/// A packed layouting node with precomputed hash. +#[derive(Clone)] +pub struct PackedNode { + node: Rc<dyn Layout>, + #[cfg(feature = "layout-cache")] + hash: u64, +} + +impl Layout for PackedNode { + fn layout( + &self, + ctx: &mut LayoutContext, + regions: &Regions, + ) -> Vec<Constrained<Rc<Frame>>> { + #[cfg(not(feature = "layout-cache"))] + return self.node.layout(ctx, regions); + + #[cfg(feature = "layout-cache")] + ctx.layouts.get(self.hash, regions).unwrap_or_else(|| { + ctx.level += 1; + let frames = self.node.layout(ctx, regions); + ctx.level -= 1; + + let entry = FramesEntry::new(frames.clone(), ctx.level); + + #[cfg(debug_assertions)] + if !entry.check(regions) { + eprintln!("node: {:#?}", self.node); + eprintln!("regions: {:#?}", regions); + eprintln!( + "constraints: {:#?}", + frames.iter().map(|c| c.cts).collect::<Vec<_>>() + ); + panic!("constraints did not match regions they were created for"); + } + + ctx.layouts.insert(self.hash, entry); + frames + }) + } + + fn pack(self) -> PackedNode + where + Self: Sized + Hash + 'static, + { + self + } +} + +impl Hash for PackedNode { + fn hash<H: Hasher>(&self, _state: &mut H) { + #[cfg(feature = "layout-cache")] + _state.write_u64(self.hash); + #[cfg(not(feature = "layout-cache"))] + unimplemented!() + } +} + +impl Debug for PackedNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.node.fmt(f) + } +} |
