summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-11-16 10:41:30 +0100
committerLaurenz <laurmaedje@gmail.com>2021-11-16 10:41:30 +0100
commit0e0f340502beada1cd9ee23857f48b91a0d11a90 (patch)
treee4e4087260720adf4bab57edeac6a3d3cb5f14cf /src/library
parentbc118634aca5de415d211cab38c4eaa3d3cca25f (diff)
Revert page and inline levels
Diffstat (limited to 'src/library')
-rw-r--r--src/library/container.rs3
-rw-r--r--src/library/document.rs16
-rw-r--r--src/library/flow.rs10
-rw-r--r--src/library/grid.rs8
-rw-r--r--src/library/image.rs23
-rw-r--r--src/library/mod.rs2
-rw-r--r--src/library/pad.rs6
-rw-r--r--src/library/page.rs20
-rw-r--r--src/library/par.rs17
-rw-r--r--src/library/shape.rs44
-rw-r--r--src/library/stack.rs8
-rw-r--r--src/library/transform.rs40
12 files changed, 136 insertions, 61 deletions
diff --git a/src/library/container.rs b/src/library/container.rs
index 4b52139f..2bcbbd19 100644
--- a/src/library/container.rs
+++ b/src/library/container.rs
@@ -5,14 +5,13 @@ use super::{ShapeKind, ShapeNode};
pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let width = args.named("width")?;
let height = args.named("height")?;
- let fill = args.named("fill")?;
let body: Template = args.find().unwrap_or_default();
Ok(Value::Template(Template::from_inline(move |style| {
ShapeNode {
shape: ShapeKind::Rect,
width,
height,
- fill: fill.map(Paint::Color),
+ fill: None,
child: Some(body.to_flow(style).pack()),
}
})))
diff --git a/src/library/document.rs b/src/library/document.rs
new file mode 100644
index 00000000..fe01d2df
--- /dev/null
+++ b/src/library/document.rs
@@ -0,0 +1,16 @@
+use super::prelude::*;
+use super::PageNode;
+
+/// The root layout node, a document consisting of top-level page runs.
+#[derive(Debug, Hash)]
+pub struct DocumentNode {
+ /// The page runs.
+ pub pages: Vec<PageNode>,
+}
+
+impl DocumentNode {
+ /// Layout the document into a sequence of frames, one per page.
+ pub fn layout(&self, ctx: &mut LayoutContext) -> Vec<Rc<Frame>> {
+ self.pages.iter().flat_map(|node| node.layout(ctx)).collect()
+ }
+}
diff --git a/src/library/flow.rs b/src/library/flow.rs
index cc53d520..c41fb62c 100644
--- a/src/library/flow.rs
+++ b/src/library/flow.rs
@@ -48,7 +48,7 @@ pub struct FlowNode {
pub children: Vec<FlowChild>,
}
-impl BlockLevel for FlowNode {
+impl Layout for FlowNode {
fn layout(
&self,
ctx: &mut LayoutContext,
@@ -63,8 +63,8 @@ impl BlockLevel for FlowNode {
pub enum FlowChild {
/// Vertical spacing between other children.
Spacing(Spacing),
- /// Any block node and how to align it in the flow.
- Node(BlockNode, Align),
+ /// A node and how to align it in the flow.
+ Node(PackedNode, Align),
}
impl Debug for FlowChild {
@@ -157,8 +157,8 @@ impl<'a> FlowLayouter<'a> {
self.items.push(FlowItem::Absolute(resolved));
}
- /// Layout a block node.
- fn layout_node(&mut self, ctx: &mut LayoutContext, node: &BlockNode, align: Align) {
+ /// Layout a node.
+ fn layout_node(&mut self, ctx: &mut LayoutContext, node: &PackedNode, align: Align) {
let frames = node.layout(ctx, &self.regions);
let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() {
diff --git a/src/library/grid.rs b/src/library/grid.rs
index bafd639c..692ed210 100644
--- a/src/library/grid.rs
+++ b/src/library/grid.rs
@@ -58,7 +58,7 @@ pub struct GridNode {
/// Defines sizing of gutter rows and columns between content.
pub gutter: Spec<Vec<TrackSizing>>,
/// The nodes to be arranged in a grid.
- pub children: Vec<BlockNode>,
+ pub children: Vec<PackedNode>,
}
/// Defines how to size a grid cell along an axis.
@@ -72,7 +72,7 @@ pub enum TrackSizing {
Fractional(Fractional),
}
-impl BlockLevel for GridNode {
+impl Layout for GridNode {
fn layout(
&self,
ctx: &mut LayoutContext,
@@ -92,7 +92,7 @@ impl BlockLevel for GridNode {
/// Performs grid layout.
struct GridLayouter<'a> {
/// The children of the grid.
- children: &'a [BlockNode],
+ children: &'a [PackedNode],
/// Whether the grid should expand to fill the region.
expand: Spec<bool>,
/// The column tracks including gutter tracks.
@@ -591,7 +591,7 @@ impl<'a> GridLayouter<'a> {
///
/// Returns `None` if it's a gutter cell.
#[track_caller]
- fn cell(&self, x: usize, y: usize) -> Option<&'a BlockNode> {
+ fn cell(&self, x: usize, y: usize) -> Option<&'a PackedNode> {
assert!(x < self.cols.len());
assert!(y < self.rows.len());
diff --git a/src/library/image.rs b/src/library/image.rs
index b51e4e70..f93d4b54 100644
--- a/src/library/image.rs
+++ b/src/library/image.rs
@@ -36,23 +36,31 @@ pub struct ImageNode {
pub height: Option<Linear>,
}
-impl InlineLevel for ImageNode {
- fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame {
+impl Layout for ImageNode {
+ fn layout(
+ &self,
+ ctx: &mut LayoutContext,
+ regions: &Regions,
+ ) -> Vec<Constrained<Rc<Frame>>> {
let img = ctx.images.get(self.id);
let pixel_size = Spec::new(img.width() as f64, img.height() as f64);
let pixel_ratio = pixel_size.x / pixel_size.y;
- let width = self.width.map(|w| w.resolve(base.w));
- let height = self.height.map(|w| w.resolve(base.h));
+ let width = self.width.map(|w| w.resolve(regions.base.w));
+ let height = self.height.map(|w| w.resolve(regions.base.h));
+
+ let mut cts = Constraints::new(regions.expand);
+ cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height));
let size = match (width, height) {
(Some(width), Some(height)) => Size::new(width, height),
(Some(width), None) => Size::new(width, width / pixel_ratio),
(None, Some(height)) => Size::new(height * pixel_ratio, height),
(None, None) => {
- if space.is_finite() {
+ cts.exact.x = Some(regions.current.w);
+ if regions.current.w.is_finite() {
// Fit to width.
- Size::new(space, space / pixel_ratio)
+ Size::new(regions.current.w, regions.current.w / pixel_ratio)
} else {
// Unbounded width, we have to make up something,
// so it is 1pt per pixel.
@@ -63,6 +71,7 @@ impl InlineLevel for ImageNode {
let mut frame = Frame::new(size, size.h);
frame.push(Point::zero(), Element::Image(self.id, size));
- frame
+
+ vec![frame.constrain(cts)]
}
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 775fb51f..f11e05f3 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -6,6 +6,7 @@
mod align;
mod container;
mod deco;
+mod document;
mod flow;
mod grid;
mod image;
@@ -36,6 +37,7 @@ pub use self::image::*;
pub use align::*;
pub use container::*;
pub use deco::*;
+pub use document::*;
pub use flow::*;
pub use grid::*;
pub use pad::*;
diff --git a/src/library/pad.rs b/src/library/pad.rs
index d17a4ca2..88f8c562 100644
--- a/src/library/pad.rs
+++ b/src/library/pad.rs
@@ -16,7 +16,7 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
bottom.or(all).unwrap_or_default(),
);
- Ok(Value::Template(Template::from_block(move |style| {
+ Ok(Value::Template(Template::from_inline(move |style| {
PadNode {
padding,
child: body.to_flow(style).pack(),
@@ -30,10 +30,10 @@ pub struct PadNode {
/// The amount of padding.
pub padding: Sides<Linear>,
/// The child node whose sides to pad.
- pub child: BlockNode,
+ pub child: PackedNode,
}
-impl BlockLevel for PadNode {
+impl Layout for PadNode {
fn layout(
&self,
ctx: &mut LayoutContext,
diff --git a/src/library/page.rs b/src/library/page.rs
index 16e6283d..a5935002 100644
--- a/src/library/page.rs
+++ b/src/library/page.rs
@@ -73,3 +73,23 @@ pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
template.pagebreak(true);
Ok(Value::Template(template))
}
+
+/// Layouts its children onto one or multiple pages.
+#[derive(Debug, Hash)]
+pub struct PageNode {
+ /// The size of the page.
+ pub size: Size,
+ /// The node that produces the actual pages.
+ pub child: PackedNode,
+}
+
+impl PageNode {
+ /// Layout the page run into a sequence of frames, one per page.
+ 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 expand = self.size.to_spec().map(Length::is_finite);
+ let regions = Regions::repeat(self.size, self.size, expand);
+ self.child.layout(ctx, &regions).into_iter().map(|c| c.item).collect()
+ }
+}
diff --git a/src/library/par.rs b/src/library/par.rs
index 216b7d41..c8befc2d 100644
--- a/src/library/par.rs
+++ b/src/library/par.rs
@@ -8,7 +8,7 @@ use xi_unicode::LineBreakIterator;
use super::prelude::*;
use super::{shape, Decoration, ShapedText, Spacing};
use crate::style::TextStyle;
-use crate::util::{EcoString, RangeExt, SliceExt};
+use crate::util::{EcoString, RangeExt, RcExt, SliceExt};
/// `par`: Configure paragraphs.
pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
@@ -63,7 +63,7 @@ pub struct ParNode {
pub children: Vec<ParChild>,
}
-impl BlockLevel for ParNode {
+impl Layout for ParNode {
fn layout(
&self,
ctx: &mut LayoutContext,
@@ -77,7 +77,7 @@ impl BlockLevel for ParNode {
// Prepare paragraph layout by building a representation on which we can
// do line breaking without layouting each and every line from scratch.
- let layouter = ParLayouter::new(self, ctx, regions, bidi);
+ let layouter = ParLayouter::new(self, ctx, regions.clone(), bidi);
// Find suitable linebreaks.
layouter.layout(ctx, regions.clone())
@@ -126,7 +126,7 @@ pub enum ParChild {
/// A run of text and how to align it in its line.
Text(EcoString, Align, Rc<TextStyle>),
/// Any child node and how to align it in its line.
- Node(InlineNode, Align),
+ Node(PackedNode, Align),
/// A decoration that applies until a matching `Undecorate`.
Decorate(Decoration),
/// The end of a decoration.
@@ -182,9 +182,12 @@ impl<'a> ParLayouter<'a> {
fn new(
par: &'a ParNode,
ctx: &mut LayoutContext,
- regions: &Regions,
+ mut regions: Regions,
bidi: BidiInfo<'a>,
) -> Self {
+ // Disable expansion for children.
+ regions.expand = Spec::splat(false);
+
let mut items = vec![];
let mut ranges = vec![];
let mut starts = vec![];
@@ -216,8 +219,8 @@ impl<'a> ParLayouter<'a> {
}
}
ParChild::Node(ref node, align) => {
- let frame = node.layout(ctx, regions.current.w, regions.base);
- items.push(ParItem::Frame(frame, align));
+ let frame = node.layout(ctx, &regions).remove(0);
+ items.push(ParItem::Frame(Rc::take(frame.item), align));
ranges.push(range);
}
ParChild::Decorate(ref deco) => {
diff --git a/src/library/shape.rs b/src/library/shape.rs
index 5be26aa4..c64dedb3 100644
--- a/src/library/shape.rs
+++ b/src/library/shape.rs
@@ -94,7 +94,7 @@ pub struct ShapeNode {
/// How to fill the shape, if at all.
pub fill: Option<Paint>,
/// The child node to place into the shape, if any.
- pub child: Option<BlockNode>,
+ pub child: Option<PackedNode>,
}
/// The type of a shape.
@@ -110,15 +110,36 @@ pub enum ShapeKind {
Ellipse,
}
-impl InlineLevel for ShapeNode {
- fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame {
+impl Layout for ShapeNode {
+ fn layout(
+ &self,
+ ctx: &mut LayoutContext,
+ regions: &Regions,
+ ) -> Vec<Constrained<Rc<Frame>>> {
// Resolve width and height relative to the region's base.
- let width = self.width.map(|w| w.resolve(base.w));
- let height = self.height.map(|h| h.resolve(base.h));
+ let width = self.width.map(|w| w.resolve(regions.base.w));
+ let height = self.height.map(|h| h.resolve(regions.base.h));
+
+ // Generate constraints.
+ let mut cts = Constraints::new(regions.expand);
+ cts.set_base_if_linear(regions.base, Spec::new(self.width, self.height));
+
+ // Set tight exact and base constraints if the child is
+ // automatically sized since we don't know what the child might do.
+ if self.width.is_none() {
+ cts.exact.x = Some(regions.current.w);
+ cts.base.x = Some(regions.base.w);
+ }
+
+ // Same here.
+ if self.height.is_none() {
+ cts.exact.y = Some(regions.current.h);
+ cts.base.y = Some(regions.base.h);
+ }
// Layout.
let mut frame = if let Some(child) = &self.child {
- let mut node: &dyn BlockLevel = child;
+ let mut node: &dyn Layout = child;
let padded;
if matches!(self.shape, ShapeKind::Circle | ShapeKind::Ellipse) {
@@ -133,11 +154,14 @@ impl InlineLevel for ShapeNode {
// The "pod" is the region into which the child will be layouted.
let mut pod = {
- let size = Size::new(width.unwrap_or(space), height.unwrap_or(base.h));
+ let size = Size::new(
+ width.unwrap_or(regions.current.w),
+ height.unwrap_or(regions.base.h),
+ );
let base = Size::new(
- if width.is_some() { size.w } else { base.w },
- if height.is_some() { size.h } else { base.h },
+ if width.is_some() { size.w } else { regions.base.w },
+ if height.is_some() { size.h } else { regions.base.h },
);
let expand = Spec::new(width.is_some(), height.is_some());
@@ -180,6 +204,6 @@ impl InlineLevel for ShapeNode {
frame.prepend(pos, Element::Geometry(geometry, fill));
}
- frame
+ vec![frame.constrain(cts)]
}
}
diff --git a/src/library/stack.rs b/src/library/stack.rs
index fe1deda0..46825939 100644
--- a/src/library/stack.rs
+++ b/src/library/stack.rs
@@ -60,7 +60,7 @@ pub struct StackNode {
pub children: Vec<StackChild>,
}
-impl BlockLevel for StackNode {
+impl Layout for StackNode {
fn layout(
&self,
ctx: &mut LayoutContext,
@@ -76,7 +76,7 @@ pub enum StackChild {
/// Spacing between other nodes.
Spacing(Spacing),
/// Any block node and how to align it in the stack.
- Node(BlockNode),
+ Node(PackedNode),
}
impl Debug for StackChild {
@@ -174,8 +174,8 @@ impl<'a> StackLayouter<'a> {
self.items.push(StackItem::Absolute(resolved));
}
- /// Layout a block node.
- fn layout_node(&mut self, ctx: &mut LayoutContext, node: &BlockNode) {
+ /// Layout a node.
+ fn layout_node(&mut self, ctx: &mut LayoutContext, node: &PackedNode) {
let frames = node.layout(ctx, &self.regions);
let len = frames.len();
for (i, frame) in frames.into_iter().enumerate() {
diff --git a/src/library/transform.rs b/src/library/transform.rs
index b7e5e36c..20d2bc1d 100644
--- a/src/library/transform.rs
+++ b/src/library/transform.rs
@@ -1,5 +1,4 @@
use super::prelude::*;
-use super::{ShapeKind, ShapeNode};
/// `move`: Move content without affecting layout.
pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
@@ -10,13 +9,7 @@ pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
Ok(Value::Template(Template::from_inline(move |style| {
MoveNode {
offset: Spec::new(x, y),
- child: ShapeNode {
- shape: ShapeKind::Rect,
- width: None,
- height: None,
- fill: None,
- child: Some(body.to_flow(style).pack()),
- },
+ child: body.to_flow(style).pack(),
}
})))
}
@@ -24,21 +17,30 @@ pub fn move_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
#[derive(Debug, Hash)]
struct MoveNode {
offset: Spec<Option<Linear>>,
- child: ShapeNode,
+ child: PackedNode,
}
-impl InlineLevel for MoveNode {
- fn layout(&self, ctx: &mut LayoutContext, space: Length, base: Size) -> Frame {
- let offset = Point::new(
- self.offset.x.map(|x| x.resolve(base.w)).unwrap_or_default(),
- self.offset.y.map(|y| y.resolve(base.h)).unwrap_or_default(),
- );
+impl Layout for MoveNode {
+ fn layout(
+ &self,
+ ctx: &mut LayoutContext,
+ regions: &Regions,
+ ) -> Vec<Constrained<Rc<Frame>>> {
+ let mut frames = self.child.layout(ctx, regions);
- let mut frame = self.child.layout(ctx, space, base);
- for (point, _) in &mut frame.children {
- *point += offset;
+ for (Constrained { item: frame, .. }, (_, base)) in
+ frames.iter_mut().zip(regions.iter())
+ {
+ let offset = Point::new(
+ self.offset.x.map(|x| x.resolve(base.w)).unwrap_or_default(),
+ self.offset.y.map(|y| y.resolve(base.h)).unwrap_or_default(),
+ );
+
+ for (point, _) in &mut Rc::make_mut(frame).children {
+ *point += offset;
+ }
}
- frame
+ frames
}
}