diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 147e8c9d4..ebdfba0ef 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -200,6 +200,7 @@ impl PackedNode { } impl Layout for PackedNode { + #[track_caller] fn layout( &self, ctx: &mut LayoutContext, @@ -219,8 +220,12 @@ impl Layout for PackedNode { state.finish() }; + // This is not written with `unwrap_or_else`, because then the + // #[track_caller] annotation doesn't work. #[cfg(feature = "layout-cache")] - ctx.layout_cache.get(hash, regions).unwrap_or_else(|| { + if let Some(frames) = ctx.layout_cache.get(hash, regions) { + frames + } else { ctx.level += 1; let frames = self.node.layout(ctx, regions, styles); ctx.level -= 1; @@ -240,7 +245,7 @@ impl Layout for PackedNode { ctx.layout_cache.insert(hash, entry); frames - }) + } } fn pack(self) -> PackedNode { diff --git a/src/layout/regions.rs b/src/layout/regions.rs index 66e6da175..3f8b6d259 100644 --- a/src/layout/regions.rs +++ b/src/layout/regions.rs @@ -7,10 +7,11 @@ pub struct Regions { pub current: Size, /// The base size for relative sizing. pub base: Size, - /// An iterator of followup regions. - pub backlog: std::vec::IntoIter, - /// The final region that is repeated once the backlog is drained. - pub last: Option, + /// The height of followup regions. The width is the same for all regions. + pub backlog: std::vec::IntoIter, + /// 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, /// Whether nodes should expand to fill the regions instead of shrinking to /// fit the content. pub expand: Spec, @@ -34,19 +35,33 @@ impl Regions { current: size, base, backlog: vec![].into_iter(), - last: Some(size), + last: Some(size.y), expand, } } /// Create new regions where all sizes are mapped with `f`. + /// + /// 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(&self, mut f: F) -> Self where F: FnMut(Size) -> Size, { - let mut regions = self.clone(); - regions.mutate(|s| *s = f(*s)); - regions + let x = self.current.x; + Self { + current: f(self.current), + base: f(self.base), + backlog: self + .backlog + .as_slice() + .iter() + .map(|&y| f(Size::new(x, y)).y) + .collect::>() + .into_iter(), + last: self.last.map(|y| f(Size::new(x, y)).y), + expand: self.expand, + } } /// Whether the current region is full and a region break is called for. @@ -58,14 +73,15 @@ impl Regions { /// /// If this is true, calling `next()` will have no effect. pub fn in_last(&self) -> bool { - self.backlog.len() == 0 && self.last.map_or(true, |size| self.current == size) + self.backlog.len() == 0 + && self.last.map_or(true, |height| self.current.y == height) } /// Advance to the next region if there is any. pub fn next(&mut self) { - if let Some(size) = self.backlog.next().or(self.last) { - self.current = size; - self.base = size; + if let Some(height) = self.backlog.next().or(self.last) { + self.current.y = height; + self.base.y = height; } } @@ -76,17 +92,11 @@ impl Regions { let first = std::iter::once((self.current, self.base)); let backlog = self.backlog.as_slice().iter(); let last = self.last.iter().cycle(); - first.chain(backlog.chain(last).map(|&s| (s, s))) - } - - /// Mutate all contained sizes in place. - pub fn mutate(&mut self, mut f: F) - where - F: FnMut(&mut Size), - { - f(&mut self.current); - f(&mut self.base); - self.last.as_mut().map(|x| f(x)); - self.backlog.as_mut_slice().iter_mut().for_each(f); + first.chain(backlog.chain(last).map(|&height| { + ( + Size::new(self.current.x, height), + Size::new(self.base.x, height), + ) + })) } } diff --git a/src/library/columns.rs b/src/library/columns.rs index 149da755b..5ea76973e 100644 --- a/src/library/columns.rs +++ b/src/library/columns.rs @@ -38,63 +38,40 @@ impl Layout for ColumnsNode { regions: &Regions, styles: StyleChain, ) -> Vec>> { - let columns = self.columns.get(); - // Separating the infinite space into infinite columns does not make - // much sense. Note that this line assumes that no infinitely wide - // region will follow if the first region's width is finite. + // much sense. if regions.current.x.is_infinite() { return self.child.layout(ctx, regions, styles); } - // Gutter width for each region. (Can be different because the relative - // component is calculated seperately for each region.) - let mut gutters = vec![]; + // Determine the width of the gutter and each column. + let columns = self.columns.get(); + let gutter = styles.get(Self::GUTTER).resolve(regions.base.x); + let width = (regions.current.x - gutter * (columns - 1) as f64) / columns as f64; - // Sizes of all columns resulting from `region.current`, - // `region.backlog` and `regions.last`. - let mut sizes = vec![]; + // Create the pod regions. + let pod = Regions { + current: Size::new(width, regions.current.y), + base: Size::new(width, regions.base.y), + backlog: std::iter::once(®ions.current.y) + .chain(regions.backlog.as_slice()) + .flat_map(|&height| std::iter::repeat(height).take(columns)) + .skip(1) + .collect::>() + .into_iter(), + last: regions.last, + expand: Spec::new(true, regions.expand.y), + }; - for (current, base) in regions - .iter() - .take(1 + regions.backlog.len() + regions.last.iter().len()) - { - let gutter = styles.get(Self::GUTTER).resolve(base.x); - let width = (current.x - gutter * (columns - 1) as f64) / columns as f64; - let size = Size::new(width, current.y); - gutters.push(gutter); - sizes.extend(std::iter::repeat(size).take(columns)); - } - - let first = sizes.remove(0); - let mut pod = Regions::one( - first, - Size::new(first.x, regions.base.y), - Spec::new(true, regions.expand.y), - ); - - // Retrieve elements for the last region from the vectors. - let last_gutter = regions.last.map(|_| { - let gutter = gutters.pop().unwrap(); - let size = sizes.drain(sizes.len() - columns ..).next().unwrap(); - pod.last = Some(size); - gutter - }); - - pod.backlog = sizes.into_iter(); - - let mut finished = vec![]; + // Layout the children. let mut frames = self.child.layout(ctx, &pod, styles).into_iter(); let dir = styles.get(ParNode::DIR); let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize; + let mut finished = vec![]; // Stitch together the columns for each region. - for ((current, base), gutter) in regions - .iter() - .take(total_regions) - .zip(gutters.into_iter().chain(last_gutter.into_iter().cycle())) - { + for (current, base) in regions.iter().take(total_regions) { // The height should be the parent height if the node shall expand. // Otherwise its the maximum column height for the frame. In that // case, the frame is first created with zero height and then diff --git a/src/library/grid.rs b/src/library/grid.rs index c49ac84f5..6a9b790a2 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -386,9 +386,10 @@ 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) { - // All widths should be `rcol` except the base for auto columns. let mut pod = self.regions.clone(); - pod.mutate(|size| size.x = rcol); + pod.current.x = rcol; + + // All widths should be `rcol` except the base for auto columns. if self.cols[x] == TrackSizing::Auto { pod.base.x = self.regions.base.x; } @@ -513,18 +514,15 @@ impl<'a> GridLayouter<'a> { // Prepare regions. let size = Size::new(self.used.x, heights[0]); let mut pod = Regions::one(size, self.regions.base, Spec::splat(true)); - pod.backlog = heights[1 ..] - .iter() - .map(|&h| Size::new(self.used.x, h)) - .collect::>() - .into_iter(); + pod.backlog = heights[1 ..].to_vec().into_iter(); // Layout the row. let mut pos = Point::zero(); for (x, &rcol) in self.rcols.iter().enumerate() { if let Some(node) = self.cell(x, y) { + pod.current.x = rcol; + // All widths should be `rcol` except the base for auto columns. - pod.mutate(|size| size.x = rcol); if self.cols[x] == TrackSizing::Auto { pod.base.x = self.regions.base.x; }