From b9c0fd87d304942db315ca6fdbccddf233d045cd Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 14 Feb 2023 12:34:16 +0100 Subject: [PATCH] Fix grid sizing --- library/src/layout/flow.rs | 32 ++++++++--------- library/src/layout/grid.rs | 69 ++++++++++++++++++++---------------- library/src/layout/stack.rs | 20 +++++------ src/geom/fr.rs | 2 +- tests/ref/bugs/grid-3.png | Bin 0 -> 1844 bytes tests/typ/bugs/grid-3.typ | 8 +++++ 6 files changed, 71 insertions(+), 60 deletions(-) create mode 100644 tests/ref/bugs/grid-3.png create mode 100644 tests/typ/bugs/grid-3.typ diff --git a/library/src/layout/flow.rs b/library/src/layout/flow.rs index 00eeb5374..ee845f06b 100644 --- a/library/src/layout/flow.rs +++ b/library/src/layout/flow.rs @@ -70,9 +70,9 @@ struct FlowLayouter<'a> { regions: Regions<'a>, /// Whether the flow should expand to fill the region. expand: Axes, - /// The full size of `regions.size` that was available before we started + /// The intial size of `regions.size` that was available before we started /// subtracting. - full: Size, + initial: Size, /// Whether the last block was a paragraph. last_was_par: bool, /// Spacing and layouted blocks. @@ -98,7 +98,6 @@ impl<'a> FlowLayouter<'a> { /// Create a new flow layouter. fn new(mut regions: Regions<'a>) -> Self { let expand = regions.expand; - let full = regions.size; // Disable vertical expansion for children. regions.expand.y = false; @@ -106,7 +105,7 @@ impl<'a> FlowLayouter<'a> { Self { regions, expand, - full, + initial: regions.size, last_was_par: false, items: vec![], finished: vec![], @@ -117,7 +116,7 @@ impl<'a> FlowLayouter<'a> { fn layout_spacing(&mut self, node: VNode, styles: StyleChain) { self.layout_item(match node.amount { Spacing::Rel(v) => FlowItem::Absolute( - v.resolve(styles).relative_to(self.full.y), + v.resolve(styles).relative_to(self.initial.y), node.weakness > 0, ), Spacing::Fr(v) => FlowItem::Fractional(v), @@ -256,10 +255,10 @@ impl<'a> FlowLayouter<'a> { let mut fr = Fr::zero(); let mut used = Size::zero(); for item in &self.items { - match *item { - FlowItem::Absolute(v, _) => used.y += v, - FlowItem::Fractional(v) => fr += v, - FlowItem::Frame(ref frame, ..) => { + match item { + FlowItem::Absolute(v, _) => used.y += *v, + FlowItem::Fractional(v) => fr += *v, + FlowItem::Frame(frame, ..) => { let size = frame.size(); used.y += size.y; used.x.set_max(size.x); @@ -269,14 +268,10 @@ impl<'a> FlowLayouter<'a> { } // Determine the size of the flow in this region depending on whether - // the region expands. - let mut size = self.expand.select(self.full, used).min(self.full); - - // Account for fractional spacing in the size calculation. - let remaining = self.full.y - used.y; - if fr.get() > 0.0 && self.full.y.is_finite() { - used.y = self.full.y; - size.y = self.full.y; + // the region expands. Also account for fractional spacing. + let mut size = self.expand.select(self.initial, used).min(self.initial); + if fr.get() > 0.0 && self.initial.y.is_finite() { + size.y = self.initial.y; } let mut output = Frame::new(size); @@ -290,6 +285,7 @@ impl<'a> FlowLayouter<'a> { offset += v; } FlowItem::Fractional(v) => { + let remaining = self.initial.y - used.y; offset += v.share(fr, remaining); } FlowItem::Frame(frame, aligns, _) => { @@ -309,7 +305,7 @@ impl<'a> FlowLayouter<'a> { // Advance to the next region. self.finished.push(output); self.regions.next(); - self.full = self.regions.size; + self.initial = self.regions.size; } /// Finish layouting and return the resulting fragment. diff --git a/library/src/layout/grid.rs b/library/src/layout/grid.rs index c5bbe9e5e..626cb82dc 100644 --- a/library/src/layout/grid.rs +++ b/library/src/layout/grid.rs @@ -184,15 +184,14 @@ pub struct GridLayouter<'a, 'v> { styles: StyleChain<'a>, /// Resolved column sizes. rcols: Vec, + /// The sum of `rcols`. + width: Abs, /// Resolve row sizes, by region. rrows: Vec>, /// Rows in the current region. lrows: Vec, - /// The used-up size of the current region. The horizontal size is - /// determined once after columns are resolved and not touched again. - used: Size, - /// The sum of fractions in the current region. - fr: Fr, + /// The initial size of the current region before we started subtracting. + initial: Size, /// Frames for finished regions. finished: Vec, } @@ -306,10 +305,10 @@ impl<'a, 'v> GridLayouter<'a, 'v> { regions, styles, rcols, + width: Abs::zero(), rrows: vec![], lrows, - used: Size::zero(), - fr: Fr::zero(), + initial: regions.size, finished: vec![], } } @@ -328,10 +327,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { match self.rows[y] { Sizing::Auto => self.layout_auto_row(y)?, Sizing::Rel(v) => self.layout_relative_row(v, y)?, - Sizing::Fr(v) => { - self.lrows.push(Row::Fr(v, y)); - self.fr += v; - } + Sizing::Fr(v) => self.lrows.push(Row::Fr(v, y)), } } @@ -384,7 +380,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { } // Sum up the resolved column sizes once here. - self.used.x = self.rcols.iter().sum(); + self.width = self.rcols.iter().sum(); Ok(()) } @@ -523,13 +519,16 @@ impl<'a, 'v> GridLayouter<'a, 'v> { resolved.remove(0); } - // Expand all but the last region if the space is not - // eaten up by any fr rows. - if self.fr.is_zero() { - let len = resolved.len(); - for (region, target) in self.regions.iter().zip(&mut resolved[..len - 1]) { - target.set_max(region.y); - } + // Expand all but the last region. + // Skip the first region if the space is eaten up by an fr row. + let len = resolved.len(); + for (region, target) in self + .regions + .iter() + .zip(&mut resolved[..len - 1]) + .skip(self.lrows.iter().any(|row| matches!(row, Row::Fr(..))) as usize) + { + target.set_max(region.y); } // Layout into multiple regions. @@ -569,7 +568,7 @@ impl<'a, 'v> GridLayouter<'a, 'v> { /// Layout a row with fixed height and return its frame. fn layout_single_row(&mut self, height: Abs, y: usize) -> SourceResult { - let mut output = Frame::new(Size::new(self.used.x, height)); + let mut output = Frame::new(Size::new(self.width, height)); let mut pos = Point::zero(); for (x, &rcol) in self.rcols.iter().enumerate() { @@ -594,11 +593,11 @@ impl<'a, 'v> GridLayouter<'a, 'v> { // Prepare frames. let mut outputs: Vec<_> = heights .iter() - .map(|&h| Frame::new(Size::new(self.used.x, h))) + .map(|&h| Frame::new(Size::new(self.width, h))) .collect(); // Prepare regions. - let size = Size::new(self.used.x, heights[0]); + let size = Size::new(self.width, heights[0]); let mut pod = Regions::one(size, Axes::splat(true)); pod.full = self.regions.full; pod.backlog = &heights[1..]; @@ -625,17 +624,26 @@ impl<'a, 'v> GridLayouter<'a, 'v> { /// Push a row frame into the current region. fn push_row(&mut self, frame: Frame, y: usize) { self.regions.size.y -= frame.height(); - self.used.y += frame.height(); self.lrows.push(Row::Frame(frame, y)); } /// Finish rows for one region. fn finish_region(&mut self) -> SourceResult<()> { + // Determine the height of existing rows in the region. + let mut used = Abs::zero(); + let mut fr = Fr::zero(); + for row in &self.lrows { + match row { + Row::Frame(frame, _) => used += frame.height(), + Row::Fr(v, _) => fr += *v, + } + } + // Determine the size of the grid in this region, expanding fully if // there are fr rows. - let mut size = self.used; - if self.fr.get() > 0.0 && self.regions.full.is_finite() { - size.y = self.regions.full; + let mut size = Size::new(self.width, used).min(self.initial); + if fr.get() > 0.0 && self.initial.y.is_finite() { + size.y = self.initial.y; } // The frame for the region. @@ -648,8 +656,8 @@ impl<'a, 'v> GridLayouter<'a, 'v> { let (frame, y) = match row { Row::Frame(frame, y) => (frame, y), Row::Fr(v, y) => { - let remaining = self.regions.full - self.used.y; - let height = v.share(self.fr, remaining); + let remaining = self.regions.full - used; + let height = v.share(fr, remaining); (self.layout_single_row(height, y)?, y) } }; @@ -661,10 +669,9 @@ impl<'a, 'v> GridLayouter<'a, 'v> { } self.finished.push(output); - self.regions.next(); self.rrows.push(rrows); - self.used.y = Abs::zero(); - self.fr = Fr::zero(); + self.regions.next(); + self.initial = self.regions.size; Ok(()) } diff --git a/library/src/layout/stack.rs b/library/src/layout/stack.rs index 1452a8892..864b7b423 100644 --- a/library/src/layout/stack.rs +++ b/library/src/layout/stack.rs @@ -129,8 +129,8 @@ struct StackLayouter<'a> { styles: StyleChain<'a>, /// Whether the stack itself should expand to fill the region. expand: Axes, - /// The full size of the current region that was available at the start. - full: Size, + /// The initial size of the current region before we started subtracting. + initial: Size, /// The generic size used by the frames for the current region. used: Gen, /// The sum of fractions in the current region. @@ -154,13 +154,11 @@ enum StackItem { impl<'a> StackLayouter<'a> { /// Create a new stack layouter. - fn new(dir: Dir, regions: Regions<'a>, styles: StyleChain<'a>) -> Self { + fn new(dir: Dir, mut regions: Regions<'a>, styles: StyleChain<'a>) -> Self { let axis = dir.axis(); let expand = regions.expand; - let full = regions.size; // Disable expansion along the block axis for children. - let mut regions = regions.clone(); regions.expand.set(axis, false); Self { @@ -169,7 +167,7 @@ impl<'a> StackLayouter<'a> { regions, styles, expand, - full, + initial: regions.size, used: Gen::zero(), fr: Fr::zero(), items: vec![], @@ -251,11 +249,13 @@ impl<'a> StackLayouter<'a> { fn finish_region(&mut self) { // Determine the size of the stack in this region dependening on whether // the region expands. - let used = self.used.to_axes(self.axis); - let mut size = self.expand.select(self.full, used); + let mut size = self + .expand + .select(self.initial, self.used.to_axes(self.axis)) + .min(self.initial); // Expand fully if there are fr spacings. - let full = self.full.get(self.axis); + let full = self.initial.get(self.axis); let remaining = full - self.used.main; if self.fr.get() > 0.0 && full.is_finite() { self.used.main = full; @@ -303,7 +303,7 @@ impl<'a> StackLayouter<'a> { // Advance to the next region. self.regions.next(); - self.full = self.regions.size; + self.initial = self.regions.size; self.used = Gen::zero(); self.fr = Fr::zero(); self.finished.push(output); diff --git a/src/geom/fr.rs b/src/geom/fr.rs index a737c953f..c602634de 100644 --- a/src/geom/fr.rs +++ b/src/geom/fr.rs @@ -34,7 +34,7 @@ impl Fr { pub fn share(self, total: Self, remaining: Abs) -> Abs { let ratio = self / total; if ratio.is_finite() && remaining.is_finite() { - ratio * remaining + (ratio * remaining).max(Abs::zero()) } else { Abs::zero() } diff --git a/tests/ref/bugs/grid-3.png b/tests/ref/bugs/grid-3.png new file mode 100644 index 0000000000000000000000000000000000000000..c4569851bad5b8a7c414ee63e971dc4179a755e4 GIT binary patch literal 1844 zcmc(gTUZhX9K~s^0!@Xo)zngE6DLm0D?+6)OYxHCrA*DB5~H-t3#fQWU}Ihy7DXan zLh-VxS!&=^)GpI$ii(+vA|oSj^A_rcMy}rWef##fZx8?T@P9ck=lo8F?{POBEdwn8 z0HA|)cl84RHhucx9DpAlfa3nTXaHdAI@a|_K!S8mmJ%7L+|;oI^Ffpf$I486+ur)2 zvVuTMQ9Cwsf78-)yoj&OUYe%%<+(z8nn>V(@RQeX>}GqSLMe_*QXEls`AS_MeSe=5K0Hf zWZdli>iTynFbKMF?CO`HJvhpKt}wXd0x)f))9w1*jBbTf`$io+Bel0&^|23s&p*Si@Zk1`#Xs)Co=@uc9VwJZ4d$aHjj2{*k z#Hg1y57De+n(EOjpJ7|QH12E223N^D5M$l!iM_aXxR7nunD#1Zy|Tf9hr=}SS3z5@ z0UamFNEv=Ua(W!TUuk6%C}Gh?Dd0EAQw_G&{&_Kv>eQOfloovGRlT1^4lCK);o0B( z3T#3vvV-Vi+z?JN50$`Ib9FgBt-{c}6BSuPn!o5t;cWtH!R8D(s>p5loZi&a!dSD8 zFN_urKX_rpJc2yOZYs^{`O04Ep>c5gk`)Kls1xY0x!n@m^h17uGnuLBi=5M>wjN#( zJ$4mJo`h1Yw(>fy;ysPMGCWwBcC$z31UiF)_RYMx!)RhmSG9aun=aVo+1w5qIYV~C zlR~*UD?5d8q#yWUR}o( zsgU^lRGlA9Gff~$? z#Dl&VtR=n;jZphd-Q%ZeaM!UMe|fA|{8W5I{Hs+v*2$?ZeVxx?qJq6rguQt0MJl;~ zq-P0?id%c>ThTva*zzEP=RQS`vw$NI2y)J8s5h{ARPW8s;7cvb=A!67sS(7{($HFB zx5FP@0|TRjNnPbEiy97XK53=xChv0zfg=W46Qy4#;&W(F2Rez>SClPnhk} z_)ccDSYzxktL0(84FT0;ca#&3Qv}h?n|-Q?PFO@bn_BvGl5N`6znjay$mY)6pNV|P z1WVt?SPB}t4i3pq5FWwQNM2P{WFhC&Nr5!cH&;I-a7;D4n637YF%4ie#{Rrqronr8 zyNRpjt4ny>l9$ajN4Kd!UO)XEewbofZ_7rncHyq-6R=blWW3Pctp4R}N$f(jkG40( zaRd@v{;9FNZo;f?TpQ{e78dq?st@OV%QRrmWR_No!GSJq{JkdWWsh4%JR%Fth%R?3 z04w0O9mb-kW4GCKpquwi1US$3s(S+3sK|Kj*>0e-5WRC%*GwqgO)G_EPo{m=n8dg< z6!Cs!8_;Lu!Q}SKWNsg6-JRY$2r6vJ??rRgQNZh-Jd{H literal 0 HcmV?d00001 diff --git a/tests/typ/bugs/grid-3.typ b/tests/typ/bugs/grid-3.typ new file mode 100644 index 000000000..19317c503 --- /dev/null +++ b/tests/typ/bugs/grid-3.typ @@ -0,0 +1,8 @@ +// Ensure that the list does not jump to the third page. + +--- +#set page(height: 70pt) +#v(40pt) +The following: ++ A ++ B