diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-07-21 20:35:02 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-07-25 01:03:38 +0200 |
| commit | adb71ee040752f9348b0c9a511b2ab7e3710cb80 (patch) | |
| tree | 13578f67cf7361a03924b2a7f18d3d17103dea09 /src/layout/tree.rs | |
| parent | 927f1154fa983a13eda050d716d04dead96bf8fb (diff) | |
Move and refactor
Diffstat (limited to 'src/layout/tree.rs')
| -rw-r--r-- | src/layout/tree.rs | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/layout/tree.rs b/src/layout/tree.rs new file mode 100644 index 00000000..258f1ccc --- /dev/null +++ b/src/layout/tree.rs @@ -0,0 +1,158 @@ +use super::*; + +use std::any::Any; +use std::fmt::{self, Debug, Formatter}; + +#[cfg(feature = "layout-cache")] +use fxhash::FxHasher64; + +/// A tree of layout nodes. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct LayoutTree { + /// Runs of pages with the same properties. + pub runs: Vec<PageRun>, +} + +impl LayoutTree { + /// Layout the tree into a collection of frames. + pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> { + self.runs.iter().flat_map(|run| run.layout(ctx)).collect() + } +} + +/// A run of pages that all have the same properties. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct PageRun { + /// The size of each page. + pub size: Size, + /// The layout node that produces the actual pages (typically a + /// [`StackNode`]). + pub child: LayoutNode, +} + +impl PageRun { + /// Layout the page run. + pub 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 Size { width, height } = self.size; + let expand = Spec::new(width.is_finite(), height.is_finite()); + let regions = Regions::repeat(self.size, expand); + self.child.layout(ctx, ®ions).into_iter().map(|c| c.item).collect() + } +} + +/// A dynamic layouting node. +pub struct LayoutNode { + node: Box<dyn Bounds>, + #[cfg(feature = "layout-cache")] + hash: u64, +} + +impl LayoutNode { + /// Create a new instance from any node that satisifies the required bounds. + #[cfg(feature = "layout-cache")] + pub fn new<T>(node: T) -> Self + where + T: Layout + Debug + Clone + Eq + PartialEq + Hash + 'static, + { + let hash = { + let mut state = FxHasher64::default(); + node.type_id().hash(&mut state); + node.hash(&mut state); + state.finish() + }; + + Self { node: Box::new(node), hash } + } + + /// Create a new instance from any node that satisifies the required bounds. + #[cfg(not(feature = "layout-cache"))] + pub fn new<T>(node: T) -> Self + where + T: Layout + Debug + Clone + Eq + PartialEq + 'static, + { + Self { node: Box::new(node) } + } +} + +impl Layout for LayoutNode { + fn layout( + &self, + ctx: &mut LayoutContext, + regions: &Regions, + ) -> Vec<Constrained<Rc<Frame>>> { + #[cfg(feature = "layout-cache")] + { + ctx.level += 1; + let frames = ctx.layouts.get(self.hash, regions.clone()).unwrap_or_else(|| { + let frames = self.node.layout(ctx, regions); + ctx.layouts.insert(self.hash, frames.clone(), ctx.level - 1); + frames + }); + ctx.level -= 1; + frames + } + + #[cfg(not(feature = "layout-cache"))] + self.node.layout(ctx, regions) + } +} + +impl Debug for LayoutNode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.node.fmt(f) + } +} + +impl Clone for LayoutNode { + fn clone(&self) -> Self { + Self { + node: self.node.dyn_clone(), + #[cfg(feature = "layout-cache")] + hash: self.hash, + } + } +} + +impl Eq for LayoutNode {} + +impl PartialEq for LayoutNode { + fn eq(&self, other: &Self) -> bool { + self.node.dyn_eq(other.node.as_ref()) + } +} + +#[cfg(feature = "layout-cache")] +impl Hash for LayoutNode { + fn hash<H: Hasher>(&self, state: &mut H) { + state.write_u64(self.hash); + } +} + +trait Bounds: Layout + Debug + 'static { + fn as_any(&self) -> &dyn Any; + fn dyn_eq(&self, other: &dyn Bounds) -> bool; + fn dyn_clone(&self) -> Box<dyn Bounds>; +} + +impl<T> Bounds for T +where + T: Layout + Debug + Eq + PartialEq + Clone + 'static, +{ + fn as_any(&self) -> &dyn Any { + self + } + + fn dyn_eq(&self, other: &dyn Bounds) -> bool { + if let Some(other) = other.as_any().downcast_ref::<Self>() { + self == other + } else { + false + } + } + + fn dyn_clone(&self) -> Box<dyn Bounds> { + Box::new(self.clone()) + } +} |
