Copyable regions

This commit is contained in:
Laurenz 2022-12-08 18:44:31 +01:00
parent 11c7ceb29e
commit e6857f810e
21 changed files with 80 additions and 70 deletions

View File

@ -81,7 +81,7 @@ impl<const L: ListKind> Layout for ListNode<L> {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut cells = vec![];
let mut number = NonZeroUsize::new(1).unwrap();

View File

@ -55,7 +55,7 @@ impl Layout for TableNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let fill = styles.get(Self::FILL);
let stroke = styles.get(Self::STROKE).map(PartialStroke::unwrap_or_default);

View File

@ -31,7 +31,7 @@ impl Layout for AlignNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
// The child only needs to expand along an axis if there's no alignment.
let mut pod = regions.clone();
@ -44,7 +44,7 @@ impl Layout for AlignNode {
}
// Layout the child.
let mut fragment = self.child.layout(vt, styles.chain(&map), &pod)?;
let mut fragment = self.child.layout(vt, styles.chain(&map), pod)?;
for (region, frame) in regions.iter().zip(&mut fragment) {
// Align in the target size. The target size depends on whether we
// should expand.

View File

@ -31,7 +31,7 @@ impl Layout for ColumnsNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
// Separating the infinite space into infinite columns does not make
// much sense.
@ -44,21 +44,23 @@ impl Layout for ColumnsNode {
let gutter = styles.get(Self::GUTTER).relative_to(regions.base.x);
let width = (regions.first.x - gutter * (columns - 1) as f64) / columns as f64;
let backlog: Vec<_> = std::iter::once(&regions.first.y)
.chain(regions.backlog)
.flat_map(|&height| std::iter::repeat(height).take(columns))
.skip(1)
.collect();
// Create the pod regions.
let pod = Regions {
first: Size::new(width, regions.first.y),
base: Size::new(width, regions.base.y),
backlog: std::iter::once(&regions.first.y)
.chain(regions.backlog.as_slice())
.flat_map(|&height| std::iter::repeat(height).take(columns))
.skip(1)
.collect(),
backlog: &backlog,
last: regions.last,
expand: Axes::new(true, regions.expand.y),
};
// Layout the children.
let mut frames = self.child.layout(vt, styles, &pod)?.into_iter();
let mut frames = self.child.layout(vt, styles, pod)?.into_iter();
let mut finished = vec![];
let dir = styles.get(TextNode::DIR);

View File

@ -25,7 +25,7 @@ impl Layout for BoxNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
// The "pod" is the region into which the child will be layouted.
let pod = {
@ -47,7 +47,7 @@ impl Layout for BoxNode {
};
// Layout the child.
let mut frame = self.child.layout(vt, styles, &pod)?.into_frame();
let mut frame = self.child.layout(vt, styles, pod)?.into_frame();
// Ensure frame size matches regions size if expansion is on.
let target = regions.expand.select(regions.first, frame.size());
@ -97,7 +97,7 @@ impl Layout for BlockNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
self.0.layout(vt, styles, regions)
}

View File

@ -22,7 +22,7 @@ impl Layout for FlowNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut layouter = FlowLayouter::new(regions, self.1);
@ -55,11 +55,11 @@ impl Debug for FlowNode {
}
/// Performs flow layout.
struct FlowLayouter {
struct FlowLayouter<'a> {
/// Whether this is a root page-level flow.
root: bool,
/// The regions to layout children into.
regions: Regions,
regions: Regions<'a>,
/// Whether the flow should expand to fill the region.
expand: Axes<bool>,
/// The full size of `regions.size` that was available before we started
@ -88,14 +88,13 @@ enum FlowItem {
Placed(Frame),
}
impl FlowLayouter {
impl<'a> FlowLayouter<'a> {
/// Create a new flow layouter.
fn new(regions: &Regions, root: bool) -> Self {
fn new(mut regions: Regions<'a>, root: bool) -> Self {
let expand = regions.expand;
let full = regions.first;
// Disable vertical expansion for children.
let mut regions = regions.clone();
regions.expand.y = false;
Self {
@ -166,7 +165,7 @@ impl FlowLayouter {
// aligned later.
if let Some(placed) = block.to::<PlaceNode>() {
if placed.out_of_flow() {
let frame = block.layout(vt, styles, &self.regions)?.into_frame();
let frame = block.layout(vt, styles, self.regions)?.into_frame();
self.layout_item(FlowItem::Placed(frame));
return Ok(());
}
@ -187,7 +186,7 @@ impl FlowLayouter {
// Layout the block itself.
let sticky = styles.get(BlockNode::STICKY);
let fragment = block.layout(vt, styles, &self.regions)?;
let fragment = block.layout(vt, styles, self.regions)?;
for frame in fragment {
self.layout_item(FlowItem::Frame(frame, aligns, sticky));
}

View File

@ -38,7 +38,7 @@ impl Layout for GridNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
// Prepare grid layout by unifying content and gutter tracks.
let layouter = GridLayouter::new(
@ -120,7 +120,7 @@ struct GridLayouter<'a, 'v> {
/// The row tracks including gutter tracks.
rows: Vec<TrackSizing>,
/// The regions to layout children into.
regions: Regions,
regions: Regions<'a>,
/// The inherited styles.
styles: StyleChain<'a>,
/// Resolved column sizes.
@ -156,7 +156,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
tracks: Axes<&[TrackSizing]>,
gutter: Axes<&[TrackSizing]>,
cells: &'a [Content],
regions: &Regions,
regions: Regions<'a>,
styles: StyleChain<'a>,
) -> Self {
let mut cols = vec![];
@ -318,7 +318,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
v.resolve(self.styles).relative_to(self.regions.base.y);
}
let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame();
let frame = cell.layout(self.vt, self.styles, pod)?.into_frame();
resolved.set_max(frame.width());
}
}
@ -395,7 +395,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
}
let mut sizes = cell
.layout(self.vt, self.styles, &pod)?
.layout(self.vt, self.styles, pod)?
.into_iter()
.map(|frame| frame.height());
@ -483,7 +483,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
.select(self.regions.base, size);
let pod = Regions::one(size, base, Axes::splat(true));
let frame = cell.layout(self.vt, self.styles, &pod)?.into_frame();
let frame = cell.layout(self.vt, self.styles, pod)?.into_frame();
output.push_frame(pos, frame);
}
@ -504,7 +504,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
// Prepare regions.
let size = Size::new(self.used.x, heights[0]);
let mut pod = Regions::one(size, self.regions.base, Axes::splat(true));
pod.backlog = heights[1..].to_vec();
pod.backlog = &heights[1..];
// Layout the row.
let mut pos = Point::zero();
@ -519,7 +519,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> {
}
// Push the layouted frames into the individual output frames.
let fragment = cell.layout(self.vt, self.styles, &pod)?;
let fragment = cell.layout(self.vt, self.styles, pod)?;
for (output, frame) in outputs.iter_mut().zip(fragment) {
output.push_frame(pos, frame);
}

View File

@ -16,7 +16,7 @@ impl Layout for HideNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut fragment = self.0.layout(vt, styles, regions)?;
for frame in &mut fragment {

View File

@ -90,7 +90,7 @@ pub trait Layout {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment>;
}
@ -99,7 +99,7 @@ impl Layout for Content {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
#[comemo::memoize]
fn cached(
@ -108,7 +108,7 @@ impl Layout for Content {
provider: TrackedMut<StabilityProvider>,
introspector: Tracked<Introspector>,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut vt = Vt { world, provider, introspector };
let scratch = Scratch::default();
@ -137,14 +137,14 @@ impl Layout for Content {
pub trait Inline: Layout {}
/// A sequence of regions to layout into.
#[derive(Debug, Clone, Hash)]
pub struct Regions {
#[derive(Debug, Copy, Clone, Hash)]
pub struct Regions<'a> {
/// The (remaining) size of the first region.
pub first: Size,
/// The base size for relative sizing.
pub base: Size,
/// The height of followup regions. The width is the same for all regions.
pub backlog: Vec<Abs>,
pub backlog: &'a [Abs],
/// The height of the final region that is repeated once the backlog is
/// drained. The width is the same for all regions.
pub last: Option<Abs>,
@ -153,13 +153,13 @@ pub struct Regions {
pub expand: Axes<bool>,
}
impl Regions {
impl<'a> Regions<'a> {
/// Create a new region sequence with exactly one region.
pub fn one(size: Size, base: Size, expand: Axes<bool>) -> Self {
Self {
first: size,
base,
backlog: vec![],
backlog: &[],
last: None,
expand,
}
@ -170,7 +170,7 @@ impl Regions {
Self {
first: size,
base,
backlog: vec![],
backlog: &[],
last: Some(size.y),
expand,
}
@ -180,15 +180,17 @@ impl Regions {
///
/// Note that since all regions must have the same width, the width returned
/// by `f` is ignored for the backlog and the final region.
pub fn map<F>(&self, mut f: F) -> Self
pub fn map<'v, F>(&self, backlog: &'v mut Vec<Abs>, mut f: F) -> Regions<'v>
where
F: FnMut(Size) -> Size,
{
let x = self.first.x;
Self {
backlog.clear();
backlog.extend(self.backlog.iter().map(|&y| f(Size::new(x, y)).y));
Regions {
first: f(self.first),
base: f(self.base),
backlog: self.backlog.iter().map(|&y| f(Size::new(x, y)).y).collect(),
backlog,
last: self.last.map(|y| f(Size::new(x, y)).y),
expand: self.expand,
}
@ -208,8 +210,13 @@ impl Regions {
/// Advance to the next region if there is any.
pub fn next(&mut self) {
if let Some(height) = (!self.backlog.is_empty())
.then(|| self.backlog.remove(0))
if let Some(height) = self
.backlog
.split_first()
.map(|(first, tail)| {
self.backlog = tail;
*first
})
.or(self.last)
{
self.first.y = height;

View File

@ -30,12 +30,14 @@ impl Layout for PadNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut backlog = vec![];
// Layout child into padded regions.
let padding = self.padding.resolve(styles);
let pod = regions.map(|size| shrink(size, padding));
let mut fragment = self.child.layout(vt, styles, &pod)?;
let pod = regions.map(&mut backlog, |size| shrink(size, padding));
let mut fragment = self.child.layout(vt, styles, pod)?;
for frame in &mut fragment {
// Apply the padding inversely such that the grown size padded

View File

@ -97,7 +97,7 @@ impl PageNode {
// Layout the child.
let regions = Regions::repeat(size, size, size.map(Abs::is_finite));
let mut fragment = child.layout(vt, styles, &regions)?;
let mut fragment = child.layout(vt, styles, regions)?;
let header = styles.get(Self::HEADER);
let footer = styles.get(Self::FOOTER);
@ -118,7 +118,7 @@ impl PageNode {
] {
if let Some(content) = marginal.resolve(vt, page)? {
let pod = Regions::one(area, area, Axes::splat(true));
let sub = content.layout(vt, styles, &pod)?.into_frame();
let sub = content.layout(vt, styles, pod)?.into_frame();
if std::ptr::eq(marginal, background) {
frame.prepend_frame(pos, sub);
} else {

View File

@ -538,7 +538,7 @@ fn prepare<'a>(
} else {
let size = Size::new(width, base.y);
let pod = Regions::one(size, base, Axes::splat(false));
let mut frame = inline.layout(vt, styles, &pod)?.into_frame();
let mut frame = inline.layout(vt, styles, pod)?.into_frame();
frame.translate(Point::with_y(styles.get(TextNode::BASELINE)));
items.push(Item::Frame(frame));
}
@ -1125,7 +1125,7 @@ fn commit(
let fill = Fr::one().share(fr, remaining);
let size = Size::new(fill, base.y);
let pod = Regions::one(size, base, Axes::new(false, false));
let frame = repeat.layout(vt, *styles, &pod)?.into_frame();
let frame = repeat.layout(vt, *styles, pod)?.into_frame();
let width = frame.width();
let count = (fill / width).floor();
let remaining = fill % width;

View File

@ -21,7 +21,7 @@ impl Layout for PlaceNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let out_of_flow = self.out_of_flow();
@ -33,7 +33,7 @@ impl Layout for PlaceNode {
Regions::one(regions.base, regions.base, expand)
};
let mut frame = self.0.layout(vt, styles, &pod)?.into_frame();
let mut frame = self.0.layout(vt, styles, pod)?.into_frame();
// If expansion is off, zero all sizes so that we don't take up any
// space in our parent. Otherwise, respect the expand settings.

View File

@ -16,7 +16,7 @@ impl Layout for RepeatNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
self.0.layout(vt, styles, regions)
}

View File

@ -31,7 +31,7 @@ impl Layout for StackNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut layouter = StackLayouter::new(self.dir, regions, styles);
@ -94,7 +94,7 @@ struct StackLayouter<'a> {
/// The axis of the stacking direction.
axis: Axis,
/// The regions to layout children into.
regions: Regions,
regions: Regions<'a>,
/// The inherited styles.
styles: StyleChain<'a>,
/// Whether the stack itself should expand to fill the region.
@ -124,7 +124,7 @@ enum StackItem {
impl<'a> StackLayouter<'a> {
/// Create a new stack layouter.
fn new(dir: Dir, regions: &Regions, styles: StyleChain<'a>) -> Self {
fn new(dir: Dir, regions: Regions<'a>, styles: StyleChain<'a>) -> Self {
let axis = dir.axis();
let expand = regions.expand;
let full = regions.first;
@ -195,7 +195,7 @@ impl<'a> StackLayouter<'a> {
self.dir.start().into()
});
let fragment = block.layout(vt, styles, &self.regions)?;
let fragment = block.layout(vt, styles, self.regions)?;
let len = fragment.len();
for (i, frame) in fragment.into_iter().enumerate() {
// Grow our size, shrink the region and save the frame for later.

View File

@ -29,7 +29,7 @@ impl Layout for MoveNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut fragment = self.child.layout(vt, styles, regions)?;
for frame in &mut fragment {
@ -87,7 +87,7 @@ impl<const T: TransformKind> Layout for TransformNode<T> {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut fragment = self.child.layout(vt, styles, regions)?;
for frame in &mut fragment {

View File

@ -57,7 +57,7 @@ impl Layout for MathNode {
&self,
vt: &mut Vt,
styles: StyleChain,
_: &Regions,
_: Regions,
) -> SourceResult<Fragment> {
let mut t = Texifier::new(styles);
self.texify(&mut t)?;

View File

@ -114,7 +114,7 @@ impl Layout for FillNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut fragment = self.child.layout(vt, styles, regions)?;
for frame in &mut fragment {
@ -142,7 +142,7 @@ impl Layout for StrokeNode {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut fragment = self.child.layout(vt, styles, regions)?;
for frame in &mut fragment {

View File

@ -41,14 +41,14 @@ impl Layout for ImageNode {
&self,
_: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let pxw = self.0.width() as f64;
let pxh = self.0.height() as f64;
let px_ratio = pxw / pxh;
// Find out whether the image is wider or taller than the target size.
let &Regions { first, expand, .. } = regions;
let Regions { first, expand, .. } = regions;
let region_ratio = first.x / first.y;
let wide = px_ratio > region_ratio;

View File

@ -41,7 +41,7 @@ impl Layout for LineNode {
&self,
_: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let stroke = styles.get(Self::STROKE).unwrap_or_default();

View File

@ -76,7 +76,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
&self,
vt: &mut Vt,
styles: StyleChain,
regions: &Regions,
regions: Regions,
) -> SourceResult<Fragment> {
let mut frame;
if let Some(child) = &self.0 {
@ -89,7 +89,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
let child = child.clone().padded(inset.map(|side| side.map(Length::from)));
let mut pod = Regions::one(regions.first, regions.base, regions.expand);
frame = child.layout(vt, styles, &pod)?.into_frame();
frame = child.layout(vt, styles, pod)?.into_frame();
// Relayout with full expansion into square region to make sure
// the result is really a square or circle.
@ -105,7 +105,7 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> {
pod.first = Size::splat(length);
pod.expand = Axes::splat(true);
frame = child.layout(vt, styles, &pod)?.into_frame();
frame = child.layout(vt, styles, pod)?.into_frame();
}
} else {
// The default size that a shape takes on if it has no child and