mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Refactoring
This commit is contained in:
parent
d9c529347d
commit
b2e6a29789
@ -9,7 +9,7 @@ use crate::diag::StrResult;
|
||||
use crate::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size};
|
||||
use crate::layout::{Layout, PackedNode};
|
||||
use crate::library::{
|
||||
Decoration, DocumentNode, FlowChild, FlowNode, PadNode, PageNode, ParChild, ParNode,
|
||||
Decoration, DocumentNode, FlowChild, FlowNode, PageNode, ParChild, ParNode,
|
||||
PlacedNode, Spacing,
|
||||
};
|
||||
use crate::style::Style;
|
||||
@ -400,7 +400,7 @@ impl PageBuilder {
|
||||
let Self { size, padding, hard } = self;
|
||||
(!child.children.is_empty() || (keep && hard)).then(|| PageNode {
|
||||
size,
|
||||
child: PadNode { padding, child: child.pack() }.pack(),
|
||||
child: child.pack().padded(padding),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,13 @@ impl Frame {
|
||||
}
|
||||
}
|
||||
|
||||
/// Move all elements in the frame by an offset.
|
||||
pub fn translate(&mut self, offset: Point) {
|
||||
for (point, _) in &mut self.elements {
|
||||
*point += offset;
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over all non-frame elements in this and nested frames.
|
||||
pub fn elements(&self) -> Elements {
|
||||
Elements { stack: vec![(0, Point::zero(), self)] }
|
||||
|
@ -23,11 +23,6 @@ impl<T> Spec<T> {
|
||||
Self { x: v.clone(), y: v }
|
||||
}
|
||||
|
||||
/// Borrows the individual fields.
|
||||
pub fn as_ref(&self) -> Spec<&T> {
|
||||
Spec { x: &self.x, y: &self.y }
|
||||
}
|
||||
|
||||
/// Maps the individual fields with `f`.
|
||||
pub fn map<F, U>(self, mut f: F) -> Spec<U>
|
||||
where
|
||||
|
@ -17,9 +17,9 @@ use std::rc::Rc;
|
||||
|
||||
use crate::font::FontStore;
|
||||
use crate::frame::Frame;
|
||||
use crate::geom::{Align, Linear, Spec};
|
||||
use crate::geom::{Align, Linear, Sides, Spec};
|
||||
use crate::image::ImageStore;
|
||||
use crate::library::{AlignNode, DocumentNode, MoveNode, SizedNode};
|
||||
use crate::library::{AlignNode, DocumentNode, MoveNode, PadNode, SizedNode};
|
||||
use crate::Context;
|
||||
|
||||
/// Layout a document node into a collection of frames.
|
||||
@ -129,6 +129,19 @@ impl PackedNode {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Pad this node at the sides.
|
||||
pub fn padded(self, padding: Sides<Linear>) -> Self {
|
||||
if !padding.left.is_zero()
|
||||
|| !padding.top.is_zero()
|
||||
|| !padding.right.is_zero()
|
||||
|| !padding.bottom.is_zero()
|
||||
{
|
||||
PadNode { child: self, padding }.pack()
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for PackedNode {
|
||||
|
@ -49,7 +49,6 @@ impl Layout for AlignNode {
|
||||
pod.expand.y &= self.aligns.y.is_none();
|
||||
|
||||
let mut frames = self.child.layout(ctx, &pod);
|
||||
|
||||
for (Constrained { item: frame, cts }, (current, _)) in
|
||||
frames.iter_mut().zip(regions.iter())
|
||||
{
|
||||
@ -67,10 +66,7 @@ impl Layout for AlignNode {
|
||||
let frame = Rc::make_mut(frame);
|
||||
frame.size = canvas;
|
||||
frame.baseline += offset.y;
|
||||
|
||||
for (point, _) in &mut frame.elements {
|
||||
*point += offset;
|
||||
}
|
||||
frame.translate(offset);
|
||||
|
||||
cts.expand = regions.expand;
|
||||
cts.exact = current.to_spec().map(Some);
|
||||
|
@ -61,7 +61,7 @@ impl Layout for FlowNode {
|
||||
pub enum FlowChild {
|
||||
/// Vertical spacing between other children.
|
||||
Spacing(Spacing),
|
||||
/// A node and how to align it in the flow.
|
||||
/// An arbitrary node.
|
||||
Node(PackedNode),
|
||||
}
|
||||
|
||||
|
@ -288,17 +288,17 @@ impl<'a> GridLayouter<'a> {
|
||||
for y in 0 .. self.rows.len() {
|
||||
if let Some(node) = self.cell(x, y) {
|
||||
let size = Size::new(available, self.regions.base.h);
|
||||
let mut regions =
|
||||
let mut pod =
|
||||
Regions::one(size, self.regions.base, Spec::splat(false));
|
||||
|
||||
// For linear rows, we can already resolve the correct
|
||||
// base, for auto it's already correct and for fr we could
|
||||
// only guess anyway.
|
||||
if let TrackSizing::Linear(v) = self.rows[y] {
|
||||
regions.base.h = v.resolve(self.regions.base.h);
|
||||
pod.base.h = v.resolve(self.regions.base.h);
|
||||
}
|
||||
|
||||
let frame = node.layout(ctx, ®ions).remove(0).item;
|
||||
let frame = node.layout(ctx, &pod).remove(0).item;
|
||||
resolved.set_max(frame.size.w);
|
||||
}
|
||||
}
|
||||
@ -376,17 +376,17 @@ impl<'a> GridLayouter<'a> {
|
||||
// Determine the size for each region of the row.
|
||||
for (x, &rcol) in self.rcols.iter().enumerate() {
|
||||
if let Some(node) = self.cell(x, y) {
|
||||
let mut regions = self.regions.clone();
|
||||
regions.mutate(|size| size.w = rcol);
|
||||
let mut pod = self.regions.clone();
|
||||
pod.mutate(|size| size.w = rcol);
|
||||
|
||||
// Set the horizontal base back to the parent region's base for
|
||||
// auto columns.
|
||||
if self.cols[x] == TrackSizing::Auto {
|
||||
regions.base.w = self.regions.base.w;
|
||||
pod.base.w = self.regions.base.w;
|
||||
}
|
||||
|
||||
let mut sizes =
|
||||
node.layout(ctx, ®ions).into_iter().map(|frame| frame.item.size.h);
|
||||
node.layout(ctx, &pod).into_iter().map(|frame| frame.item.size.h);
|
||||
|
||||
for (target, size) in resolved.iter_mut().zip(&mut sizes) {
|
||||
target.set_max(size);
|
||||
@ -475,8 +475,8 @@ impl<'a> GridLayouter<'a> {
|
||||
base.h = size.h;
|
||||
}
|
||||
|
||||
let regions = Regions::one(size, base, Spec::splat(true));
|
||||
let frame = node.layout(ctx, ®ions).remove(0);
|
||||
let pod = Regions::one(size, base, Spec::splat(true));
|
||||
let frame = node.layout(ctx, &pod).remove(0);
|
||||
output.push_frame(pos, frame.item);
|
||||
}
|
||||
|
||||
@ -501,8 +501,8 @@ impl<'a> GridLayouter<'a> {
|
||||
|
||||
// Prepare regions.
|
||||
let size = Size::new(self.used.w, resolved[0]);
|
||||
let mut regions = Regions::one(size, self.regions.base, Spec::splat(true));
|
||||
regions.backlog = resolved[1 ..]
|
||||
let mut pod = Regions::one(size, self.regions.base, Spec::splat(true));
|
||||
pod.backlog = resolved[1 ..]
|
||||
.iter()
|
||||
.map(|&h| Size::new(self.used.w, h))
|
||||
.collect::<Vec<_>>()
|
||||
@ -512,16 +512,16 @@ impl<'a> GridLayouter<'a> {
|
||||
let mut pos = Point::zero();
|
||||
for (x, &rcol) in self.rcols.iter().enumerate() {
|
||||
if let Some(node) = self.cell(x, y) {
|
||||
regions.mutate(|size| size.w = rcol);
|
||||
pod.mutate(|size| size.w = rcol);
|
||||
|
||||
// Set the horizontal base back to the parent region's base for
|
||||
// auto columns.
|
||||
if self.cols[x] == TrackSizing::Auto {
|
||||
regions.base.w = self.regions.base.w;
|
||||
pod.base.w = self.regions.base.w;
|
||||
}
|
||||
|
||||
// Push the layouted frames into the individual output frames.
|
||||
let frames = node.layout(ctx, ®ions);
|
||||
let frames = node.layout(ctx, &pod);
|
||||
for (output, frame) in outputs.iter_mut().zip(frames) {
|
||||
output.push_frame(pos, frame.item);
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
let right = args.named("right")?;
|
||||
let bottom = args.named("bottom")?;
|
||||
let body: Template = args.expect("body")?;
|
||||
|
||||
let padding = Sides::new(
|
||||
left.or(all).unwrap_or_default(),
|
||||
top.or(all).unwrap_or_default(),
|
||||
@ -17,17 +16,17 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
);
|
||||
|
||||
Ok(Value::Template(Template::from_inline(move |style| {
|
||||
PadNode { padding, child: body.pack(style) }
|
||||
body.pack(style).padded(padding)
|
||||
})))
|
||||
}
|
||||
|
||||
/// A node that adds padding to its child.
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct PadNode {
|
||||
/// The amount of padding.
|
||||
pub padding: Sides<Linear>,
|
||||
/// The child node whose sides to pad.
|
||||
pub child: PackedNode,
|
||||
/// The amount of padding.
|
||||
pub padding: Sides<Linear>,
|
||||
}
|
||||
|
||||
impl Layout for PadNode {
|
||||
@ -37,43 +36,23 @@ impl Layout for PadNode {
|
||||
regions: &Regions,
|
||||
) -> Vec<Constrained<Rc<Frame>>> {
|
||||
// Layout child into padded regions.
|
||||
let mut frames = self.child.layout(
|
||||
ctx,
|
||||
®ions.map(|size| size - self.padding.resolve(size).size()),
|
||||
);
|
||||
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())
|
||||
{
|
||||
fn solve_axis(length: Length, padding: Linear) -> Length {
|
||||
(length + padding.abs).safe_div(1.0 - padding.rel.get())
|
||||
}
|
||||
|
||||
// Solve for the size `padded` that satisfies (approximately):
|
||||
// `padded - padding.resolve(padded).size() == size`
|
||||
let padded = Size::new(
|
||||
solve_axis(frame.size.w, self.padding.left + self.padding.right),
|
||||
solve_axis(frame.size.h, self.padding.top + self.padding.bottom),
|
||||
);
|
||||
|
||||
// Apply the padding inversely such that the grown size padded
|
||||
// yields the frame's size.
|
||||
let padded = grow(frame.size, self.padding);
|
||||
let padding = self.padding.resolve(padded);
|
||||
let origin = Point::new(padding.left, padding.top);
|
||||
let offset = Point::new(padding.left, padding.top);
|
||||
|
||||
// Create a new larger frame and place the child's frame inside it.
|
||||
let empty = Frame::new(padded, frame.baseline + origin.y);
|
||||
let prev = std::mem::replace(frame, Rc::new(empty));
|
||||
let new = Rc::make_mut(frame);
|
||||
new.push_frame(origin, prev);
|
||||
|
||||
// Inflate min and max contraints by the padding.
|
||||
for spec in [&mut cts.min, &mut cts.max] {
|
||||
if let Some(x) = spec.x.as_mut() {
|
||||
*x += padding.size().w;
|
||||
}
|
||||
if let Some(y) = spec.y.as_mut() {
|
||||
*y += padding.size().h;
|
||||
}
|
||||
}
|
||||
// Grow the frame and translate everything in the frame inwards.
|
||||
let frame = Rc::make_mut(frame);
|
||||
frame.size = padded;
|
||||
frame.baseline += offset.y;
|
||||
frame.translate(offset);
|
||||
|
||||
// Set exact and base constraints if the child had them.
|
||||
cts.exact.x.and_set(Some(current.w));
|
||||
@ -89,8 +68,53 @@ impl Layout for PadNode {
|
||||
if self.padding.top.is_relative() || self.padding.bottom.is_relative() {
|
||||
cts.base.y = Some(base.h);
|
||||
}
|
||||
|
||||
// Inflate min and max contraints by the padding.
|
||||
for spec in [&mut cts.min, &mut cts.max] {
|
||||
if let Some(x) = spec.x.as_mut() {
|
||||
*x += padding.size().w;
|
||||
}
|
||||
if let Some(y) = spec.y.as_mut() {
|
||||
*y += padding.size().h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frames
|
||||
}
|
||||
}
|
||||
|
||||
/// Shrink a size by padding relative to the size itself.
|
||||
fn shrink(size: Size, padding: Sides<Linear>) -> Size {
|
||||
size - padding.resolve(size).size()
|
||||
}
|
||||
|
||||
/// Grow a size by padding relative to the grown size.
|
||||
/// This is the inverse operation to `shrink()`.
|
||||
///
|
||||
/// For the horizontal axis the derivation looks as follows.
|
||||
/// (Vertical axis is analogous.)
|
||||
///
|
||||
/// Let w be the grown target width,
|
||||
/// s be given width,
|
||||
/// l be the left padding,
|
||||
/// r be the right padding,
|
||||
/// p = l + r.
|
||||
///
|
||||
/// We want that: w - l.resolve(w) - r.resolve(w) = s
|
||||
///
|
||||
/// Thus: w - l.resolve(w) - r.resolve(w) = s
|
||||
/// <=> w - p.resolve(w) = s
|
||||
/// <=> w - p.rel * w - p.abs = s
|
||||
/// <=> (1 - p.rel) * w = s + p.abs
|
||||
/// <=> w = (s + p.abs) / (1 - p.rel)
|
||||
fn grow(size: Size, padding: Sides<Linear>) -> Size {
|
||||
fn solve_axis(length: Length, padding: Linear) -> Length {
|
||||
(length + padding.abs).safe_div(1.0 - padding.rel.get())
|
||||
}
|
||||
|
||||
Size::new(
|
||||
solve_axis(size.w, padding.left + padding.right),
|
||||
solve_axis(size.h, padding.top + padding.bottom),
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::f64::consts::SQRT_2;
|
||||
|
||||
use super::prelude::*;
|
||||
use super::PadNode;
|
||||
use crate::util::RcExt;
|
||||
|
||||
/// `rect`: A rectangle with optional content.
|
||||
@ -113,10 +112,8 @@ impl Layout for ShapeNode {
|
||||
if matches!(self.kind, ShapeKind::Circle | ShapeKind::Ellipse) {
|
||||
// Padding with this ratio ensures that a rectangular child fits
|
||||
// perfectly into a circle / an ellipse.
|
||||
storage = PadNode {
|
||||
padding: Sides::splat(Relative::new(0.5 - SQRT_2 / 4.0).into()),
|
||||
child: child.clone(),
|
||||
};
|
||||
let ratio = Relative::new(0.5 - SQRT_2 / 4.0);
|
||||
storage = child.clone().padded(Sides::splat(ratio.into()));
|
||||
node = &storage;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ impl Layout for StackNode {
|
||||
pub enum StackChild {
|
||||
/// Spacing between other nodes.
|
||||
Spacing(Spacing),
|
||||
/// Any block node and how to align it in the stack.
|
||||
/// An arbitrary node.
|
||||
Node(PackedNode),
|
||||
}
|
||||
|
||||
|
@ -30,14 +30,10 @@ impl Layout for MoveNode {
|
||||
for (Constrained { item: frame, .. }, (_, base)) in
|
||||
frames.iter_mut().zip(regions.iter())
|
||||
{
|
||||
let offset = Point::new(
|
||||
Rc::make_mut(frame).translate(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).elements {
|
||||
*point += offset;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
frames
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Loading…
x
Reference in New Issue
Block a user