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::geom::{Align, Dir, GenAxis, Length, Linear, Sides, Size};
|
||||||
use crate::layout::{Layout, PackedNode};
|
use crate::layout::{Layout, PackedNode};
|
||||||
use crate::library::{
|
use crate::library::{
|
||||||
Decoration, DocumentNode, FlowChild, FlowNode, PadNode, PageNode, ParChild, ParNode,
|
Decoration, DocumentNode, FlowChild, FlowNode, PageNode, ParChild, ParNode,
|
||||||
PlacedNode, Spacing,
|
PlacedNode, Spacing,
|
||||||
};
|
};
|
||||||
use crate::style::Style;
|
use crate::style::Style;
|
||||||
@ -400,7 +400,7 @@ impl PageBuilder {
|
|||||||
let Self { size, padding, hard } = self;
|
let Self { size, padding, hard } = self;
|
||||||
(!child.children.is_empty() || (keep && hard)).then(|| PageNode {
|
(!child.children.is_empty() || (keep && hard)).then(|| PageNode {
|
||||||
size,
|
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.
|
/// An iterator over all non-frame elements in this and nested frames.
|
||||||
pub fn elements(&self) -> Elements {
|
pub fn elements(&self) -> Elements {
|
||||||
Elements { stack: vec![(0, Point::zero(), self)] }
|
Elements { stack: vec![(0, Point::zero(), self)] }
|
||||||
|
@ -23,11 +23,6 @@ impl<T> Spec<T> {
|
|||||||
Self { x: v.clone(), y: v }
|
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`.
|
/// Maps the individual fields with `f`.
|
||||||
pub fn map<F, U>(self, mut f: F) -> Spec<U>
|
pub fn map<F, U>(self, mut f: F) -> Spec<U>
|
||||||
where
|
where
|
||||||
|
@ -17,9 +17,9 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use crate::font::FontStore;
|
use crate::font::FontStore;
|
||||||
use crate::frame::Frame;
|
use crate::frame::Frame;
|
||||||
use crate::geom::{Align, Linear, Spec};
|
use crate::geom::{Align, Linear, Sides, Spec};
|
||||||
use crate::image::ImageStore;
|
use crate::image::ImageStore;
|
||||||
use crate::library::{AlignNode, DocumentNode, MoveNode, SizedNode};
|
use crate::library::{AlignNode, DocumentNode, MoveNode, PadNode, SizedNode};
|
||||||
use crate::Context;
|
use crate::Context;
|
||||||
|
|
||||||
/// Layout a document node into a collection of frames.
|
/// Layout a document node into a collection of frames.
|
||||||
@ -129,6 +129,19 @@ impl PackedNode {
|
|||||||
self
|
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 {
|
impl Layout for PackedNode {
|
||||||
|
@ -49,7 +49,6 @@ impl Layout for AlignNode {
|
|||||||
pod.expand.y &= self.aligns.y.is_none();
|
pod.expand.y &= self.aligns.y.is_none();
|
||||||
|
|
||||||
let mut frames = self.child.layout(ctx, &pod);
|
let mut frames = self.child.layout(ctx, &pod);
|
||||||
|
|
||||||
for (Constrained { item: frame, cts }, (current, _)) in
|
for (Constrained { item: frame, cts }, (current, _)) in
|
||||||
frames.iter_mut().zip(regions.iter())
|
frames.iter_mut().zip(regions.iter())
|
||||||
{
|
{
|
||||||
@ -67,10 +66,7 @@ impl Layout for AlignNode {
|
|||||||
let frame = Rc::make_mut(frame);
|
let frame = Rc::make_mut(frame);
|
||||||
frame.size = canvas;
|
frame.size = canvas;
|
||||||
frame.baseline += offset.y;
|
frame.baseline += offset.y;
|
||||||
|
frame.translate(offset);
|
||||||
for (point, _) in &mut frame.elements {
|
|
||||||
*point += offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
cts.expand = regions.expand;
|
cts.expand = regions.expand;
|
||||||
cts.exact = current.to_spec().map(Some);
|
cts.exact = current.to_spec().map(Some);
|
||||||
|
@ -61,7 +61,7 @@ impl Layout for FlowNode {
|
|||||||
pub enum FlowChild {
|
pub enum FlowChild {
|
||||||
/// Vertical spacing between other children.
|
/// Vertical spacing between other children.
|
||||||
Spacing(Spacing),
|
Spacing(Spacing),
|
||||||
/// A node and how to align it in the flow.
|
/// An arbitrary node.
|
||||||
Node(PackedNode),
|
Node(PackedNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,17 +288,17 @@ impl<'a> GridLayouter<'a> {
|
|||||||
for y in 0 .. self.rows.len() {
|
for y in 0 .. self.rows.len() {
|
||||||
if let Some(node) = self.cell(x, y) {
|
if let Some(node) = self.cell(x, y) {
|
||||||
let size = Size::new(available, self.regions.base.h);
|
let size = Size::new(available, self.regions.base.h);
|
||||||
let mut regions =
|
let mut pod =
|
||||||
Regions::one(size, self.regions.base, Spec::splat(false));
|
Regions::one(size, self.regions.base, Spec::splat(false));
|
||||||
|
|
||||||
// For linear rows, we can already resolve the correct
|
// For linear rows, we can already resolve the correct
|
||||||
// base, for auto it's already correct and for fr we could
|
// base, for auto it's already correct and for fr we could
|
||||||
// only guess anyway.
|
// only guess anyway.
|
||||||
if let TrackSizing::Linear(v) = self.rows[y] {
|
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);
|
resolved.set_max(frame.size.w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,17 +376,17 @@ impl<'a> GridLayouter<'a> {
|
|||||||
// Determine the size for each region of the row.
|
// Determine the size for each region of the row.
|
||||||
for (x, &rcol) in self.rcols.iter().enumerate() {
|
for (x, &rcol) in self.rcols.iter().enumerate() {
|
||||||
if let Some(node) = self.cell(x, y) {
|
if let Some(node) = self.cell(x, y) {
|
||||||
let mut regions = self.regions.clone();
|
let mut pod = self.regions.clone();
|
||||||
regions.mutate(|size| size.w = rcol);
|
pod.mutate(|size| size.w = rcol);
|
||||||
|
|
||||||
// Set the horizontal base back to the parent region's base for
|
// Set the horizontal base back to the parent region's base for
|
||||||
// auto columns.
|
// auto columns.
|
||||||
if self.cols[x] == TrackSizing::Auto {
|
if self.cols[x] == TrackSizing::Auto {
|
||||||
regions.base.w = self.regions.base.w;
|
pod.base.w = self.regions.base.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sizes =
|
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) {
|
for (target, size) in resolved.iter_mut().zip(&mut sizes) {
|
||||||
target.set_max(size);
|
target.set_max(size);
|
||||||
@ -475,8 +475,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
base.h = size.h;
|
base.h = size.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
let regions = Regions::one(size, base, Spec::splat(true));
|
let pod = Regions::one(size, base, Spec::splat(true));
|
||||||
let frame = node.layout(ctx, ®ions).remove(0);
|
let frame = node.layout(ctx, &pod).remove(0);
|
||||||
output.push_frame(pos, frame.item);
|
output.push_frame(pos, frame.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,8 +501,8 @@ impl<'a> GridLayouter<'a> {
|
|||||||
|
|
||||||
// Prepare regions.
|
// Prepare regions.
|
||||||
let size = Size::new(self.used.w, resolved[0]);
|
let size = Size::new(self.used.w, resolved[0]);
|
||||||
let mut regions = Regions::one(size, self.regions.base, Spec::splat(true));
|
let mut pod = Regions::one(size, self.regions.base, Spec::splat(true));
|
||||||
regions.backlog = resolved[1 ..]
|
pod.backlog = resolved[1 ..]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&h| Size::new(self.used.w, h))
|
.map(|&h| Size::new(self.used.w, h))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@ -512,16 +512,16 @@ impl<'a> GridLayouter<'a> {
|
|||||||
let mut pos = Point::zero();
|
let mut pos = Point::zero();
|
||||||
for (x, &rcol) in self.rcols.iter().enumerate() {
|
for (x, &rcol) in self.rcols.iter().enumerate() {
|
||||||
if let Some(node) = self.cell(x, y) {
|
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
|
// Set the horizontal base back to the parent region's base for
|
||||||
// auto columns.
|
// auto columns.
|
||||||
if self.cols[x] == TrackSizing::Auto {
|
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.
|
// 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) {
|
for (output, frame) in outputs.iter_mut().zip(frames) {
|
||||||
output.push_frame(pos, frame.item);
|
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 right = args.named("right")?;
|
||||||
let bottom = args.named("bottom")?;
|
let bottom = args.named("bottom")?;
|
||||||
let body: Template = args.expect("body")?;
|
let body: Template = args.expect("body")?;
|
||||||
|
|
||||||
let padding = Sides::new(
|
let padding = Sides::new(
|
||||||
left.or(all).unwrap_or_default(),
|
left.or(all).unwrap_or_default(),
|
||||||
top.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| {
|
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.
|
/// A node that adds padding to its child.
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
pub struct PadNode {
|
pub struct PadNode {
|
||||||
/// The amount of padding.
|
|
||||||
pub padding: Sides<Linear>,
|
|
||||||
/// The child node whose sides to pad.
|
/// The child node whose sides to pad.
|
||||||
pub child: PackedNode,
|
pub child: PackedNode,
|
||||||
|
/// The amount of padding.
|
||||||
|
pub padding: Sides<Linear>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for PadNode {
|
impl Layout for PadNode {
|
||||||
@ -37,43 +36,23 @@ impl Layout for PadNode {
|
|||||||
regions: &Regions,
|
regions: &Regions,
|
||||||
) -> Vec<Constrained<Rc<Frame>>> {
|
) -> Vec<Constrained<Rc<Frame>>> {
|
||||||
// Layout child into padded regions.
|
// Layout child into padded regions.
|
||||||
let mut frames = self.child.layout(
|
let pod = regions.map(|size| shrink(size, self.padding));
|
||||||
ctx,
|
let mut frames = self.child.layout(ctx, &pod);
|
||||||
®ions.map(|size| size - self.padding.resolve(size).size()),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (Constrained { item: frame, cts }, (current, base)) in
|
for (Constrained { item: frame, cts }, (current, base)) in
|
||||||
frames.iter_mut().zip(regions.iter())
|
frames.iter_mut().zip(regions.iter())
|
||||||
{
|
{
|
||||||
fn solve_axis(length: Length, padding: Linear) -> Length {
|
// Apply the padding inversely such that the grown size padded
|
||||||
(length + padding.abs).safe_div(1.0 - padding.rel.get())
|
// yields the frame's size.
|
||||||
}
|
let padded = grow(frame.size, self.padding);
|
||||||
|
|
||||||
// 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),
|
|
||||||
);
|
|
||||||
|
|
||||||
let padding = self.padding.resolve(padded);
|
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.
|
// Grow the frame and translate everything in the frame inwards.
|
||||||
let empty = Frame::new(padded, frame.baseline + origin.y);
|
let frame = Rc::make_mut(frame);
|
||||||
let prev = std::mem::replace(frame, Rc::new(empty));
|
frame.size = padded;
|
||||||
let new = Rc::make_mut(frame);
|
frame.baseline += offset.y;
|
||||||
new.push_frame(origin, prev);
|
frame.translate(offset);
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set exact and base constraints if the child had them.
|
// Set exact and base constraints if the child had them.
|
||||||
cts.exact.x.and_set(Some(current.w));
|
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() {
|
if self.padding.top.is_relative() || self.padding.bottom.is_relative() {
|
||||||
cts.base.y = Some(base.h);
|
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
|
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 std::f64::consts::SQRT_2;
|
||||||
|
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use super::PadNode;
|
|
||||||
use crate::util::RcExt;
|
use crate::util::RcExt;
|
||||||
|
|
||||||
/// `rect`: A rectangle with optional content.
|
/// `rect`: A rectangle with optional content.
|
||||||
@ -113,10 +112,8 @@ impl Layout for ShapeNode {
|
|||||||
if matches!(self.kind, ShapeKind::Circle | ShapeKind::Ellipse) {
|
if matches!(self.kind, ShapeKind::Circle | ShapeKind::Ellipse) {
|
||||||
// Padding with this ratio ensures that a rectangular child fits
|
// Padding with this ratio ensures that a rectangular child fits
|
||||||
// perfectly into a circle / an ellipse.
|
// perfectly into a circle / an ellipse.
|
||||||
storage = PadNode {
|
let ratio = Relative::new(0.5 - SQRT_2 / 4.0);
|
||||||
padding: Sides::splat(Relative::new(0.5 - SQRT_2 / 4.0).into()),
|
storage = child.clone().padded(Sides::splat(ratio.into()));
|
||||||
child: child.clone(),
|
|
||||||
};
|
|
||||||
node = &storage;
|
node = &storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ impl Layout for StackNode {
|
|||||||
pub enum StackChild {
|
pub enum StackChild {
|
||||||
/// Spacing between other nodes.
|
/// Spacing between other nodes.
|
||||||
Spacing(Spacing),
|
Spacing(Spacing),
|
||||||
/// Any block node and how to align it in the stack.
|
/// An arbitrary node.
|
||||||
Node(PackedNode),
|
Node(PackedNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,14 +30,10 @@ impl Layout for MoveNode {
|
|||||||
for (Constrained { item: frame, .. }, (_, base)) in
|
for (Constrained { item: frame, .. }, (_, base)) in
|
||||||
frames.iter_mut().zip(regions.iter())
|
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.x.map(|x| x.resolve(base.w)).unwrap_or_default(),
|
||||||
self.offset.y.map(|y| y.resolve(base.h)).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
|
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