summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-11-16 11:40:42 +0100
committerLaurenz <laurmaedje@gmail.com>2021-11-16 11:40:42 +0100
commit73c4701749ac3919c5b845002052326502c67306 (patch)
tree9624be821a739b52403207fbda87dc6389f68637 /src
parent0e0f340502beada1cd9ee23857f48b91a0d11a90 (diff)
Introduce `SizedNode`
Diffstat (limited to 'src')
-rw-r--r--src/geom/paint.rs7
-rw-r--r--src/library/container.rs26
-rw-r--r--src/library/mod.rs4
-rw-r--r--src/library/par.rs12
-rw-r--r--src/library/shape.rs93
-rw-r--r--src/library/sized.rs100
-rw-r--r--src/library/stack.rs4
7 files changed, 149 insertions, 97 deletions
diff --git a/src/geom/paint.rs b/src/geom/paint.rs
index 1d82966c..74d7d147 100644
--- a/src/geom/paint.rs
+++ b/src/geom/paint.rs
@@ -45,10 +45,15 @@ impl RgbaColor {
/// White color.
pub const WHITE: Self = Self { r: 255, g: 255, b: 255, a: 255 };
- /// Constructs a new RGBA color.
+ /// Construct a new RGBA color.
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
+
+ /// Construct a new, opaque gray color.
+ pub fn gray(luma: u8) -> Self {
+ Self::new(luma, luma, luma, 255)
+ }
}
impl FromStr for RgbaColor {
diff --git a/src/library/container.rs b/src/library/container.rs
deleted file mode 100644
index 2bcbbd19..00000000
--- a/src/library/container.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use super::prelude::*;
-use super::{ShapeKind, ShapeNode};
-
-/// `box`: Place content in a rectangular box.
-pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
- let width = args.named("width")?;
- let height = args.named("height")?;
- let body: Template = args.find().unwrap_or_default();
- Ok(Value::Template(Template::from_inline(move |style| {
- ShapeNode {
- shape: ShapeKind::Rect,
- width,
- height,
- fill: None,
- child: Some(body.to_flow(style).pack()),
- }
- })))
-}
-
-/// `block`: Place content in a block.
-pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
- let body: Template = args.expect("body")?;
- Ok(Value::Template(Template::from_block(move |style| {
- body.to_flow(style)
- })))
-}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index f11e05f3..fdede8bd 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -4,7 +4,6 @@
//! definitions.
mod align;
-mod container;
mod deco;
mod document;
mod flow;
@@ -14,6 +13,7 @@ mod pad;
mod page;
mod par;
mod shape;
+mod sized;
mod spacing;
mod stack;
mod text;
@@ -35,7 +35,6 @@ mod prelude {
pub use self::image::*;
pub use align::*;
-pub use container::*;
pub use deco::*;
pub use document::*;
pub use flow::*;
@@ -44,6 +43,7 @@ pub use pad::*;
pub use page::*;
pub use par::*;
pub use shape::*;
+pub use sized::*;
pub use spacing::*;
pub use stack::*;
pub use text::*;
diff --git a/src/library/par.rs b/src/library/par.rs
index c8befc2d..d0f31016 100644
--- a/src/library/par.rs
+++ b/src/library/par.rs
@@ -77,7 +77,7 @@ impl Layout 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.clone(), bidi);
+ let layouter = ParLayouter::new(self, ctx, regions, bidi);
// Find suitable linebreaks.
layouter.layout(ctx, regions.clone())
@@ -182,12 +182,9 @@ impl<'a> ParLayouter<'a> {
fn new(
par: &'a ParNode,
ctx: &mut LayoutContext,
- mut regions: Regions,
+ 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![];
@@ -219,7 +216,10 @@ impl<'a> ParLayouter<'a> {
}
}
ParChild::Node(ref node, align) => {
- let frame = node.layout(ctx, &regions).remove(0);
+ let size = Size::new(regions.current.w, regions.base.h);
+ let expand = Spec::splat(false);
+ let pod = Regions::one(size, regions.base, expand);
+ let frame = node.layout(ctx, &pod).remove(0);
items.push(ParItem::Frame(Rc::take(frame.item), align));
ranges.push(range);
}
diff --git a/src/library/shape.rs b/src/library/shape.rs
index c64dedb3..112987ad 100644
--- a/src/library/shape.rs
+++ b/src/library/shape.rs
@@ -1,7 +1,7 @@
use std::f64::consts::SQRT_2;
use super::prelude::*;
-use super::PadNode;
+use super::{PadNode, SizedNode};
use crate::util::RcExt;
/// `rect`: A rectangle with optional content.
@@ -55,7 +55,7 @@ pub fn circle(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
}
fn shape_impl(
- shape: ShapeKind,
+ kind: ShapeKind,
mut width: Option<Linear>,
mut height: Option<Linear>,
fill: Option<Color>,
@@ -65,20 +65,30 @@ fn shape_impl(
if body.is_none() {
let v = Length::pt(30.0).into();
height.get_or_insert(v);
- width.get_or_insert(match shape {
+ width.get_or_insert(match kind {
ShapeKind::Square | ShapeKind::Circle => v,
ShapeKind::Rect | ShapeKind::Ellipse => 1.5 * v,
});
}
- Value::Template(Template::from_inline(move |style| ShapeNode {
- shape,
- width,
- height,
- fill: Some(Paint::Color(
- fill.unwrap_or(Color::Rgba(RgbaColor::new(175, 175, 175, 255))),
- )),
- child: body.as_ref().map(|template| template.to_flow(style).pack()),
+ // Set default fill if there's no fill.
+ let fill = fill.unwrap_or(Color::Rgba(RgbaColor::gray(175)));
+
+ Value::Template(Template::from_inline(move |style| {
+ let shape = Layout::pack(ShapeNode {
+ kind,
+ fill: Some(Paint::Color(fill)),
+ child: body.as_ref().map(|body| body.to_flow(style).pack()),
+ });
+
+ if width.is_some() || height.is_some() {
+ Layout::pack(SizedNode {
+ sizing: Spec::new(width, height),
+ child: shape,
+ })
+ } else {
+ shape
+ }
}))
}
@@ -86,11 +96,7 @@ fn shape_impl(
#[derive(Debug, Hash)]
pub struct ShapeNode {
/// Which shape to place the child into.
- pub shape: ShapeKind,
- /// The width, if any.
- pub width: Option<Linear>,
- /// The height, if any.
- pub height: Option<Linear>,
+ pub kind: ShapeKind,
/// How to fill the shape, if at all.
pub fill: Option<Paint>,
/// The child node to place into the shape, if any.
@@ -116,33 +122,12 @@ impl Layout for ShapeNode {
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(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 Layout = child;
let padded;
- if matches!(self.shape, ShapeKind::Circle | ShapeKind::Ellipse) {
+ if matches!(self.kind, ShapeKind::Circle | ShapeKind::Ellipse) {
// Padding with this ratio ensures that a rectangular child fits
// perfectly into a circle / an ellipse.
padded = PadNode {
@@ -152,29 +137,14 @@ impl Layout for ShapeNode {
node = &padded;
}
- // The "pod" is the region into which the child will be layouted.
- let mut pod = {
- 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 { regions.base.w },
- if height.is_some() { size.h } else { regions.base.h },
- );
-
- let expand = Spec::new(width.is_some(), height.is_some());
- Regions::one(size, base, expand)
- };
-
// Now, layout the child.
- let mut frames = node.layout(ctx, &pod);
+ let mut frames = node.layout(ctx, regions);
- if matches!(self.shape, ShapeKind::Square | ShapeKind::Circle) {
+ if matches!(self.kind, ShapeKind::Square | ShapeKind::Circle) {
// Relayout with full expansion into square region to make sure
// the result is really a square or circle.
let size = frames[0].item.size;
+ let mut pod = regions.clone();
pod.current.w = size.w.max(size.h).min(pod.current.w);
pod.current.h = pod.current.w;
pod.expand = Spec::splat(true);
@@ -185,14 +155,12 @@ impl Layout for ShapeNode {
assert_eq!(frames.len(), 1);
Rc::take(frames.into_iter().next().unwrap().item)
} else {
- // Resolve shape size.
- let size = Size::new(width.unwrap_or_default(), height.unwrap_or_default());
- Frame::new(size, size.h)
+ Frame::new(regions.current, regions.current.h)
};
// Add background shape if desired.
if let Some(fill) = self.fill {
- let (pos, geometry) = match self.shape {
+ let (pos, geometry) = match self.kind {
ShapeKind::Square | ShapeKind::Rect => {
(Point::zero(), Geometry::Rect(frame.size))
}
@@ -204,6 +172,11 @@ impl Layout for ShapeNode {
frame.prepend(pos, Element::Geometry(geometry, fill));
}
+ // Generate tight constraints for now.
+ let mut cts = Constraints::new(regions.expand);
+ cts.exact = regions.current.to_spec().map(Some);
+ cts.base = regions.base.to_spec().map(Some);
+
vec![frame.constrain(cts)]
}
}
diff --git a/src/library/sized.rs b/src/library/sized.rs
new file mode 100644
index 00000000..631f9e6f
--- /dev/null
+++ b/src/library/sized.rs
@@ -0,0 +1,100 @@
+use super::prelude::*;
+
+/// `box`: Size content and place it into a paragraph.
+pub fn box_(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ let body: Template = args.find().unwrap_or_default();
+ Ok(Value::Template(Template::from_inline(move |style| {
+ let flow = body.to_flow(style).pack();
+ if width.is_some() || height.is_some() {
+ Layout::pack(SizedNode {
+ sizing: Spec::new(width, height),
+ child: flow,
+ })
+ } else {
+ flow
+ }
+ })))
+}
+
+/// `block`: Size content and place it into the flow.
+pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ let body: Template = args.find().unwrap_or_default();
+ Ok(Value::Template(Template::from_block(move |style| {
+ let flow = body.to_flow(style).pack();
+ if width.is_some() || height.is_some() {
+ Layout::pack(SizedNode {
+ sizing: Spec::new(width, height),
+ child: flow,
+ })
+ } else {
+ flow
+ }
+ })))
+}
+
+/// A node that sizes its child.
+#[derive(Debug, Hash)]
+pub struct SizedNode {
+ /// The node to-be-sized.
+ pub child: PackedNode,
+ /// How to size the node horizontally and vertically.
+ pub sizing: Spec<Option<Linear>>,
+}
+
+impl Layout for SizedNode {
+ 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.sizing.x.map(|w| w.resolve(regions.base.w));
+ let height = self.sizing.y.map(|h| h.resolve(regions.base.h));
+
+ // Generate constraints.
+ let mut cts = Constraints::new(regions.expand);
+ cts.set_base_if_linear(regions.base, self.sizing);
+
+ // Set tight exact and base constraints if the child is
+ // automatically sized since we don't know what the child might do.
+ if self.sizing.x.is_none() {
+ cts.exact.x = Some(regions.current.w);
+ cts.base.x = Some(regions.base.w);
+ }
+
+ // Same here.
+ if self.sizing.y.is_none() {
+ cts.exact.y = Some(regions.current.h);
+ cts.base.y = Some(regions.base.h);
+ }
+
+ // The "pod" is the region into which the child will be layouted.
+ let pod = {
+ let size = Size::new(
+ width.unwrap_or(regions.current.w),
+ height.unwrap_or(regions.current.h),
+ );
+
+ let base = Size::new(
+ 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() || regions.expand.x,
+ height.is_some() || regions.expand.y,
+ );
+
+ // TODO: Allow multiple regions if only width is set.
+ Regions::one(size, base, expand)
+ };
+
+ let mut frames = self.child.layout(ctx, &pod);
+ frames[0].cts = cts;
+ frames
+ }
+}
diff --git a/src/library/stack.rs b/src/library/stack.rs
index 46825939..dec5ec2d 100644
--- a/src/library/stack.rs
+++ b/src/library/stack.rs
@@ -35,12 +35,12 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
children.push(StackChild::Spacing(*v));
delayed = None;
}
- Child::Any(template) => {
+ Child::Any(child) => {
if let Some(v) = delayed {
children.push(StackChild::Spacing(v));
}
- let node = template.to_flow(style).pack();
+ let node = child.to_flow(style).pack();
children.push(StackChild::Node(node));
delayed = spacing;
}