Repeatable Table Headers [More Flexible Tables Pt.5a]#3545
Merged
laurmaedje merged 44 commits intotypst:mainfrom Mar 6, 2024
Merged
Repeatable Table Headers [More Flexible Tables Pt.5a]#3545laurmaedje merged 44 commits intotypst:mainfrom
laurmaedje merged 44 commits intotypst:mainfrom
Conversation
482c11c to
a2ebe32
Compare
18 tasks
laurmaedje
reviewed
Mar 4, 2024
laurmaedje
reviewed
Mar 4, 2024
Relevant for rowspans with full colspan spanning multiple auto rows (the first N - 1 will be empty and removed)
Contributor
Author
|
I've added a fix for a small bug I found while, well, pondering about things. :P Turns out lines above empty auto rows were being ignored since the auto rows were removed. Now they are still considered, but have less priority than lines above whatever is below. |
Member
|
Needs merge/rebase for status checks to be okay again. |
Doesn't matter much right now since headers always start at the first row, but doing this mostly for correctness
- Delay gutter adjustment for header end index - Cells always check if they are in a header, if so expand it
- Ensure rowspans aren't laid out through the same rows twice - Ensure multi-page auto rows cause previous rowspans to be laid out even at their first regions
- It only checked if there were '(amount of header rows)' rows or less in the region to determine if the header was an orphan. The problem is that empty auto rows are omitted, so the header can have less rows than 'header.end'. Now, we just check if the last row is a header. If so, no other rows were laid out.
- We were enumerating since the start of all regions, not since the first region spanned by the rowspan. Therefore, the rowspan wouldn't apply its `dy` correctly, only in the first page of the entire table.
- Line position filtering has been moved to 'render_fills_strokes'
Contributor
Author
|
Alright, turns out there were several little logic bugs I could think of. There could perhaps be more, but in principle the most relevant ones should be gone. We can probably figure the rest out during testing. |
Safer alternative in case a header row is removed
Contributor
Author
|
Ready. 👍 |
Member
|
Great, thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Allows specifying a
grid.headerandtable.headerto have some rows repeat across a grid's or table's pages.NOTE: This PR depends on #3501, so the PR diff is larger than it really is; as such, here is the link to the real diff: PgBiel/typst@rowspans...PgBiel:typst:repeatable-headers(EDIT: PR rebased!)First part of the fifth task in #3001. Closes #400 (as the initially desired feature set is available), although more header features are intended to be added over time.
For now, a header may only start at the top of the table, and end at its last page.
User-facing API
table.headerandgrid.headerto place a repeatable header in your grid or table. It will repeat across pages.table.header(repeat: false, ...)to not repeat it, though that's generally not very useful (other than for organizational purposes), so the default isrepeat: true.Without gutter:
With gutter:
Implementation details
The implementation is actually rather simple (compared to other PRs, such as rowspans - that one was very complex). Here are the most important tricks I used for layout:
Headerstruct inCellGrid. It currently only contains where the header stops, since the header always starts aty == 0.finish_region, we calllayout_header, which will layout, for the next region, each individual header row as an unbreakable row group, and thus calculate the header height for the current region.layout_headerwill usesimulate_header(which just callssimulate_unbreakable_row_groupfrom the rowspans PR) to calculate the header height and skip to the first fitting region.finish_region).self.regions.in_last()is true, currently. In this PR, however, I created a newin_last_with_offsetfunction. It's very similar to the former, but instead of checking if we're in theregions.lastregion and all the full height is remaining, we check iffull height - header heightis remaining, since skipping a region will always add a header to the top. So, if we're at the last region and there's just the header above us, we stop skipping.prepare_auto_row_cell_measurement, where we prepare the information needed to construct thepod, we now useself.regions.mapto modify the backlog and the last region by subtracting the current header height from both. This makes sure cells in breakable auto rows won't overlap with the header.Regions::nextto automatically subtract an automatically-calculated header height from the new region size and not change the value offull, which is probably more technically appropriate, but I didn't take this approach not only to keep the implementation simple, but also becausefullis already reduced when we're effectively laying out the rowspan, so it wouldn't make much of a difference in practice, other than through possibly very unusual setups. We can always change this in a future update as users give their feedback.And that's it!
Other details
Grid/TableChildto accept both headers andGrid/TableItem(the old children); headers can contain items too (but not other headers, of course - there are helpful errors for that). InCellGrid::resolve, there are now two for loops - one for children and one for items inside the children (if the child is a header, then one child iteration will correspond to N item iterations (inside the header); if the child is a single item, then there's only a single item iteration, and everything behaves as usual).