diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-11-26 23:51:18 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-11-26 23:51:18 +0100 |
| commit | 50bd8634711507ead8491d8d0c2abad0481e6a83 (patch) | |
| tree | eb4a9cfc659334be5444f1e7f9d06e35439d455c /src | |
| parent | 3a15922d2ffc041c3523edb479f008a9034fd400 (diff) | |
More independent placed node
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/template.rs | 21 | ||||
| -rw-r--r-- | src/frame.rs | 10 | ||||
| -rw-r--r-- | src/geom/align.rs | 6 | ||||
| -rw-r--r-- | src/geom/spec.rs | 16 | ||||
| -rw-r--r-- | src/layout/mod.rs | 5 | ||||
| -rw-r--r-- | src/library/align.rs | 16 | ||||
| -rw-r--r-- | src/library/flow.rs | 24 | ||||
| -rw-r--r-- | src/library/image.rs | 12 | ||||
| -rw-r--r-- | src/library/mod.rs | 12 | ||||
| -rw-r--r-- | src/library/pad.rs | 4 | ||||
| -rw-r--r-- | src/library/placed.rs | 46 | ||||
| -rw-r--r-- | src/library/sized.rs | 12 | ||||
| -rw-r--r-- | src/library/transform.rs | 5 |
13 files changed, 126 insertions, 63 deletions
diff --git a/src/eval/template.rs b/src/eval/template.rs index e9cbbfcf..d447161a 100644 --- a/src/eval/template.rs +++ b/src/eval/template.rs @@ -337,13 +337,24 @@ impl Builder { /// Push a block node into the active flow, finishing the active paragraph. fn block(&mut self, node: PackedNode) { + let mut is_placed = false; + if let Some(placed) = node.downcast::<PlacedNode>() { + is_placed = true; + + // This prevents paragraph spacing after the placed node if it + // is completely out-of-flow. + if placed.out_of_flow() { + self.flow.last = Last::None; + } + } + self.parbreak(); - let in_flow = node.downcast::<PlacedNode>().is_none(); self.flow.push(FlowChild::Node(node)); - if in_flow { - self.parbreak(); - } else { - // This prevents duplicate paragraph spacing around placed nodes. + self.parbreak(); + + // This prevents paragraph spacing between the placed node and + // the paragraph below it. + if is_placed { self.flow.last = Last::None; } } diff --git a/src/frame.rs b/src/frame.rs index 4eea0578..e02fdf46 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -57,13 +57,13 @@ impl Frame { /// Resize the frame to a new size, distributing new space according to the /// given alignments. - pub fn resize(&mut self, new: Size, aligns: Spec<Align>) { - if self.size != new { + pub fn resize(&mut self, target: Size, aligns: Spec<Align>) { + if self.size != target { let offset = Point::new( - aligns.x.resolve(new.x - self.size.x), - aligns.y.resolve(new.y - self.size.y), + aligns.x.resolve(target.x - self.size.x), + aligns.y.resolve(target.y - self.size.y), ); - self.size = new; + self.size = target; self.baseline += offset.y; self.translate(offset); } diff --git a/src/geom/align.rs b/src/geom/align.rs index f068b821..be2eac96 100644 --- a/src/geom/align.rs +++ b/src/geom/align.rs @@ -18,6 +18,12 @@ pub enum Align { } impl Align { + /// Top-left alignment. + pub const LEFT_TOP: Spec<Self> = Spec { x: Align::Left, y: Align::Top }; + + /// Center-horizon alignment. + pub const CENTER_HORIZON: Spec<Self> = Spec { x: Align::Center, y: Align::Horizon }; + /// The axis this alignment belongs to. pub const fn axis(self) -> SpecAxis { match self { diff --git a/src/geom/spec.rs b/src/geom/spec.rs index 640d6231..16d63959 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -281,6 +281,14 @@ impl BitOr for Spec<bool> { } } +impl BitOr<bool> for Spec<bool> { + type Output = Self; + + fn bitor(self, rhs: bool) -> Self::Output { + Self { x: self.x | rhs, y: self.y | rhs } + } +} + impl BitAnd for Spec<bool> { type Output = Self; @@ -289,6 +297,14 @@ impl BitAnd for Spec<bool> { } } +impl BitAnd<bool> for Spec<bool> { + type Output = Self; + + fn bitand(self, rhs: bool) -> Self::Output { + Self { x: self.x & rhs, y: self.y & rhs } + } +} + impl BitOrAssign for Spec<bool> { fn bitor_assign(&mut self, rhs: Self) { self.x |= rhs.x; diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 405e4e83..9c57152a 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -136,10 +136,7 @@ impl PackedNode { /// Transform this node's contents without affecting layout. pub fn moved(self, offset: Point) -> Self { - self.transformed( - Transform::translation(offset.x, offset.y), - Spec::new(Align::Left, Align::Top), - ) + self.transformed(Transform::translation(offset.x, offset.y), Align::LEFT_TOP) } /// Transform this node's contents without affecting layout. diff --git a/src/library/align.rs b/src/library/align.rs index 7ad5a2d4..a2881cce 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -2,6 +2,18 @@ use super::prelude::*; /// `align`: Configure the alignment along the layouting axes. pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + castable! { + Spec<Option<Align>>, + Expected: "1d or 2d alignment", + @align: Align => { + let mut aligns = Spec::default(); + aligns.set(align.axis(), Some(*align)); + aligns + }, + @aligns: Spec<Align> => aligns.map(Some), + + } + let aligns = args.expect::<Spec<_>>("alignment")?; let body = args.expect::<Template>("body")?; Ok(Value::Template(Template::from_block(move |style| { @@ -36,8 +48,8 @@ impl Layout for AlignNode { // Layout the child. let mut frames = self.child.layout(ctx, &pod); - for (Constrained { item: frame, cts }, (current, base)) in - frames.iter_mut().zip(regions.iter()) + for ((current, base), Constrained { item: frame, cts }) in + regions.iter().zip(&mut frames) { // Align in the target size. The target size depends on whether we // should expand. diff --git a/src/library/flow.rs b/src/library/flow.rs index e105c596..587b5a80 100644 --- a/src/library/flow.rs +++ b/src/library/flow.rs @@ -101,10 +101,10 @@ enum FlowItem { Absolute(Length), /// Fractional spacing between other items. Fractional(Fractional), - /// A frame to be placed directly at the origin. - Placed(Rc<Frame>), /// A frame for a layouted child node and how to align it. Frame(Rc<Frame>, Spec<Align>), + /// An absolutely placed frame. + Placed(Rc<Frame>), } impl<'a> FlowLayouter<'a> { @@ -166,17 +166,11 @@ impl<'a> FlowLayouter<'a> { /// Layout a node. fn layout_node(&mut self, ctx: &mut LayoutContext, node: &PackedNode) { - // Placed nodes with vertical alignment are handled separately - // because their position shouldn't depend on other flow elements. if let Some(placed) = node.downcast::<PlacedNode>() { - if let Some(aligned) = placed.child.downcast::<AlignNode>() { - if aligned.aligns.y.is_some() { - let base = self.regions.base; - let pod = Regions::one(base, base, Spec::splat(true)); - let frame = placed.layout(ctx, &pod).remove(0); - self.items.push(FlowItem::Placed(frame.item)); - return; - } + let frame = node.layout(ctx, &self.regions).remove(0); + if placed.out_of_flow() { + self.items.push(FlowItem::Placed(frame.item)); + return; } } @@ -233,9 +227,6 @@ impl<'a> FlowLayouter<'a> { FlowItem::Fractional(v) => { before += v.resolve(self.fr, remaining); } - FlowItem::Placed(frame) => { - output.push_frame(Point::zero(), frame); - } FlowItem::Frame(frame, aligns) => { ruler = ruler.max(aligns.y); @@ -253,6 +244,9 @@ impl<'a> FlowLayouter<'a> { output.push_frame(pos, frame); } + FlowItem::Placed(frame) => { + output.push_frame(Point::with_y(before), frame); + } } } diff --git a/src/library/image.rs b/src/library/image.rs index 92580f6e..a20f3856 100644 --- a/src/library/image.rs +++ b/src/library/image.rs @@ -51,7 +51,7 @@ impl Layout for ImageNode { let wide = pixel_ratio > current_ratio; // The space into which the image will be placed according to its fit. - let canvas = if expand.x && expand.y { + let target = if expand.x && expand.y { current } else if expand.x || (wide && current.x.is_finite()) { Size::new(current.x, current.y.min(current.x.safe_div(pixel_ratio))) @@ -65,20 +65,20 @@ impl Layout for ImageNode { let size = match self.fit { ImageFit::Contain | ImageFit::Cover => { if wide == (self.fit == ImageFit::Contain) { - Size::new(canvas.x, canvas.x / pixel_ratio) + Size::new(target.x, target.x / pixel_ratio) } else { - Size::new(canvas.y * pixel_ratio, canvas.y) + Size::new(target.y * pixel_ratio, target.y) } } - ImageFit::Stretch => canvas, + ImageFit::Stretch => target, }; // First, place the image in a frame of exactly its size and then resize - // the frame to the canvas size, center aligning the image in the + // the frame to the target size, center aligning the image in the // process. let mut frame = Frame::new(size); frame.push(Point::zero(), Element::Image(self.id, size)); - frame.resize(canvas, Spec::new(Align::Center, Align::Horizon)); + frame.resize(target, Align::CENTER_HORIZON); // Create a clipping group if the fit mode is "cover". if self.fit == ImageFit::Cover { diff --git a/src/library/mod.rs b/src/library/mod.rs index c953a76e..d60a13ea 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -153,15 +153,3 @@ castable! { Expected: "color", Value::Color(color) => Paint::Solid(color), } - -castable! { - Spec<Option<Align>>, - Expected: "1d or 2d alignment", - @align: Align => { - let mut aligns = Spec::default(); - aligns.set(align.axis(), Some(*align)); - aligns - }, - @aligns: Spec<Align> => aligns.map(Some), - -} diff --git a/src/library/pad.rs b/src/library/pad.rs index ce7f4150..4bd7fd64 100644 --- a/src/library/pad.rs +++ b/src/library/pad.rs @@ -39,8 +39,8 @@ impl Layout for PadNode { let pod = regions.map(|size| shrink(size, self.padding)); let mut frames = self.child.layout(ctx, &pod); - for (Constrained { item: frame, cts }, (current, base)) in - frames.iter_mut().zip(regions.iter()) + for ((current, base), Constrained { item: frame, cts }) in + regions.iter().zip(&mut frames) { // Apply the padding inversely such that the grown size padded // yields the frame's size. diff --git a/src/library/placed.rs b/src/library/placed.rs index 4f1c5b85..1ae51530 100644 --- a/src/library/placed.rs +++ b/src/library/placed.rs @@ -1,4 +1,5 @@ use super::prelude::*; +use super::AlignNode; /// `place`: Place content at an absolute position. pub fn place(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { @@ -13,23 +14,60 @@ pub fn place(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { }))) } -/// A node that places its child out-of-flow. +/// A node that places its child absolutely. #[derive(Debug, Hash)] pub struct PlacedNode { /// The node to be placed. pub child: PackedNode, } +impl PlacedNode { + /// Whether this node wants to be placed relative to its its parent's base + /// origin. instead of relative to the parent's current flow/cursor + /// position. + pub fn out_of_flow(&self) -> bool { + self.child + .downcast::<AlignNode>() + .map_or(false, |node| node.aligns.y.is_some()) + } +} + impl Layout for PlacedNode { fn layout( &self, ctx: &mut LayoutContext, regions: &Regions, ) -> Vec<Constrained<Rc<Frame>>> { - let mut frames = self.child.layout(ctx, regions); - for frame in frames.iter_mut() { - Rc::make_mut(&mut frame.item).size = Size::zero(); + let out_of_flow = self.out_of_flow(); + + // The pod is the base area of the region because for absolute + // placement we don't really care about the already used area (current). + let pod = { + let expand = if out_of_flow { Spec::splat(true) } else { regions.expand }; + Regions::one(regions.base, regions.base, expand) + }; + + let mut frames = self.child.layout(ctx, &pod); + let Constrained { item: frame, cts } = &mut frames[0]; + + // If expansion is off, zero all sizes so that we don't take up any + // space in our parent. Otherwise, respect the expand settings. + let target = regions.expand.select(regions.current, Size::zero()); + Rc::make_mut(frame).resize(target, Align::LEFT_TOP); + + // Place relative to parent's base origin by offsetting our elements by + // the negative cursor position. + if out_of_flow { + let offset = (regions.current - regions.base).to_point(); + Rc::make_mut(frame).translate(offset); } + + // Set base constraint because our pod size is base and exact + // constraints if we needed to expand or offset. + *cts = Constraints::new(regions.expand); + cts.base = regions.base.map(Some); + cts.exact = regions.current.filter(regions.expand | out_of_flow); + frames } } diff --git a/src/library/sized.rs b/src/library/sized.rs index 9b2cdf22..f421637b 100644 --- a/src/library/sized.rs +++ b/src/library/sized.rs @@ -56,14 +56,14 @@ impl Layout for SizedNode { }; let mut frames = self.child.layout(ctx, &pod); + let Constrained { cts, .. } = &mut frames[0]; // Set base & exact constraints if the child is automatically sized - // since we don't know what the child might do. Also set base if our - // sizing is relative. - let frame = &mut frames[0]; - frame.cts = Constraints::new(regions.expand); - frame.cts.exact = regions.current.filter(is_auto); - frame.cts.base = regions.base.filter(is_auto | is_rel); + // since we don't know what the child might have done. Also set base if + // our sizing is relative. + *cts = Constraints::new(regions.expand); + cts.exact = regions.current.filter(is_auto); + cts.base = regions.base.filter(is_auto | is_rel); frames } diff --git a/src/library/transform.rs b/src/library/transform.rs index c8b48666..9ba71ecf 100644 --- a/src/library/transform.rs +++ b/src/library/transform.rs @@ -30,7 +30,7 @@ fn transform_impl(args: &mut Args, transform: Transform) -> TypResult<Value> { let origin = args .named("origin")? .unwrap_or(Spec::splat(None)) - .unwrap_or(Spec::new(Align::Center, Align::Horizon)); + .unwrap_or(Align::CENTER_HORIZON); Ok(Value::Template(Template::from_inline(move |style| { body.pack(style).transformed(transform, origin) @@ -56,11 +56,12 @@ impl Layout for TransformNode { ) -> Vec<Constrained<Rc<Frame>>> { let mut frames = self.child.layout(ctx, regions); - for Constrained { item: frame, .. } in frames.iter_mut() { + for Constrained { item: frame, .. } in &mut frames { let Spec { x, y } = self.origin.zip(frame.size).map(|(o, s)| o.resolve(s)); let transform = Transform::translation(x, y) .pre_concat(self.transform) .pre_concat(Transform::translation(-x, -y)); + Rc::make_mut(frame).transform(transform); } |
