summaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/background.rs6
-rw-r--r--src/layout/fixed.rs2
-rw-r--r--src/layout/frame.rs2
-rw-r--r--src/layout/mod.rs54
-rw-r--r--src/layout/pad.rs2
-rw-r--r--src/layout/par.rs24
-rw-r--r--src/layout/shaping.rs8
-rw-r--r--src/layout/stack.rs24
8 files changed, 74 insertions, 48 deletions
diff --git a/src/layout/background.rs b/src/layout/background.rs
index a5afbc4a..3a76a264 100644
--- a/src/layout/background.rs
+++ b/src/layout/background.rs
@@ -1,7 +1,7 @@
use super::*;
/// A node that places a rectangular filled background behind its child.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct BackgroundNode {
/// The kind of shape to use as a background.
pub shape: BackgroundShape,
@@ -12,7 +12,7 @@ pub struct BackgroundNode {
}
/// The kind of shape to use as a background.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum BackgroundShape {
Rect,
Ellipse,
@@ -24,7 +24,7 @@ impl Layout for BackgroundNode {
for frame in &mut frames {
let (point, shape) = match self.shape {
- BackgroundShape::Rect => (Point::ZERO, Shape::Rect(frame.size)),
+ BackgroundShape::Rect => (Point::zero(), Shape::Rect(frame.size)),
BackgroundShape::Ellipse => {
(frame.size.to_point() / 2.0, Shape::Ellipse(frame.size))
}
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs
index a0e5e973..7c28e8e5 100644
--- a/src/layout/fixed.rs
+++ b/src/layout/fixed.rs
@@ -1,7 +1,7 @@
use super::*;
/// A node that can fix its child's width and height.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct FixedNode {
/// The fixed width, if any.
pub width: Option<Linear>,
diff --git a/src/layout/frame.rs b/src/layout/frame.rs
index cf8ddb09..61a84d6d 100644
--- a/src/layout/frame.rs
+++ b/src/layout/frame.rs
@@ -96,7 +96,7 @@ pub enum Shape {
}
/// How text and shapes are filled.
-#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, Copy, Clone, PartialEq, Hash, Serialize, Deserialize)]
pub enum Fill {
/// A solid color.
Color(Color),
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index 7997f584..207d5bed 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -18,6 +18,10 @@ pub use stack::*;
use std::any::Any;
use std::fmt::{self, Debug, Formatter};
+use std::hash::{Hash, Hasher};
+
+use decorum::NotNan;
+use fxhash::FxHasher64;
use crate::env::Env;
use crate::geom::*;
@@ -64,39 +68,62 @@ impl PageRun {
}
/// A wrapper around a dynamic layouting node.
-pub struct AnyNode(Box<dyn Bounds>);
+pub struct AnyNode {
+ node: Box<dyn Bounds>,
+ hash: u64,
+}
impl AnyNode {
/// Create a new instance from any node that satisifies the required bounds.
- pub fn new<T>(any: T) -> Self
+ pub fn new<T>(node: T) -> Self
where
- T: Layout + Debug + Clone + PartialEq + 'static,
+ T: Layout + Debug + Clone + PartialEq + Hash + 'static,
{
- Self(Box::new(any))
+ let hash = {
+ let mut state = FxHasher64::default();
+ node.hash(&mut state);
+ state.finish()
+ };
+
+ Self { node: Box::new(node), hash }
+ }
+
+ /// The cached hash for the boxed node.
+ pub fn hash(&self) -> u64 {
+ self.hash
}
}
impl Layout for AnyNode {
fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec<Frame> {
- self.0.layout(ctx, regions)
+ self.node.layout(ctx, regions)
}
}
impl Clone for AnyNode {
fn clone(&self) -> Self {
- Self(self.0.dyn_clone())
+ Self {
+ node: self.node.dyn_clone(),
+ hash: self.hash,
+ }
}
}
impl PartialEq for AnyNode {
fn eq(&self, other: &Self) -> bool {
- self.0.dyn_eq(other.0.as_ref())
+ self.node.dyn_eq(other.node.as_ref())
+ }
+}
+
+impl Hash for AnyNode {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ state.write_u64(self.hash);
}
}
impl Debug for AnyNode {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.0.fmt(f)
+ self.node.fmt(f)
}
}
@@ -202,10 +229,7 @@ impl Regions {
///
/// If this is true, 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.is_nan() || size.is_nan() || self.current == size
- })
+ self.backlog.is_empty() && self.last.map_or(true, |size| self.current == size)
}
/// Advance to the next region if there is any.
@@ -217,9 +241,9 @@ impl Regions {
}
/// Shrink `current` to ensure that the aspect ratio can be satisfied.
- pub fn apply_aspect_ratio(&mut self, aspect: f64) {
- let width = self.current.width.min(aspect * self.current.height);
- let height = width / aspect;
+ pub fn apply_aspect_ratio(&mut self, aspect: NotNan<f64>) {
+ let width = self.current.width.min(aspect.into_inner() * self.current.height);
+ let height = width / aspect.into_inner();
self.current = Size::new(width, height);
}
}
diff --git a/src/layout/pad.rs b/src/layout/pad.rs
index ad24d62c..ccf0d5e1 100644
--- a/src/layout/pad.rs
+++ b/src/layout/pad.rs
@@ -1,7 +1,7 @@
use super::*;
/// A node that adds padding to its child.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct PadNode {
/// The amount of padding.
pub padding: Sides<Linear>,
diff --git a/src/layout/par.rs b/src/layout/par.rs
index 22400dd3..f21778de 100644
--- a/src/layout/par.rs
+++ b/src/layout/par.rs
@@ -10,7 +10,7 @@ use crate::util::{RangeExt, SliceExt};
type Range = std::ops::Range<usize>;
/// A node that arranges its children into a paragraph.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct ParNode {
/// The inline direction of this paragraph.
pub dir: Dir,
@@ -21,7 +21,7 @@ pub struct ParNode {
}
/// A child of a paragraph node.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Hash)]
pub enum ParChild {
/// Spacing between other nodes.
Spacing(Length),
@@ -255,7 +255,7 @@ impl ParItem<'_> {
/// The size of the item.
pub fn size(&self) -> Size {
match self {
- Self::Spacing(amount) => Size::new(*amount, Length::ZERO),
+ Self::Spacing(amount) => Size::new(*amount, Length::zero()),
Self::Text(shaped, _) => shaped.size,
Self::Frame(frame, _) => frame.size,
}
@@ -264,7 +264,7 @@ impl ParItem<'_> {
/// The baseline of the item.
pub fn baseline(&self) -> Length {
match self {
- Self::Spacing(_) => Length::ZERO,
+ Self::Spacing(_) => Length::zero(),
Self::Text(shaped, _) => shaped.baseline,
Self::Frame(frame, _) => frame.baseline,
}
@@ -287,7 +287,7 @@ impl<'a> LineStack<'a> {
regions,
finished: vec![],
lines: vec![],
- size: Size::ZERO,
+ size: Size::zero(),
}
}
@@ -308,13 +308,13 @@ impl<'a> LineStack<'a> {
}
let mut output = Frame::new(self.size, self.size.height);
- let mut offset = Length::ZERO;
+ let mut offset = Length::zero();
let mut first = true;
for line in std::mem::take(&mut self.lines) {
let frame = line.build(self.size.width);
- let pos = Point::new(Length::ZERO, offset);
+ let pos = Point::new(Length::zero(), offset);
if first {
output.baseline = pos.y + frame.baseline;
first = false;
@@ -326,7 +326,7 @@ impl<'a> LineStack<'a> {
self.finished.push(output);
self.regions.next();
- self.size = Size::ZERO;
+ self.size = Size::zero();
}
fn finish(mut self) -> Vec<Frame> {
@@ -421,9 +421,9 @@ impl<'a> LineLayout<'a> {
}
}
- let mut width = Length::ZERO;
- let mut top = Length::ZERO;
- let mut bottom = Length::ZERO;
+ let mut width = Length::zero();
+ let mut top = Length::zero();
+ let mut bottom = Length::zero();
// Measure the size of the line.
for item in first.iter().chain(items).chain(&last) {
@@ -452,7 +452,7 @@ impl<'a> LineLayout<'a> {
let free = size.width - self.size.width;
let mut output = Frame::new(size, self.baseline);
- let mut offset = Length::ZERO;
+ let mut offset = Length::zero();
let mut ruler = Align::Start;
self.reordered(|item| {
diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs
index da25b7a6..f8ab7037 100644
--- a/src/layout/shaping.rs
+++ b/src/layout/shaping.rs
@@ -62,7 +62,7 @@ impl<'a> ShapedText<'a> {
/// Build the shaped text's frame.
pub fn build(&self) -> Frame {
let mut frame = Frame::new(self.size, self.baseline);
- let mut offset = Length::ZERO;
+ let mut offset = Length::zero();
for (face_id, group) in self.glyphs.as_ref().group_by_key(|g| g.face_id) {
let pos = Point::new(offset, self.baseline);
@@ -331,9 +331,9 @@ fn measure(
glyphs: &[ShapedGlyph],
props: &FontProps,
) -> (Size, Length) {
- let mut width = Length::ZERO;
- let mut top = Length::ZERO;
- let mut bottom = Length::ZERO;
+ let mut width = Length::zero();
+ let mut top = Length::zero();
+ let mut bottom = Length::zero();
let mut expand_vertical = |face: &Face| {
top.set_max(face.vertical_metric(props.top_edge).to_length(props.size));
bottom.set_max(-face.vertical_metric(props.bottom_edge).to_length(props.size));
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 11f9c3d7..bb767378 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -1,7 +1,9 @@
+use decorum::NotNan;
+
use super::*;
/// A node that stacks its children.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub struct StackNode {
/// The `main` and `cross` directions of this stack.
///
@@ -11,13 +13,13 @@ pub struct StackNode {
/// The fixed aspect ratio between width and height, if any.
///
/// The resulting frames will satisfy `width = aspect * height`.
- pub aspect: Option<f64>,
+ pub aspect: Option<NotNan<f64>>,
/// The nodes to be stacked.
pub children: Vec<StackChild>,
}
/// A child of a stack node.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Hash)]
pub enum StackChild {
/// Spacing between other nodes.
Spacing(Length),
@@ -56,7 +58,7 @@ impl From<StackNode> for AnyNode {
struct StackLayouter {
dirs: Gen<Dir>,
- aspect: Option<f64>,
+ aspect: Option<NotNan<f64>>,
main: SpecAxis,
regions: Regions,
finished: Vec<Frame>,
@@ -67,7 +69,7 @@ struct StackLayouter {
}
impl StackLayouter {
- fn new(dirs: Gen<Dir>, aspect: Option<f64>, mut regions: Regions) -> Self {
+ fn new(dirs: Gen<Dir>, aspect: Option<NotNan<f64>>, mut regions: Regions) -> Self {
if let Some(aspect) = aspect {
regions.apply_aspect_ratio(aspect);
}
@@ -79,7 +81,7 @@ impl StackLayouter {
finished: vec![],
frames: vec![],
full: regions.current,
- size: Gen::ZERO,
+ size: Gen::zero(),
ruler: Align::Start,
regions,
}
@@ -122,11 +124,11 @@ impl StackLayouter {
if let Some(aspect) = self.aspect {
let width = size
.width
- .max(aspect * size.height)
+ .max(aspect.into_inner() * size.height)
.min(self.full.width)
- .min(aspect * self.full.height);
+ .min(aspect.into_inner() * self.full.height);
- size = Size::new(width, width / aspect);
+ size = Size::new(width, width / aspect.into_inner());
}
let mut output = Frame::new(size, size.height);
@@ -141,7 +143,7 @@ impl StackLayouter {
// Align along the cross axis.
let cross = aligns
.cross
- .resolve(self.dirs.cross, Length::ZERO .. size.cross - child.cross);
+ .resolve(self.dirs.cross, Length::zero() .. size.cross - child.cross);
// Align along the main axis.
let main = aligns.main.resolve(
@@ -163,7 +165,7 @@ impl StackLayouter {
output.push_frame(pos, frame);
}
- self.size = Gen::ZERO;
+ self.size = Gen::zero();
self.ruler = Align::Start;
self.regions.next();
if let Some(aspect) = self.aspect {