From decb4fd9b98968dbaa89451ab6620b33819260a5 Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Sun, 3 Mar 2024 16:32:27 -0300 Subject: Merging cells: Rowspans [More Flexible Tables Pt.3b] (#3501) --- tests/ref/bugs/grid-4.png | Bin 0 -> 1117 bytes tests/ref/layout/grid-rowspan-basic.png | Bin 0 -> 94597 bytes tests/ref/layout/grid-rowspan-split-1.png | Bin 0 -> 30406 bytes tests/ref/layout/grid-rowspan-split-2.png | Bin 0 -> 11282 bytes tests/ref/layout/grid-rowspan-split-3.png | Bin 0 -> 99690 bytes tests/ref/layout/grid-rtl.png | Bin 44946 -> 72464 bytes tests/ref/layout/grid-stroke.png | Bin 48899 -> 55633 bytes tests/typ/bugs/grid-4.typ | 17 +++ tests/typ/layout/grid-colspan.typ | 2 +- tests/typ/layout/grid-positioning.typ | 8 ++ tests/typ/layout/grid-rowspan-basic.typ | 211 ++++++++++++++++++++++++++++++ tests/typ/layout/grid-rowspan-split-1.typ | 89 +++++++++++++ tests/typ/layout/grid-rowspan-split-2.typ | 37 ++++++ tests/typ/layout/grid-rowspan-split-3.typ | 108 +++++++++++++++ tests/typ/layout/grid-rtl.typ | 41 ++++++ tests/typ/layout/grid-stroke.typ | 14 ++ 16 files changed, 526 insertions(+), 1 deletion(-) create mode 100644 tests/ref/bugs/grid-4.png create mode 100644 tests/ref/layout/grid-rowspan-basic.png create mode 100644 tests/ref/layout/grid-rowspan-split-1.png create mode 100644 tests/ref/layout/grid-rowspan-split-2.png create mode 100644 tests/ref/layout/grid-rowspan-split-3.png create mode 100644 tests/typ/bugs/grid-4.typ create mode 100644 tests/typ/layout/grid-rowspan-basic.typ create mode 100644 tests/typ/layout/grid-rowspan-split-1.typ create mode 100644 tests/typ/layout/grid-rowspan-split-2.typ create mode 100644 tests/typ/layout/grid-rowspan-split-3.typ (limited to 'tests') diff --git a/tests/ref/bugs/grid-4.png b/tests/ref/bugs/grid-4.png new file mode 100644 index 00000000..475f561e Binary files /dev/null and b/tests/ref/bugs/grid-4.png differ diff --git a/tests/ref/layout/grid-rowspan-basic.png b/tests/ref/layout/grid-rowspan-basic.png new file mode 100644 index 00000000..966c8fd9 Binary files /dev/null and b/tests/ref/layout/grid-rowspan-basic.png differ diff --git a/tests/ref/layout/grid-rowspan-split-1.png b/tests/ref/layout/grid-rowspan-split-1.png new file mode 100644 index 00000000..12cd5fc6 Binary files /dev/null and b/tests/ref/layout/grid-rowspan-split-1.png differ diff --git a/tests/ref/layout/grid-rowspan-split-2.png b/tests/ref/layout/grid-rowspan-split-2.png new file mode 100644 index 00000000..e55c5e23 Binary files /dev/null and b/tests/ref/layout/grid-rowspan-split-2.png differ diff --git a/tests/ref/layout/grid-rowspan-split-3.png b/tests/ref/layout/grid-rowspan-split-3.png new file mode 100644 index 00000000..3d809123 Binary files /dev/null and b/tests/ref/layout/grid-rowspan-split-3.png differ diff --git a/tests/ref/layout/grid-rtl.png b/tests/ref/layout/grid-rtl.png index f81e992e..3cf0b9aa 100644 Binary files a/tests/ref/layout/grid-rtl.png and b/tests/ref/layout/grid-rtl.png differ diff --git a/tests/ref/layout/grid-stroke.png b/tests/ref/layout/grid-stroke.png index 0f0b562a..409d10f1 100644 Binary files a/tests/ref/layout/grid-stroke.png and b/tests/ref/layout/grid-stroke.png differ diff --git a/tests/typ/bugs/grid-4.typ b/tests/typ/bugs/grid-4.typ new file mode 100644 index 00000000..691bf877 --- /dev/null +++ b/tests/typ/bugs/grid-4.typ @@ -0,0 +1,17 @@ +// Ensure gutter rows at the top or bottom of a region are skipped. + +--- +#set page(height: 10em) + +#table( + row-gutter: 1.5em, + inset: 0pt, + rows: (1fr, auto), + [a], + [], + [], + [f], + [e\ e], + [], + [a] +) diff --git a/tests/typ/layout/grid-colspan.typ b/tests/typ/layout/grid-colspan.typ index 3fd1a0fd..1bdadcf1 100644 --- a/tests/typ/layout/grid-colspan.typ +++ b/tests/typ/layout/grid-colspan.typ @@ -81,7 +81,7 @@ --- // Error: 4:8-4:32 cell would span a previously placed cell at column 2, row 0 -// Hint: 4:8-4:32 try specifying your cells in a different order or reducing the cell's colspan +// Hint: 4:8-4:32 try specifying your cells in a different order or reducing the cell's rowspan or colspan #grid( columns: 3, grid.cell(x: 2, y: 0)[x], diff --git a/tests/typ/layout/grid-positioning.typ b/tests/typ/layout/grid-positioning.typ index ca71cb37..5461fb1c 100644 --- a/tests/typ/layout/grid-positioning.typ +++ b/tests/typ/layout/grid-positioning.typ @@ -221,3 +221,11 @@ fill: (x, y) => if calc.odd(x + y) { red.lighten(50%) } else { green }, table.cell(x: 2, y: 6148914691236517206)[a], ) + +--- +// Error: 3:3-3:45 cell would span an exceedingly large position +// Hint: 3:3-3:45 try reducing the cell's rowspan or colspan +#grid( + columns: 500, + grid.cell(rowspan: 6148914691236517206)[a] +) diff --git a/tests/typ/layout/grid-rowspan-basic.typ b/tests/typ/layout/grid-rowspan-basic.typ new file mode 100644 index 00000000..49164fa6 --- /dev/null +++ b/tests/typ/layout/grid-rowspan-basic.typ @@ -0,0 +1,211 @@ +#grid( + columns: 4, + fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + inset: 5pt, + align: center, + grid.cell(rowspan: 2, fill: orange)[*Left*], + [Right A], [Right A], [Right A], + [Right B], grid.cell(colspan: 2, rowspan: 2, fill: orange.darken(10%))[B Wide], + [Left A], [Left A], + [Left B], [Left B], grid.cell(colspan: 2, rowspan: 3, fill: orange)[Wide and Long] +) + +#table( + columns: 4, + fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + inset: 5pt, + align: center, + table.cell(rowspan: 2, fill: orange)[*Left*], + [Right A], [Right A], [Right A], + [Right B], table.cell(colspan: 2, rowspan: 2, fill: orange.darken(10%))[B Wide], + [Left A], [Left A], + [Left B], [Left B], table.cell(colspan: 2, rowspan: 3, fill: orange)[Wide and Long] +) + +--- +#grid( + columns: 4, + fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + inset: 5pt, + align: center, + gutter: 3pt, + grid.cell(rowspan: 2, fill: orange)[*Left*], + [Right A], [Right A], [Right A], + [Right B], grid.cell(colspan: 2, rowspan: 2, fill: orange.darken(10%))[B Wide], + [Left A], [Left A], + [Left B], [Left B], grid.cell(colspan: 2, rowspan: 3, fill: orange)[Wide and Long] +) + +#table( + columns: 4, + fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + inset: 5pt, + align: center, + gutter: 3pt, + table.cell(rowspan: 2, fill: orange)[*Left*], + [Right A], [Right A], [Right A], + [Right B], table.cell(colspan: 2, rowspan: 2, fill: orange.darken(10%))[B Wide], + [Left A], [Left A], + [Left B], [Left B], table.cell(colspan: 2, rowspan: 3, fill: orange)[Wide and Long] +) + +--- +// Fixed-size rows +#set page(height: 10em) +#grid( + columns: 2, + rows: 1.5em, + fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + grid.cell(rowspan: 3)[R1], [b], + [c], + [d], + [e], [f], + grid.cell(rowspan: 5)[R2], [h], + [i], + [j], + [k], + [l], + [m], [n] +) + +--- +// Cell coordinate tests +#set page(height: 10em) +#show table.cell: it => [(#it.x, #it.y)] +#table( + columns: 3, + fill: red, + [a], [b], table.cell(rowspan: 2)[c], + table.cell(colspan: 2)[d], + table.cell(colspan: 3, rowspan: 10)[a], + table.cell(colspan: 2)[b], +) +#table( + columns: 3, + gutter: 3pt, + fill: red, + [a], [b], table.cell(rowspan: 2)[c], + table.cell(colspan: 2)[d], + table.cell(colspan: 3, rowspan: 9)[a], + table.cell(colspan: 2)[b], +) + +--- +// Auto row expansion +#set page(height: 10em) +#grid( + columns: (1em, 1em), + rows: (0.5em, 0.5em, auto), + fill: orange, + gutter: 3pt, + grid.cell(rowspan: 4, [x x x x] + place(bottom)[*Bot*]), + [a], + [b], + [c], + [d] +) + +--- +// Excessive rowspan (no gutter) +#set page(height: 10em) +#table( + columns: 4, + fill: red, + [a], [b], table.cell(rowspan: 2)[c], [d], + table.cell(colspan: 2, stroke: (bottom: aqua + 2pt))[e], table.cell(stroke: (bottom: aqua))[f], + table.cell(colspan: 2, rowspan: 10)[R1], table.cell(colspan: 2, rowspan: 10)[R2], + [b], +) + +--- +// Excessive rowspan (with gutter) +#set page(height: 10em) +#table( + columns: 4, + gutter: 3pt, + fill: red, + [a], [b], table.cell(rowspan: 2)[c], [d], + table.cell(colspan: 2, stroke: (bottom: aqua + 2pt))[e], table.cell(stroke: (bottom: aqua))[f], + table.cell(colspan: 2, rowspan: 10)[R1], table.cell(colspan: 2, rowspan: 10)[R2], + [b], +) + +--- +// Fractional rows +// They cause the auto row to expand more than needed. +#set page(height: 10em) +#grid( + fill: red, + gutter: 3pt, + columns: 3, + rows: (1em, auto, 1fr), + [a], [b], grid.cell(rowspan: 3, block(height: 4em, width: 1em, fill: orange)), + [c], [d], + [e], [f] +) + +--- +// Fractional rows +#set page(height: 10em) +#grid( + fill: red, + gutter: 3pt, + columns: 3, + rows: (1fr, auto, 1em), + [a], [b], grid.cell(rowspan: 3, block(height: 4em, width: 1em, fill: orange)), + [c], [d], + [e], [f] +) + +--- +// Cell order +#let count = counter("count") +#show grid.cell: it => { + count.step() + count.display() +} + +#grid( + columns: (2em,) * 3, + stroke: aqua, + rows: 1.2em, + fill: (x, y) => if calc.odd(x + y) { red } else { orange }, + [a], grid.cell(rowspan: 2)[b], grid.cell(rowspan: 2)[c], + [d], + grid.cell(rowspan: 2)[f], [g], [h], + [i], [j], + [k], [l], [m], + grid.cell(rowspan: 2)[n], [o], [p], + [q], [r], + [s], [t], [u] +) + +--- +#table( + columns: 3, + rows: (auto, auto, auto, 2em), + gutter: 3pt, + table.cell(rowspan: 4)[a \ b\ c\ d\ e], [c], [d], + [e], table.cell(breakable: false, rowspan: 2)[f], + [g] +) + +--- +// Test cell breakability +#show grid.cell: it => { + assert.eq(it.breakable, (it.x, it.y) != (0, 6) and (it.y in (2, 5, 6) or (it.x, it.y) in ((0, 1), (2, 3), (1, 7)))) + it.breakable +} +#grid( + columns: 3, + rows: (6pt, 1fr, auto, 1%, 1em, auto, auto, 0.2in), + row-gutter: (0pt, 0pt, 0pt, auto), + [a], [b], [c], + grid.cell(rowspan: 3)[d], [e], [f], + [g], [h], + [i], grid.cell(rowspan: 2)[j], + [k], + grid.cell(y: 5)[l], + grid.cell(y: 6, breakable: false)[m], grid.cell(y: 6, breakable: true)[n], + grid.cell(y: 7, breakable: false)[o], grid.cell(y: 7, breakable: true)[p], grid.cell(y: 7, breakable: auto)[q] +) diff --git a/tests/typ/layout/grid-rowspan-split-1.typ b/tests/typ/layout/grid-rowspan-split-1.typ new file mode 100644 index 00000000..e247fa80 --- /dev/null +++ b/tests/typ/layout/grid-rowspan-split-1.typ @@ -0,0 +1,89 @@ +// Rowspan split tests + +--- +#set page(height: 10em) +#table( + columns: 2, + rows: (auto, auto, 3em), + fill: red, + [a], table.cell(rowspan: 3, block(width: 50%, height: 10em, fill: orange) + place(bottom)[*ZD*]), + [e], + [f] +) + +--- +#set page(height: 10em) +#table( + columns: 2, + rows: (auto, auto, 3em), + row-gutter: 1em, + fill: red, + [a], table.cell(rowspan: 3, block(width: 50%, height: 10em, fill: orange) + place(bottom)[*ZD*]), + [e], + [f] +) + +--- +#set page(height: 5em) +#table( + columns: 2, + fill: red, + inset: 0pt, + table.cell(fill: orange, rowspan: 10, place(bottom)[*Z*] + [x\ ] * 10 + place(bottom)[*ZZ*]), + ..([y],) * 10, + [a], [b], +) + +--- +#set page(height: 5em) +#table( + columns: 2, + fill: red, + inset: 0pt, + gutter: 2pt, + table.cell(fill: orange, rowspan: 10, place(bottom)[*Z*] + [x\ ] * 10 + place(bottom)[*ZZ*]), + ..([y],) * 10, + [a], [b], +) + +--- +#set page(height: 5em) +#table( + columns: 2, + fill: red, + inset: 0pt, + table.cell(fill: orange, rowspan: 10, breakable: false, place(bottom)[*Z*] + [x\ ] * 10 + place(bottom)[*ZZ*]), + ..([y],) * 10, + [a], [b], +) + +--- +#set page(height: 5em) +#table( + columns: 2, + fill: red, + inset: 0pt, + gutter: 2pt, + table.cell(fill: orange, rowspan: 10, breakable: false, place(bottom)[*Z*] + [x\ ] * 10 + place(bottom)[*ZZ*]), + ..([y],) * 10, + [a], [b], +) + +--- +#set page(height: 5em) +#grid( + columns: 2, + stroke: red, + inset: 5pt, + grid.cell(rowspan: 5)[a\ b\ c\ d\ e] +) + +--- +#set page(height: 5em) +#table( + columns: 2, + gutter: 3pt, + stroke: red, + inset: 5pt, + table.cell(rowspan: 5)[a\ b\ c\ d\ e] +) diff --git a/tests/typ/layout/grid-rowspan-split-2.typ b/tests/typ/layout/grid-rowspan-split-2.typ new file mode 100644 index 00000000..189feed3 --- /dev/null +++ b/tests/typ/layout/grid-rowspan-split-2.typ @@ -0,0 +1,37 @@ +// Rowspan split without ending at the auto row + +--- +#set page(height: 6em) +#table( + rows: (4em,) * 7 + (auto,) + (4em,) * 7, + columns: 2, + column-gutter: 1em, + row-gutter: (1em, 2em) * 4, + fill: (x, y) => if calc.odd(x + y) { orange.lighten(20%) } else { red }, + table.cell(rowspan: 15, [a \ ] * 15), + [] * 15 +) + +--- +#set page(height: 6em) +#table( + rows: (4em,) * 7 + (auto,) + (4em,) * 7, + columns: 2, + column-gutter: 1em, + row-gutter: (1em, 2em) * 4, + fill: (x, y) => if calc.odd(x + y) { green } else { green.darken(40%) }, + table.cell(rowspan: 15, block(fill: blue, width: 2em, height: 4em * 14 + 3em)), + [] * 15 +) + +--- +#set page(height: 6em) +#table( + rows: (3em,) * 15, + columns: 2, + column-gutter: 1em, + row-gutter: (1em, 2em) * 4, + fill: (x, y) => if calc.odd(x + y) { aqua } else { blue }, + table.cell(breakable: true, rowspan: 15, [a \ ] * 15), + [] * 15 +) diff --git a/tests/typ/layout/grid-rowspan-split-3.typ b/tests/typ/layout/grid-rowspan-split-3.typ new file mode 100644 index 00000000..4c3ce7d8 --- /dev/null +++ b/tests/typ/layout/grid-rowspan-split-3.typ @@ -0,0 +1,108 @@ +// Some splitting corner cases + +--- +// Inside the larger rowspan's range, there's an unbreakable rowspan and a +// breakable rowspan. This should work normally. +// The auto row will also expand ignoring the last fractional row. +#set page(height: 10em) +#table( + gutter: 0.5em, + columns: 2, + rows: (2em,) * 10 + (auto, auto, 2em, 1fr), + fill: (_, y) => if calc.even(y) { aqua } else { blue }, + table.cell(rowspan: 14, block(width: 2em, height: 2em * 10 + 2em + 5em, fill: red)[]), + ..([a],) * 5, + table.cell(rowspan: 3)[a\ b], + table.cell(rowspan: 5, [a\ b\ c\ d\ e\ f\ g\ h]), + [z] +) + +--- +// Inset moving to next region bug +#set page(width: 10cm, height: 2.5cm, margin: 0.5cm) +#set text(size: 11pt) +#table( + columns: (1fr, 1fr, 1fr), + [A], + [B], + [C], + [D], + table.cell(rowspan: 2, lorem(4)), + [E], + [F], + [G], +) + +--- +// Second lorem must be sent to the next page, too big +#set page(width: 10cm, height: 9cm, margin: 1cm) +#set text(size: 11pt) +#table( + columns: (1fr, 1fr, 1fr), + align: center, + rows: (4cm, auto), + [A], [B], [C], + table.cell(rowspan: 4, breakable: false, lorem(10)), + [D], + table.cell(rowspan: 2, breakable: false, lorem(20)), + [E], +) + +--- +// Auto row must expand properly in both cases +#set text(10pt) +#show table.cell: it => if it.x == 0 { it } else { layout(size => size.height) } +#table( + columns: 2, + rows: (1em, auto, 2em, 3em, 4em), + gutter: 3pt, + table.cell(rowspan: 5, block(fill: orange, height: 15em)[a]), + [b], + [c], + [d], + [e], + [f] +) + +#table( + columns: 2, + rows: (1em, auto, 2em, 3em, 4em), + gutter: 3pt, + table.cell(rowspan: 5, breakable: false, block(fill: orange, height: 15em)[a]), + [b], + [c], + [d], + [e], + [f] +) + +--- +// Expanding on unbreakable auto row +#set page(height: 7em, margin: (bottom: 2em)) +#grid( + columns: 2, + rows: (1em, 1em, auto, 1em, 1em, 1em), + fill: (x, y) => if x == 0 { aqua } else { blue }, + stroke: black, + gutter: 2pt, + grid.cell(rowspan: 5, block(height: 10em)[a]), + [a], + [b], + grid.cell(breakable: false, v(3em) + [c]), + [d], + [e], + [f], [g] +) + +--- +#show table.cell.where(x: 0): strong +#show table.cell.where(y: 0): strong +#set page(height: 13em) +#let lets-repeat(thing, n) = ((thing + colbreak(),) * (calc.max(0, n - 1)) + (thing,)).join() +#table( + columns: 4, + fill: (x, y) => if x == 0 or y == 0 { gray }, + [], [Test 1], [Test 2], [Test 3], + table.cell(rowspan: 15, align: horizon, lets-repeat((rotate(-90deg, reflow: true)[*All Tests*]), 3)), + ..([123], [456], [789]) * 15 +) diff --git a/tests/typ/layout/grid-rtl.typ b/tests/typ/layout/grid-rtl.typ index dcac9810..be9fac51 100644 --- a/tests/typ/layout/grid-rtl.typ +++ b/tests/typ/layout/grid-rtl.typ @@ -137,3 +137,44 @@ #grid( [a], grid.vline(position: left) ) + +--- +#set text(dir: rtl) + +#grid( + columns: 4, + fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + inset: 5pt, + align: center, + grid.cell(rowspan: 2, fill: orange)[*Left*], + [Right A], [Right A], [Right A], + [Right B], grid.cell(colspan: 2, rowspan: 2, fill: orange.darken(10%))[B Wide], + [Left A], [Left A], + [Left B], [Left B], grid.cell(colspan: 2, rowspan: 3, fill: orange)[Wide and Long] +) + +#table( + columns: 4, + fill: (x, y) => if calc.odd(x + y) { blue.lighten(50%) } else { blue.lighten(10%) }, + inset: 5pt, + align: center, + gutter: 3pt, + table.cell(rowspan: 2, fill: orange)[*Left*], + [Right A], [Right A], [Right A], + [Right B], table.cell(colspan: 2, rowspan: 2, fill: orange.darken(10%))[B Wide], + [Left A], [Left A], + [Left B], [Left B], table.cell(colspan: 2, rowspan: 3, fill: orange)[Wide and Long] +) + +--- +#set page(height: 10em) +#set text(dir: rtl) +#table( + columns: 2, + rows: (auto, auto, 3em), + row-gutter: 1em, + fill: red, + [a], table.cell(rowspan: 3, block(width: 50%, height: 10em, fill: orange) + place(bottom)[*ZD*]), + [e], + [f] +) diff --git a/tests/typ/layout/grid-stroke.typ b/tests/typ/layout/grid-stroke.typ index 51b810d9..9d01e1cd 100644 --- a/tests/typ/layout/grid-stroke.typ +++ b/tests/typ/layout/grid-stroke.typ @@ -274,6 +274,20 @@ table.hline(position: bottom) ) +--- +// Test partial border line overrides +#set page(width: auto, height: 7em, margin: (bottom: 1em)) +#table( + columns: 4, + stroke: (x, y) => if y == 0 or y == 4 { orange } else { aqua }, + table.hline(stroke: blue, start: 1, end: 2), table.cell(stroke: red, v(3em)), table.cell(stroke: blue)[b], table.cell(stroke: green)[c], [M], + [a], [b], [c], [M], + [d], [e], [f], [M], + [g], [h], [i], [M], + table.cell(stroke: red)[a], table.cell(stroke: blue)[b], table.cell(stroke: green)[c], [M], + table.hline(stroke: blue, start: 1, end: 2), +) + --- // Error: 8:3-8:32 cannot place horizontal line at the 'bottom' position of the bottom border (y = 2) // Hint: 8:3-8:32 set the line's position to 'top' or place it at a smaller 'y' index -- cgit v1.2.3