#Tables

1 messages Β· Page 4 of 1

wary knot
#

note that tables also have a last step which adds fills and strokes

#

though it wasnt hard to add rowspan support for that

#

well, had to think a bit to get the best algorithms and stuff but yeah

#

:p

#

im really glad i did colspans first

#

i cant imagine debugging both at once

#

lol

#

and the colspan code gave a strong foundation for the more basic rowspan stuff

bold bluff
#

I could imagine sometime in the future people would use typst to generate HTML, and have longer tables, and others may want to print those to pages and PDFs and such. Of course, table sizes would be arbitrary in some formats and have to break in others. It’s probably a worst case scenario for this.

wary knot
#

well yea

#

i think all of this work would be basically useless when exporting to HTML

#

lol

#

though HTML itself supports rowspans and colspans

#

and headers

#

so whatever

#

and regardless

#

the pre-grid work would still be useful

#

i.e. table.cell and the ability to specify arbitrary positions and stuff

#

that'd translate into HTML with cells in the same positions as in PDF

#

in theory

#

that'll depend on how the "layout rework" / layout IR thing pans out

#

but it's definitely possible to reuse like 100% of that particular code lol

robust viper
#

Tables

robust viper
#

I'll write down some thoughts about this later, not sure whether still today.

robust viper
#

Here's an example, which I will use for my question: There are two columns. In the first column there is cell A, which is a rowspan going from row i to k. In the second column, there is cell B, which has no span. Row i is auto-sized and a few non-auto rows follow. If I understand your approach correctly, to determine the proper size for B, you measure A and then subtract the fixed row sizings from i+1 to k plus the gutters in between and this will be the height of B.

Now the question: Do you measure A in an infinite vertical space or do you measure it precisely with the regions it will be layouted into later? If it it's latter, couldn't you simulate how the rows i+1 to k will be layouted (since they are all fixed sized, let's ignore fr for now) and by that also know which gutters will be present?

wary knot
# robust viper Here's an example, which I will use for my question: There are two columns. In t...

Do you measure A in an infinite vertical space or do you measure it precisely with the regions it will be layouted into later?
Sort of the latter I think? I mimic its backlog (at least the heights we know from previous regions up to the last spanned auto row), but thats about it. However, now that I'm thinking about it, it probably would make sense to simulate that even more precisely, by adding the current region's size to the backlog, and then appending whatever self.regions.backlog has.

If it it's latter, couldn't you simulate how the rows i+1 to k will be layouted (since they are all fixed sized, let's ignore fr for now) and by that also know which gutters will be present?
Hmmm... I didn't consider that. But that also sounds a bit annoying to implement πŸ€”
I'll experiment with the idea though, so thanks for sharing

robust viper
#

By the way, regarding the stroke thing you wrote about a while ago. I think we should not let the strokes affect layout like I attempted in my branch. It would be less predictable and way too complex.

wary knot
#

it'd happen at grid resolution stage, so it'd be pretty straightforward
though at the same time I think it could be undesirable

robust viper
#

I would at least leave it for now. Especially since then it is consistent with how other containers behave at the moment.

#

Configurable inside/center/outside strokes can be a separate work package across the whole layout engine.

wary knot
#

got it

wary knot
#

for two main reasons

#

the first one being that (in my view) it's still possible for the rowspan to receive less space than it needs, since some other cell in the auto row could make it expand enough to change the configuration of gutter rows, which could theoretically make our predictions fail

#

the second one being that we'd have to simulate layout with quite some precision, so in particular we'd have to consider that there might be unbreakable rowspans contained within rows spanned later, so we'd have to take that into account too

#

cuz that could cause a group of rows to move to the next page

robust viper
wary knot
robust viper
#

It would be slower but maybe not that bad due to memoization.

#

At least if we ensure that it doesn't degrade to exponential time in edge cases.

wary knot
#

hmmm

#

so you mean we'd "unlayout" all rows after that last auto row, (after all rowspan rows were laid out) measure again and repeat layout for the remaining rows?

#

i mean

#

"unlayout" after measuring

#

not the opposite

#

lol

robust viper
#

yeah more or less

wary knot
#

though maybe if we do that, we aren't necessarily converging to a good solution, just creating a new configuration

#

idk

robust viper
#

in the second pass we would know the sizes for sure

#

at least for the first row we backtracked back to

#

at least I think so

wary knot
#

hmmm

#

i think i'll need to think further about this, rn my brain is kinda melting πŸ˜‚

#

in principle this would lead to some rather significant changes in the implementation

#

cuz rn rowspans are only laid out at the end

#

after all other cells

#

but well, if we can get that to work, then perhaps it's better, in the name of correctness

robust viper
#

is a rowspan ending in row k laid out at row k or after everything else?

wary knot
#

right now the latter (although it's possible to change that)

robust viper
#

interesting

#

one thing I'd like to note is that it is important in which order things are pushed into frames

#

as that decides the numbering order

#

but it can also be faked by inserting things at an index (into a layer) after the fact

wary knot
#

right

#

my main concern is that some rows can be omitted from layout

#

for example, if row k is fully empty by the time the rowspan gets there, and we depend on row k to layout, then the rowspan isnt laid out

#

assuming row k is an auto row

#

cuz empty auto rows are omitted

robust viper
#

what is the definition of "empty"?

wary knot
#

measure returned empty frames

#

(im referring to existing code )

#

i guess we could use some mechanism to detect if theres some non-empty rowspan which stops at that auto row, if so push it even if as an empty frame

robust viper
#

I think that this code is only there to prevent empty frames at the end of a page in case of a multi page auto row.

wary knot
#

sorry

wary knot
#

this basically

#

(ignore the unbreakable stuff, that's probably gonna change :p)

robust viper
#

I am not sure whether that happens in practice tbh

wary knot
#

at this point it will basically only happen for rowspans

#

:p

#

cuz measure skips all frames from previous regions (for the last spanned auto row)

#

and ignores rowspans (when it's not the last)

#

so for example , if a cell spans all columns and has a rowspan across 5 auto rows, at least the first 4 are omitted

#

which caused some problems with fill and stroke, but I managed to work around that

robust viper
wary knot
#

consider that to be me thinking out loud

#

πŸ˜‚

robust viper
#

I also wonder about fractional rows

#

In some experiments with tablex, fr + rowspan failed to fill the whole page.

wary knot
#

just to confirm, the first page? or subsequent pages?

robust viper
#

Just one page

wary knot
#

hmmm that's interesting

robust viper
#

I can send the code when I'm home

wary knot
#

yea sure

#

i'll check the tablex implementation later but in principle it's likely distinct from the one we have now

#

cuz tablex doesnt layout per region but per row

#

so row heights are all calculated in advance

robust viper
#

is fr best effort then?

wary knot
#

well, it depends on measure basically

#

which isnt always fully accurate

#

there could be multiple variables at play there, e.g. layout convergence

robust viper
#

It did converge

wary knot
#

i see

#

i can take a look later then

#

that could be insightful

#

but either way fractional rows are indeed a bit of a problem

#

in principle it could be simpler to just admit that we can't fully predict how stuff will be laid out and provide tablex's fit-spans option to users so they can control which rowspans affect auto rows

#

but i wanna take a stab at the other approach of backtracking at the last row

#

let's see how that turns out

#

also btw

#

a bit unrelated to this discussion but

#

ive been thinking of starting work on line customization in parallel, in case we spend too much time on rowspans

#

im still evaluating whether or not that could interfere somehow but in principle i think it should be pretty self-contained

#

im not sure yet though so just thinking out loud again

#

πŸ˜„

robust viper
# wary knot yea sure

This demonstrates what I was referring to earlier:

#import "@preview/tablex:0.0.8": *
#show: columns.with(2)

#tablex(
  columns: (80pt, 80pt),
  rows: (auto, 1fr, 20pt),
  rowspanx(3, lorem(10)),
  [A],
  [B],
  [C],
)
#colbreak()
#table(
  columns: (80pt, 80pt),
  rows: (auto, 1fr, 20pt),
  lorem(10),
  [A],
  lorem(10),
  [B],
  lorem(1),
  [C],
)
wary knot
#

I'm not really sure why it's doing that lol

#

measure went wrong along the way or something

#

Anyway

#

I'm starting to prototype the line stuff cuz yeah

#

I'll share in a sec

#

so, what do we think about the following:

  1. global (Grid-wide) stroke is a dictionary with 3 keys:

(inside: stroke or none or (x, y) => (left, right, top, bottom: cell stroke),
outside: stroke or (left, right, top, bottom: border stroke) or none or auto (default, inherits 'inside'),
gutter: stroke or none or (x, y) => same as cell stroke but for gutter cells or auto (default, inherits 'inside') )
^not 100% sure about the gutter one yet
writing just stroke: red for example is the same as stroke: (inside: red)

  1. per-cell stroke: table.cell(stroke: stroke or none or (left: right: ...) or auto (Default))

  2. explicit lines: grid.hline, grid.vline , ... => in principle they'd be types, not elements, but not 100% sure

    • they can cross gutters by default (though we could add a parameter like skip-gutters to override that, however not strictly necessary)
  3. priority order is: per-cell stroke and explicit hline / vline > anything defined by global stroke (in principle i was thinking of having global stroke just "disappear" on specific points when you override it somewhere)

robust viper
#

and could I also write a left/right/top/bottom dict directly without returning it through a (x, y) => function?

wary knot
#

though we dont have to maintain this layout, i guess the more important question here is regarding the functionality

#

in principle i think im fine with removing the outside layer

wary knot
wary knot
#

so thats probably just the first thing i had thought of

#

but it's fine, we'll create a new dict type either way so

robust viper
#

for that, you might actually be able to salvage something from my branch

wary knot
#

oh right

#

the strokes branch

#

got it

night python
wary knot
#

im not rlly sure regarding vlines cuz I have to admit the way tablex does it is a bit hacky

#

like the

#tablex(
  vlinex(), (), (), vlinex(),
)

deal

#

but I think I can implement something similar but without the (), () stuff

#

you'd just have to table.vline(stroke: none)

#

Β―_(ツ)_/Β―

#

anyway im writing stuff down

#

and i think i have a clearer idea of my initial plan for strokes now

#

basically we'll have the following priorities:

#

cell stroke > explicit lines > automatic lines

#

and when a cell's stroke is none that's equivalent to not changing much

#

also I think I will use the tablex approach of disabling the automatic line when you draw an explicit line over it

#

otherwise it would be too clunky to deal with

#

however the automatic line won't be disabled if you just change cell stroke

#

after all you can completely override the automatic line using explicit lines, but not with cell stroke (which cant cross gutters since gutters - in theory - aren't cells)

#

oh god

#

this will multiply the size of table cells tenfold

#

sorry people

#

πŸ˜‚

robust viper
frozen oyster
#

that's a chonker

wary knot
robust viper
#

well, if an Option is none we waste 480 bytes right now. if there was a box in the Option we would save that

#

if the strokes are uniform strokes it should probably rather be Arc though

#

and after type rework that Arc could even be shared with what Value will then use to store the Stroke!

wary knot
#

right

#

I guess for per-cell override we can just create a new Arc anyway

#

though we would have to Arc on the Sides right?

robust viper
#

sides<option<arc<stroke>>>

#

is what I meant

wary knot
#

ah

#

though

#

I think this could not be very helpful if they use the (x, y) => (sides of stroke here) form

#

only if they use (sides of stroke here) directly

#

which is probably gonna be rare

#

and automatic lines (i.e. specifying red + 2pt instead of either option above) dont affect the cell stroke at all (at least in my current mental model)

robust viper
#

but they price for the field size is paid always

wary knot
#

yeah

#

i agree we need to do something about it

#

just not sure how that would go about

robust viper
#

since there is no other Option in the type that's the highest layer where we can save anything

#

or through Arc sharing of course, but that seems harder

wary knot
#

i think what i was thinking of is that Arc sharing would be very rare (only in the (sides of stroke here) case)

#

for 99% of other cases it would be just a Box

robust viper
#

I was thinking of the case where Sides casts from a stroke and ends up calling Sides::uniform

wary knot
#

hmmm

#

I think I see what you mean

#

so you mean that , when they write (x, y) => black for example

#

we reduce the amount of space used by 4 times

robust viper
#

yes

wary knot
#

okay

#

that makes sense

robust viper
#

we could also implement a more general optimization within Sides

wary knot
#

I hope it doesnt affect performance too much πŸ˜… , but a small price to pay for 10000 tables with 5000 cells each...

robust viper
#

where it chooses between uniform and non-uniform boxed/arced repr internally

#

but that's out of scope for the PR I would say

wary knot
#

i agree

#

but sounds like a cool idea in principle

#

we'd have to study whether or not it'd be worth it for other cases though

#

but that's for another day

#

also btw @robust viper I can finally implement Fold on Celled thanks to your PR on that

#

so that's pretty cool πŸ‘

robust viper
#

nice

wary knot
#

right now I think im only gonna implement it for Value, Value, im not sure if it'd make sense to implement for others

#

I guess it could perhaps make sense for inner = Array, outer = Value which isnt too hard to implement
but then inner = Func, outer = Value would require creating another Func πŸ€”

#

so for now i chose to be consistent :p

wary knot
# robust viper sides<option<arc<stroke>>>

ok so, do you think it's fine to implement IntoValue / FromValue / Reflect for Arc<T> in order to achieve this? Or should I make a newtype for this to reduce the impact?

#

well, the plan is to generalize it in the future IIUC so maybe the former isnt that bad of a workaround for now, but still could be seen as too much for the PR ig

robust viper
#

it seems okay at first glance

wary knot
#

ok

#

i think it wouldnt be too hard to change later anyway

#

so i'll go with that for now

wary knot
#

right so

#

@robust viper regarding the Arc optimization, I found a small problem :p

#

I believe the optimization is considerably hindered by Resolve

#

because to implement Resolve on Arc<T> we have to create a new Arc<T::Output>

#

so basically every side is always a separate Arc

#

rn strokes are always resolved so im not sure how we'd tackle that

#

even if they arent resolved immediately, i.e. you specified (x, y) => stroke, that just delays the resolve call to when the function is effectively called, i.e. when generating the grid (I had to make a separate ResolvedCelled<T> type to make that possible)

#

an alternative would be to check for equal strokes somehow, but that sounds like the amount of bookkeeping required wouldnt be worth it

#

unless we use a HashMap or something I guess

#

well , thinking further, I guess there isnt much we can do regarding delayed resolve calls either way

#

while non-delayed ones should be fine

#

we just wont be able to convert to FixedStroke right away

#

(otherwise we'd have to create new Arcs to convert Arc<Stroke<Abs>> - that's the output of resolving Arc<Stroke> - to Arc<FixedStroke>)

#

uhhh actually idk

#

theres a problem which is that pushing the stroke for GridCell requires converting back from Arc<Stroke<Abs>> to Arc<Stroke> (aka Arc<Stroke<Length>> )

#

maybe i can like fold twice or something to avoid that lol

wary knot
#

that's how browsers do tables apparently

#

and seems like we have been nailing it so far

#

πŸ˜‚

#

step 5 is different though but thats the only "big" difference

grand haven
wary knot
#

uh

#

sorry

#

2.A.5

grand haven
#

3A.5?

wary knot
grand haven
#

right

wary knot
#

in tablex they all roughly match

#

in typst the current algorithm measures rows on the fly

#

so it's a bit different

grand haven
#

Is one approach better than the other?

wary knot
#

well

#

measuring on the fly is probably better for the simplest cases (i.e. no rowspans)

#

but we will have rowspans now so we have to backtrack at some point or another

grand haven
#

considering we'll have html output at some point it might be good to align with css tables anyway

wary knot
#

yeah for sure

#

though i wouldnt tie our hands too much in that sense

#

but still not bad to know that we arent that far off

#

of note, my current algorithm just delays layout of rowspans to the end of table layout

#

but there are a few problems with that (as Laurenz said, the order in which things are placed matters for counter numbering and stuff)

#

so in theory i'd make it happen whenever the last row is reached instead

#

but the problem is that the last row is currently not guaranteed to exist

#

πŸ˜‚

#

though thats pretty easy to fix in general

#

it's just that theres an optimization for when no space is being used in an auto row , then it's just yeetus deletus

#

anyway

#

so

#

one possible solution to the problems i was facing could in theory be to take an approach similar to the one suggested by the CSS spec

#

but with no rowspans it could be a bit wasteful

#

maybe we can have a flag like "needs relayout" idk

#

but im still considering it, cuz laurenz did propose an alternate algorithm , so im currently thinking about it

wary knot
#

from what im seeing, using #[resolve] basically means resolve is called on the spot

#

meaning the Arc optimization is basically useless :p
(as it is right now)

#

cuz each resolve has to create a new Arc so u get one Arc per stroke field in the end

#

one possible way around this would be to make a SharedSides or smth as an enum with Uniform(Arc<T>) vs Sides(Sides<Arc<T>>) or something

#

then it would at least allow some gains when using black instead of (left: black, right: black, top: black, bottom: black) for stroke

#

but anyway

#

im gonna work with what I have for now, we can change later

wary knot
#

lovely

#

(i fixed it. but funny = true;)

vernal herald
#

fucking awesome

grand haven
wary knot
#

some random test failure

#

among like 30 others

#

and theres a veeeeery tiny difference

#

lol

#

of two pixels!

upbeat pine
#

but with png it's hard to understand anything.

wary knot
#

lol

#

yea

#

but it was enough for me to figure it out

#

i screwed up the thickness of horizontal lines

#

lol

#

cuz im implementing per-cell stroke on vertical lines first

#

well, with that said...

upbeat pine
#

it looks like for small files pdf files are literally 5 times smaller

#

so maybe pdf is the way.

wary knot
#

i mean... depends a lot on the content lol

#

but anyway

#

ok so

#

one thing i was wondering about

#

if a cell has something like stroke: none, should that cause it to disable the automatic lines around it? πŸ€”

#

and what about explicit lines ?

#

right now im just considering stroke: none as "i dont care"

#

so lines just keep going like usual

#

i guess thats also how stuff behaves with a block or smth

upbeat pine
#

none is void, nothingness

wary knot
#

that'd be a global stroke: none

upbeat pine
#

hmm

wary knot
#

basically it'd encourage using set union rather than set difference to build your set of table lines, if you get what i mean

#

:p

upbeat pine
#

well, I think it's the same as CSS priority selectors. the more specific it is, the more priority

#

so if global-only set, then that's that. if also stroke none set for a cell, then it should override the global setting

upbeat pine
wary knot
#

yea but consider like

#

consider two adjacent cells AB

#

what if A.stroke.right is none and B.stroke.left is red?

#

right now im prioritizing B either way

#

but i think it'd be more natural if it were red

upbeat pine
#

and this is what I don't understand in spreadsheets. idk what calc does in this situation

wary knot
#

most will probably just overlap strokes

#

im trying to unify them if possible

upbeat pine
#

afaik it seems that it prints whichever's stroke is the thickest.

wary knot
#

so rn what im doing is a three-way fold

#

im folding the left stroke with the right stroke with the automatic line's stroke

upbeat pine
#

so like if red stroke is thicker than blue one, then the stroke will be red and no blue at all.

wary knot
#

so if the left stroke is 5pt, the right stroke is red and the automatic line is dashed, you'll get a dashed red 5pt stroke

upbeat pine
#

so "some" stroke by this logic should be prioritized over "none" stroke.

upbeat pine
wary knot
#

well

#

i was planning on doing that for explicit lines

#

as in explicit lines dont fold with per-cell strokes

#

but maybe that doesnt make much sense

#

or at least could be seen as inconsistent. idk

#

maybe it should be just a two-way fold

#

between the two cell strokes

#

at least that would be more consistent

upbeat pine
#

but why fold?

wary knot
#

fold combines properties in a useful way

#

one cell just wants a 5pt border, the other just wants a red border

#

so you get red + 5pt

upbeat pine
#

but I think this is very impractical

wary knot
#

but if one wants blue and the other wants red then one necessarily wins

#

so i prioritized the right cell in those cases

wary knot
#

like

upbeat pine
wary knot
#

if you write black + 5pt instead of just 5pt then you will properly ask for a color

#

and then you will engage in the epic duel with your right partner

#

another alternative is to just be lazy and overlap the two

#

but idk

#

i think per-cell strokes are "in the same level" of abstraction

#

so i think overlapping would be bad cuz if everyone asked for the same stroke you'd have a bunch of equal strokes overlapped

#

unnecessarily

#

meanwhile it could make more sense to overlap explicit lines

upbeat pine
wary knot
#

cuz then that's on you

wary knot
#

for RTL it'd be the left one automatically

upbeat pine
#

and here is if red is slightly thicker:

wary knot
#

hmm

#

interesting

#

in this case it'd probably be thicker blue

#

unless blue explicitly specified a thickness

#

then blue would still win

#

but be thin

#

i think it's more consistent to always prioritize the right cell

#

maybe right + bottom in general

#

for hlines

upbeat pine
wary knot
#

sorry

#

messed it up lol

#

fixed

#

i guess spreadsheets just default to LTR

upbeat pine
wary knot
#

you could test with some RTL language

upbeat pine
#

maybe, but we don't know

wary knot
#

ok so

upbeat pine
#

also how do you save that in the spreadsheet that supposed to look the same on any machine?

wary knot
#

our options are basically

if left cell has red + 5pt stroke , right cell has blue stroke and the automatic line (i.e. the line coming from #table(stroke: ...stroke here...)) is just dashed (no other stroke properties overridden), then the resulting stroke for the vertical line between the two cells would be:

a) three-way fold between the three strokes: blue + 5pt + dashed (prioritizing right)
b) two-way fold between the cells (completely overriding the vertical line): blue + 5pt stroke (prioritizing right)
c) same as above but resulting in red + 5pt (prioritize left)
d) no folding (prioritize right): blue
e) no folding (prioritize left): red + 5pt

upbeat pine
#

it can't depend on system's things. only if it's fonts or something.

wary knot
#

much like typst does it

upbeat pine
wary knot
#

i mean that when you open the spreadsheet in the spreadsheet app, the horizontal order of cells is reversed, and the spreadsheet grows to the left

#

but when saved to a text file it's in LTR order

upbeat pine
#

you mean the order changes when the spreadsheet is being saved to a file?

wary knot
#

?r

#set text(dir: rtl)
#table(
  columns: 3,
  [a], [b], [c],
  [d], [e], [f],
  [g], [h], [i]
)
wary knot
#

thats what i mean

upbeat pine
#

but that's typst, not spreadsheet/calc

wary knot
#

so

#

what im hypothesizing is that it does the same

#

i dont know if it does, but i didnt test it either

#

anyway whatever

#

let's set that aside

wary knot
#

:p

#

and a related question is

if the user specified table.vline(x: 1, stroke: (dashed properties here)) , the cell at position (0, 5) has a right stroke of red + 5pt, and the cell at position (1, 5) has a left stroke of blue, what would be the effective stroke of the vertical line between the two cells?
(same options)

upbeat pine
#

is "automatic line" a #set table.cell(stoke: (dash: "dashed"))?

wary knot
#

yes

#

wait

#

no

#

misread

#

it'd be #set table(stroke: ...)

#

not #set table.cell

#

#set table.cell behaves the same as overriding each cell's stroke manually (and, of note, it does fold)

upbeat pine
#

but then it's not "automatic", it's the "global default" a.k.a. "table-wide default"

wary knot
#

well

#

to be more specific, automatic is #set table(stroke: dashed) for example, in contrast with #set table(stroke: (x, y) => dashed)

#

the former generates lines on between every column and every row (what I call "automatic lines")

#

the latter doesnt

upbeat pine
#

and what the latter does then?

#

cell-wide stroke?

wary knot
#

but with filtering

upbeat pine
#

yeah

upbeat pine
wary knot
#

no it's intentional

#

the comparison is missing vs present

#

i mean i get what you mean (there are two identical options as a result)

upbeat pine
#

no, I mean the "prioritizing right" thing

wary knot
#

i would need a fourth property to separate them though

#

maybe miter_limit i guess lol

#

but at that point the question would be too complex idk

upbeat pine
#

what you didn't say is if folding mixes colors

wary knot
#

it doesnt

#

otherwise the first one would be red.mix(blue)

upbeat pine
#

well, if you "want to sell folding", I would including the color mixing. otherwise it's not a good folding.

wary knot
#

well i dont make the rules

#

it's how stroke folding works today

#

:p

upbeat pine
#

that's true

wary knot
#

?r

#set table(stroke: red + 5pt)
#set table(stroke: blue)

#table[stuff]
wary knot
#

lol

#

im dumb

#

kk ther

upbeat pine
#

brain malfunction, amiright

wary knot
#

ye

wary knot
#

that it doesnt mix colors

upbeat pine
#

but you don't say to mix them, you just overriding one color with another

wary knot
#

exactly

#

thats how it works

#

stroke folding atm is between missing and present properties

#

so if one stroke specifies some properties and the other specifies some others, the resulting stroke has all of them

upbeat pine
#

and I also implied half-transparent colors, that mixes better, logically, because there is transparency involved.

wary knot
#

but if they have the same properties then one of the strokes just wins

#

in this case it's the innermost set rule

#

in our case we dont know yet (im proposing the one from the right cell)

upbeat pine
wary knot
#

no no

#

it's not that smart

#

just a bit

#

:p

upbeat pine
wary knot
#

lol

#

anyway yeah so basically

#

right now we need to define what happens when there are conflicts

#

of all sorts

#

at one particular line position (between two cells), there can be:

  • automatic line
  • stroke from left cell (or top, for hlines)
  • stroke from right cell (or bottom)
  • explicit line
#

i think im gonna add those questions to the table planning document (been a while since I last updated it :p)

upbeat pine
wary knot
#

right now my mental model is:

  1. explicit line causes automatic line to be completely disabled (so there's never a conflict between the two);
  2. for automatic lines (defined e.g. as #set table(stroke: red)), the two cell's strokes are folded together with the automatic line's stroke
  3. explicit lines always lose to the (folded) cell strokes
  4. two conflicting explicit lines are drawn on top of each other
#

i guess it's not the most consistent thing

#

but there are too many variables lol

#

it could be made more consistent with "fold all the things" mentality and have explicit lines fold between each other and with cell strokes as well

#

we could also error for conflicting explicit lines

#

lol

#

thats boring though

upbeat pine
# wary knot but there are too many variables lol

whenever there are a lot of variables, you have to lock some of them out. also try to keep practicality in mind, something that everyone will use, not some very niche edge case. that can be addressed last, if at all. that's my take/advise.

wary knot
#

yea

#

i was thinking of just completely disabling fold for explicit lines

#

hence my last two choices

#

cuz i think they're just supposed to be overrides for what you expect of the table lines in a sense

#

while the rest (automatic stuff) would use folding where possible

#

but honestly idk

#

this could be too arbitrary

#

i need more opinions

upbeat pine
#

we also can just "see what will happen" and adjust the behavior in next typst version. I mean the default behavior should be good enough for most people at first, and then we can tinker more. a lot of people can post a lot of useful feedback that can substantially simplify the process of coming up with default behavior.

wary knot
#

Ok sΓ³

#

I had an idea of how we could make it more consistent

#

I'm just not 100% sure if it's what the user would expect in general

#

But basically we could make explicit lines fold between each other , with priority to the one specified last

#

And in particular they'd fold with cell strokes and all that

#

Basically all lines would be drawn in a single run

#

And it would be a fold all the things kind of thing

#

Notably I'd need the lists of lines to be sorted for the algorithm to be simple enough

#

But then I'd have to use an integer for priority or something , unless i folded before inserting them into the list

#

Tbh maybe doing it before is wiser

#

Actually , well, it'd still split your lines into many tiny pieces so whatever

wary knot
#

it seems i may have done an oopsie

#

ok well it was mostly just me assigning a really high value to y in grid.hline(y: ...)

#

dont mind me

#

πŸ˜‚

vernal herald
#

Is this in memory? How much ram does your machine posses?

#

Also that's one fat opsie

wary knot
#

i should have expected that

vernal herald
#

Ah okay.

wary knot
#

it werk

#

code:

#grid(
  columns: 3,
  inset: 3pt,
  stroke: (x, _) => (right: (5pt, (dash: "dotted")).at(calc.rem(x, 2))),
  grid.vline(x: 0, stroke: red),
  grid.vline(x: 1, stroke: red),
  grid.vline(x: 2, stroke: red),
  grid.vline(x: 3, stroke: red),
  grid.cell[a],
  [b], [c]
)
#

horizontal lines: soon ℒ️ ...
easy border customization: soon ℒ️ ...
automatic hline / vline positioning: soon ℒ️ ...
basic error checking i really need to do this: soon ℒ️ ...

wary knot
#

or at least, it would be slightly harder

#

cuz now it errors if you try to place the line at an invalid index

#

instead of trying really hard to do it

#

and eating all of your RAM

#

but i mean, grid.cell(y: 293182498)[i hate RAM] would basically cause the same thing so whatever

#

but then that's your fault!

frozen oyster
#

I actually hate that, currently, recursion and lots of calls to small methods causes use ram consumption 😦

frozen oyster
frozen oyster
#

HMMMMMMMMMMMMMMMMMM

wary knot
#

gotta let those functional typsters go brrrrr

frozen oyster
#

Should I? πŸ˜‚

wary knot
#

if it is then... wouldnt be a bad idea

#

but i think it could require Rust support for it in the first place

#

which is currently in nightly

#

(the become keyword)

frozen oyster
vernal herald
#

Someone sent me a link to code once that achieve tail call optimizations by.... Using loops?

pure sundial
#

Because tail call optimisation is converting calls to loop (jmp), if I remember correctly

wary knot
#

Well i guess it depends on how low level the language is but yeah

#

With the VM compiler it could become even easier to just repeat evaluation for some bytecode

#

Though you'd have to make sure the arguments are in the correct registers and whatnot

#

And still , without Rust support it could be a bit useless lol

#

But idk

signal breach
#

That can be used now for tail call optimization on stable. It allows many kinds of useful recursion cases.

#

AFAIK, It's not clear that rust will get TCO due to its impact in debugging.

wary knot
#

ladies and gentlemen...

#

i am proud to announce...

#

horizontal lines πŸ‘

#

huzzah

#

that's basically all of the "hard part" - per-cell strokes and manually-specified strokes are working

#

next up on the list are a few tiny things, including easy border customization and automatic hline / vline positioning

#

and also general cleanup ig lol

upbeat pine
wary knot
#

wdym

#

between horizontal and vertical?

upbeat pine
#

yes

wary knot
#

or the thing i had asked?

#

horizontal lines are drawn first

upbeat pine
#

because...?

wary knot
#

well

#

no particular reason

#

it's just how it is

upbeat pine
#

ok

wary knot
#

someone in tablex had proposed adding a z-index thing for lines

#

it could be interesting to have something like that but we'd need a bit of design discussion

upbeat pine
#

mhm

#

So, has the rowspan been lowered in priority or is it done? or you just taking a break from it?

wary knot
#

well

#

it's paused for now

#

it'll probably need a rather substantial rewrite to accommodate the new algorithm, whichever it is

#

but I still dont know exactly where to go with that

#

plus just fixing a ton of small rowspan things over time was really mentally draining

#

:p

#

so by working on the next PR im at least moving things forward

#

and for some line customization is much more important than rowspans anyway

upbeat pine
#

well.......

#

some would disagree, but whatever

wary knot
#

well yes

#

for some

#

:p

#

and well

#

i hope to get that rowspan work done soon

#

but if it takes too long and release approaches, we can also just release a reduced version of rowspans

#

unbreakable-only

#

and then add breakable later

#

that'd be better than nothing ig

#

lol

upbeat pine
#

I still don't understand this whole business with breakable rowspans (I just never had to deal with it, so idk if I will ever use it), so some rowspan capability is definitely better than none.

wary knot
#

unbreakable rowspans are the tablex ones

#

they are rather simple to implement

#

you just gotta make sure all spanned rows are in the same page

#

and then just layout the cell normally

#

breakable rowspans are much harder

#

this is technically my first time implementing them since tablex doesnt have breakable rowspans

#

(it doesnt have breakable cells at all as a matter of fact)

#

but the idea is that for example you could have a rowspan going through all 100 rows of your multi-page table

#

so that can be interesting depending on your needs

#

but it's hard to implement

#

requires some amount of backtracking and stuff

#

i.e. laying out all spanned rows and then going back and laying out the rowspan

upbeat pine
#

hmm, maybe I saw a breakable rowspans, but I probably never did them myself.

#

hold on, is the unbreakable version already implemented?

wary knot
#

yeah

upbeat pine
#

wow

#

well that's amazing then

wary knot
#

i mean the breakable version is implemented too

#

the problem is that it's not ideal

#

theres the gutter problem i spoke about before

upbeat pine
#

beta version shouldn't be ideal

wary knot
#

of course yea

#

but it has to be fixed before PR'ing

#

and the fix involves changing the algorithm so yea

#

will take a bit ℒ️

upbeat pine
#

I'm interested if people would prefer non-ideal breakable version over none.

wary knot
#

well yea that could be made to work

#

but it would be a breaking change if we fixed it later

#

ig

#

:p

#

well not rlly breaking but it could change your document layout considerably

wary knot
#

ok, we have border overrides now

#

(you can also just use hlines and vlines with index 0 / len() )

#

code:

#set page(height: 5em)
#table(
  columns: 3,
  inset: 3pt,
  stroke: (left: 3pt + green.darken(20%), right: 3pt + yellow.darken(20%), top: 3pt + blue.darken(20%), bottom: 3pt + red.darken(20%)),
  [a], [b], [c],
  [a], [b], [c],
  [a], [b], [c],
  [a], [b], [c],
  [a], [b], [c],
)
upbeat pine
#

will radius come later?

wary knot
#

Well idk

#

Using radius would require using a rect

upbeat pine
#

can't it be copied from a block?

wary knot
#

Which means the border stroke would be uniform

#

Which isn't always the case

upbeat pine
wary knot
#

In fact it probably won't work with gutter yet

#

But it should be easy to fix (just roughly a matter of dividing indices by 2)

upbeat pine
#

I don't understand: if you can customize the outer table stroke, why can't you use a radius on it? is it (radius property) tightly coupled with another elements? isn't the table a block/rect already?

wary knot
#

this is necessary so that cell strokes have priority

#

and so you can override them with manual hlines / vlines

#

a block/rect requires uniform stroke

#

so you cant override it at certain points

#

with that said, we could just say that border strokes have maximum priority and forbid any kinds of overrides on them

#

but idk if that'd be desirable

upbeat pine
#

can't table use a custom radius implementation?

#

or will it be too hard to do?

#

like rounding 2 lines so that they make a round corner.

wary knot
#

uhhh

#

yeah i have no idea how to do that

grand haven
#

If you want you want rounded corners it sounds better to put a rect inside the cell

#

Or put the table inside a rect for the outside

grand haven
wary knot
#

i mean uniform strokes on each side

grand haven
#

Okay, I misunderstood then

wary knot
#

but yeah i think you can use a block for now

#

with table

#

i guess you can "kind of" override strokes here

upbeat pine
upbeat pine
wary knot
#

hmm

#

i mean, right now blocks preserve radius on all pages

#

so tables would (hypothetically) do the same

grand haven
#

There's an issue tracking that I believe

wary knot
#

there could be some parameter to block to control that

wary knot
#

Ok so

#

I'm thinking here that stroke: none should get some love

#

Cuz right now it's just completely useless

#

Setting stroke: none for a cell isn't very useful for example

#

I guess it makes sense if you want to override some set rule or something though

#

But e.g. for hlines or vlines , why would you use a stroke: none when that's the same as not adding anything at all

#

So i could make it so explicit stroke: none wins where appropriate

#

Anyway

#

I think , with that aside, that pretty much all functionality is implemented

#

Idk if there's something I forgot

#

Oh right, gutter

#

Looking into it

#

for per-cell stuff it already works so thats nice

bold bluff
wary knot
#

if we reversed that then there would be a black vertical line on the top border instead

bold bluff
#

I guess I don’t think about this much. I think it would look prettier if the middle hline was behind the others. But I really don’t have a clue how typst handles zorder.

wary knot
#

well

#

right now there are just two for loops in the "render_fills_strokes" function

#

one draws hlines and one draws vlines

#

there isnt that much to it really :p

#

we could in theory make a single for loop by joining the lists of hlines and vlines and sorting them by "priority"

bold bluff
#

Hlines is the second loop?

wary knot
#

im just not sure what would be the most expected thing here

wary knot
#

that is, they are added below other things

bold bluff
#

I have to admit I like the simplicity of it. I had a tablex the other day. And I had overlapping hlines but I couldn’t figure out which lines would be drawn first. It’s more tricky when the x y cell placement is used.

#

It’s one of those things that most people don’t realize or care about until they see something that they don’t expect.

wary knot
#

yea

#

i was thinking that we could somehow make it so that vlines specified after hlines would appear on top

#

when manually specified

#

but

#

im not sure how easy that is to make

#

at least in an efficient way

#

or maybe we should just not care and have a layer parameter and thats it

#

with that said, this isn't an essential thing :p
we can definitely ship without this as needed
this difference is only observed if your stroke has differing thickness

bold bluff
#

Could you just provide a drawing order offset. It gets delayed prepending

#

Ehh now that I read it. It sounds like a hack.

#

Shouldn’t the stroke have a zorder attribute?

wary knot
bold bluff
#

So smart 😎

wary knot
#

at least it's been requested before for tablex

#

but there is some overhead to that approach

#

in that we will have to sort the lines in every page

#

maybe it's not that bad though

#

especially if we use a stable sort

bold bluff
#

How does cetz do this?

wary knot
#

it's worth noting though

#

that frames themselves have a layer mechanism

#

but i wouldnt just expose that directly to the user cuz there'd be the risk of messing with stuff that isnt a line

#

well

#

im not fully sure how borders would interact with this thoug

#

like, should we give borders a higher priority

#

but then what about other kinds of lines

#

should they have the same priority as borders

lone nova
#

What about repeated header of the table? 😁 Is it possible to implement this in upcoming typst release?

wary knot
#

yeah

#

i mean

#

my current plan is to go up to part 5 (repeated headers) for the next release

#

im hoping it will be possible, though it will depend on the speed of previous PRs

wary knot
#

0.11

#

well IIUC we have a few weeks still

#

so there should be time

#

but let's see

lone nova
bold bluff
#

As far as the right way to order lines . I’d be tempted to see how html/css does it. I thought i saw a snippet above somewhere.

#

Not that it’s really the right way.

wary knot
#

and i guess there is also this

#

?r ```
#rect(
stroke: (left: red + 10pt, top: blue + 5pt),
width: 50pt,
height: 50pt,
)

wary knot
#

though i dont think it'd be practical to implement something like that in tables

#

and css doesnt do that, only for borders

bold bluff
wary knot
#

yea thanks, I'm trying πŸ˜‚

bold bluff
wary knot
#

neither is

bold bluff
#

Tricky

wary knot
#

theres like a blending thing in the middle

#

okay

#

i think i see

#

in html borders occupy space

#

this is something we dont do (at least yet)

#

so they dont overlap , rather one goes above the other

bold bluff
#

The vlines for html?

wary knot
#

oh

#

nvm

#

i mean it's true that they occupy space

#

but what they're doing here is

#

largest thickness goes on top

#

on my browser (firefox) at least

bold bluff
#

Ohh that’s actually pretty smart

#

Sort by stroke thickness

wary knot
#

yeah

#

i just wonder if this is generally desirable though

#

but it seems to produce the most pleasing output

#

im just worried cuz this could be a bit inconsistent with the rest of typst

#

blocks and rects do the blending thing, here we dont

#

but theres also not much we can do ig

bold bluff
#

Well it’s one way.

wary knot
#

yeah

#

let's ask for more opinions

bold bluff
#

I’m trying to think when you would want thin strokes above thick strokes. But idk it seems like a pretty good general thing. I’m sure I’ll find something later

#

The thin ones can also be manually placed later.

#

I’m realizing how rare it is to have h/v lines of different colors.

#

A w3c example.

#

Latex doesn’t follow the same rules.

#

But it is more of an example table in both cases.

wary knot
#

im trying to figure out how tabularray handles this

bold bluff
#

It’s truly a gem among latex table libraries.

#

This example is from tabular ray. It has thin dashes over the thick boarder.

wary knot
#

ah yeah

#

hlines on top

bold bluff
#

So far two camps.

  • big borders on top
  • hlines on top
wary knot
#

i cant deny the html one looks a bit nicer

#

but at the same time its stroke affects layout so it's not really the same

bold bluff
#

Anyway I’m sleepy. Hope you find the best solution.

wary knot
#

yeah

#

thanks for the input

#

i think i'll ping the gang ℒ️ for opinions

wary knot
# wary knot ok, we have border overrides now

opinions needed!

atm hlines are always drawn on top of vlines, which can look a bit weird as shown above. So:

  1. should we display lines with larger stroke on top of others with smaller stroke, as HTML/CSS (or at least Firefox) seems to do? That avoids the problem shown above, but is there any situation in which the current behavior could be desirable?

    • ⚠️ In HTML/CSS, stroke thickness affects layout, which isn't the case in Typst, which could impact the answer.
    • Tabularray (LaTeX) seems to do the same as Typst (hlines on top).
  2. should we add smth like a layer (or z-index but that's ugly) param to (grid/table).(hline/vline) to let users customize the order in which lines they specify are drawn?

    • Or should we maybe, instead, have lines always be drawn in the specification order (so specifying a vline after an hline in the table params causes it to be drawn above that hline in particular, but not on top of hlines coming after it)?
      - This other approach could perhaps not play so well with automatic positioning of lines.
    • Or we can just not care/postpone.

also, some other things we have to decide:
3. What about gutter? Rn you can customize just a gutter line by placing a line between 2 cells and covering with 2 lines on top of both ends. But I thought about a gutter parameter to the stroke option, which would accept a stroke type, an array of columns, or (x, y) => stroke where x, y are the gutter's position relative to other gutters (so like (0, 0) would be the 1st gutter cell, (1, 0) would be the 2nd gutter cell in that row..). Thoughts?

  1. Should a cell with stroke: none or (left: none) or smth (not unspecified stroke) interrupt lines around it? (Rn stroke: none is useless on cells, so I thought of maybe making it useful.)
    • What about explicit lines with stroke: none? Maybe that could be used to remove some default strokes, making that behavior opt-in instead of forcing it.
wary knot
#

cc @robust viper @keen crescent @upbeat pine @grand haven (and others who could want to join in)

keen crescent
#

I'll probably need to catch up on the rest of the stuff to give a better opinion; haven't been keeping up due to some personal issues

wary knot
#

i was thinking that some way to disable one default stroke but not all could be very useful tho

#

but maybe this is like "backwards thinking" a bit

#

rn tablex just completely removes a default stroke if you specify a line over it

keen crescent
#

What do you mean by 'default stroke?'

wary knot
#

like

#

?r ```
#table(
columns: 2,
[a], [b],
[c], [d]
)

wary knot
#

those strokes

#

if you specify your own stroke at the second column (between the two cells), tablex removes the black line in the second column

keen crescent
#

Ah, I misread slightly, I thought you were talking about strokes on lines

wary knot
#

of course that makes sense if your line covers the entire thing. but what if your line is restricted to a small part

#

hm

#

actually

#

i mean if it's restricted to a small part then you probably just want to affect that part anyway

#

so

keen crescent
#

I think it could be awkward if you just remove stroke on all boundaries of a single cell but keep other cells with parts of their strokes

wary knot
#

oh that wouldnt happen

#

but still

#

uhm

#

i subtly kinda changed the topic which may be causing a bit of confusion

#

but still I agree with you

#

more generally, how about we add something like

#

stroke: (horizontal: none, vertical: x => (red, blue).at(calc.rem(x, 2)))

#

does that sound useful?

#

cuz i mean technically you can do the same with cell strokes

#

so it could be "redundant" in a sense

keen crescent
#

so remove the stroke on the horizontal, but keep it on the vertical?

wary knot
#

yeah

#

or keep it with some function or something

#

wait

#

important

keen crescent
#

I see that kind of as a hacky way of creating a row/colspan

wary knot
#

here i mean table(stroke: ...)

#

yea sorry

#

although that will be possible anyway

#

lol

#

but

#

i mean like table-wide

#

like tablex's auto-vlines: true/false, auto-hlines: true/false

keen crescent
#

Ah

#

I thought we were still talking about cells

wary knot
#

yea sorry i forgot you cant read my mind

#

dumb me

keen crescent
#

Table wide I definitely can see that being useful, if redundant with auto-h/vlines

#

Very useful

#

Is there an implementation of that feature from tablex in base yet? I think I remember that being in discussion

wary knot
#

in base
wdym?

keen crescent
#

You mentioned auto-h/vlines in tablex, but not in base typst (I don't remember if it's already there or not, I might be being dumb)

wary knot
#

oh

#

yeah base typst barely has any stroke customization

#

rn

#

what im doing rn is adding all the stuff related to that

keen crescent
#

Ah ok

wary knot
#

note that in the future you'll be able to achieve more or less the same with

table(stroke: (x, y) => (left: (red, blue).at(calc.rem(x, 2)), right: (red, blue).at(calc.rem(x + 1, 2))))

keen crescent
#

I wonder if disabling the lines at the stroke level versus the line level is the better way to go though

wary knot
#

it's a bit subtle

#

the main difference is that the default lines cross gutter

keen crescent
#

Stroke level brings up bad memories of tables in ms-word

wary knot
#

lol

#

yeah i was thinking about making the API as flexible as possible for both usecases

#

i want to use lines vs i want to use per-cell stroke

keen crescent
#

Understood, I think that's reasonable

wary knot
#

except that it doesnt affect your own explicit lines (they always override the table-wide thing)

#

which i think would be consistent

#

either way

#

i wont add this for now, just bringing it up

#

we can very well have a PR without everything and later just add more stuff as needed

#

next thing ill implement will be a pos parameter to explicit lines

#

akin to gutter-pos in tablex

#

cuz you can be either to the left or right of gutter

wary knot
#

i only pinged you cuz you seem to be interested in general, but please dont feel forced to respond, we all have our own stuff so it's fine πŸ™‚

wicked totem
#

Hi @wary knot - in the new Table api, can the row height be specified?

wary knot
#

You'd write rows: (1em, 2em, 3em) for instance

upbeat pine
# wary knot **opinions needed**! atm hlines are always drawn on top of vlines, which can lo...
  1. idk if this would improve output of all use cases, so maybe no.
  2. this would be very cool as it looks like it provides a very granular settings which is good for typst
  • order of specification could be nice as an alternative option, this way your tables will be less verbose, but for complex output a layer thing should be used
  1. but gutter is an empty space between cells, not a stoke line
  2. probably. I think the priority is obvious: table < vhline < cell. so if cell doesn't want a stroke around it, it won't have it. but idk about adjacent cells with different stroke settings. probably a cell with visible stroke should win.
robust viper
# wary knot **opinions needed**! atm hlines are always drawn on top of vlines, which can lo...

I think 2 is overkill for now, I would not care/postpone. I don't really understand the relative part in the idea of 3. Overall, I'm not sure how often gutter will be used with table and how much work we should invest in it. It mostly exists because it is useful for grids. 4 sort of depends on how we view strokes and lines: Are they the different interfaces that configure the same thing or are they separate things?

lone nova
#

I'm basically of the same opinion. The only thing is probably better to have a layer than to adhere to the input order. The input order can become unreadable very quickly, especially with complex tables.

bold bluff
#

I like being able to fiddle with the layers. But I also like a smart default I would prefer thick lines on top for most situations. I don’t think inputs order is valid because you could use xy placements. And that could cause overlapping artifacts between adjacent cells.

upbeat pine
#

the input order would work for simple tables and it will make tables less verbose.

wary knot
#

thanks for the feedback everyone!

wary knot
#

regarding 4: that's basically what im doing right now, though ignoring stroke: none

wary knot
#

regarding 3: i meant basically having a separate coordinate system for gutters, i.e. a "gutter cell" at (x, y) in that system would correspond to real internal cell (2*x - 1, 2*y - 1)

#

but yeah i mean i just thought of having a separate way to configure gutter lines, but it's already possible to do that in a way so it's not that bad

#

regarding 4: right now they are basically the same thing

#

ive made everything fold, in order of priority, so cell strokes fold with lines at the same position and then fold with global stroke at the same position

#

but the fold ignores none, so it's only none at a position if all of those are none

robust viper
#

same with gutter. It shouldn't have extra API for now imo because the use cases are still (at least to me) rather unclear.

wary knot
#

gotcha

upbeat pine
wary knot
upbeat pine
#

ahhh, I thought something like this:

#

so something like "lines that intersect/overlap gutter"

#

#1182377294682128445 message:

Rn you can customize just a gutter line by placing a line between 2 cells and covering with 2 lines on top of both ends.
so "a gutter line" is that part of a line that overlaps gutter? what is the "line" in "by placing a line between 2 cells"? and which "lines" are in "covering with 2 lines on top of both ends"?

#

is this "a gutter line"?

wary knot
#

The idea is that you'd draw a line between the top b and the bottom b

#

And then place a line on the top b only, and on the bottom b only

#

That way you'll have changed the gutter line

upbeat pine
#

draw a line between the top b and the bottom b
this?

#

or this?

wary knot
#

And then add right stroke to both b's

#

What's left is the gutter line

upbeat pine
#

so you don't actually draw a full-length vline? you can do \cline{1-2}?

wary knot
#

Yeah you can

#

It's optional though

upbeat pine
wary knot
#

Yeah

#

To customize just the gutter line

#

But as Laurenz said, it would probably be a very rare use case

upbeat pine
#

so what about the "gutter cell" thing? I don't understand how can you navigate the indices here...

upbeat pine
#

so making lines that overlap gutter customizable is overkill, at least for now for sure.

wary knot
#

But yeah. I agree it'd be a bit convoluted

#

So I'll leave it out

wary knot
upbeat pine
wary knot
#

Yeah it'd need further design discussion to be viable

#

So I don't think it's worth it

#

Ok so

#

Regarding thickness

#

I'm thinking of implementing the sorting based on thickness solution

#

Cuz then there would rarely be any complaints about layering

#

Maybe if you use a pattern stroke

#

But that's about it

wary knot
#

ok

#

here's a pro tip for you guys

#

one of the best ways to have ideas is called taking a shower

#

πŸ˜‚

#

ok so i think i've figured out what we should do regarding stroke: none

wary knot
#

basically here's what im thinking

#
  • table.hline(stroke: none) (likewise for vline) will completely delete lines under it. But not cell strokes (mainly cuz they have priority).
#

cuz the idea is to override the default lines.

#

But

#
  • table.cell(stroke: none) will not delete anything
#

it just will make sure that particular cell has no stroke override

#

but it wont affect the lines

#

i think this makes more sense

#

because then it's easier to say "I want to delete this line" by just adding a table.hline(stroke: none)

#

otherwise hlines and vlines with stroke: none would just be 100% useless lol.

#

i think this would be consistent with being able to disable lines at the border with stroke: (top: none) for e.g.

#

they'd all just be "lines"

#

i also advanced a bit in my mental model of the new rowspans algorithm

#

but the work is still being done

#

all i can say (without confusing everyone) is that I've thought of one possible way to start work on it

#

but i havent thought of it to its completion yet

#

perhaps another conversation with Laurenz might be in order , as things become clearer πŸ™‚

#

and finally

#

I had some ideas regarding headers and footers

#

just to share with you guys, right now im thinking of something like this

#

simplest case:

#table(
  columns: 2,
  table.header(
    table.cell(colspan: 2)[*Super Header*],
    [*A*], [*B*],
    [bye],
    repeat: true
  ),
  [data], [data],
  [data], [data],
  [data], [data],
  table.footer(
    [*C*], [*D*],
    table.cell(colspan: 2)[*Super Footer*],
    repeat: true
  )
)
#

that would be the basic API at least

#

but I had the idea of allowing headers and footers in arbitrary positions

#

so

#

simplest case:

#table(
  columns: 2,
  table.header(
    table.cell(colspan: 2)[*Super Header*],
    [*A*], [*B*],
    [bye],
    repeat: true
  ),
  [data], [data],
  [data], [data],
  [data], [data],
  table.header(
    table.cell(colspan: 2)[*New Header Just Dropped*],
    [*A*], [*B*],
    repeat: true
  ),
  [data], [data],
  [data], [data],
  table.footer(
    [*C*], [*D*],
    table.cell(colspan: 2)[*First Footer*],
    repeat: true
  )
  [data], [data],
  [data], [data],
  table.footer(
    [*C*], [*D*],
    table.cell(colspan: 2)[*Second Footer*],
    repeat: true
  )
)
#

the idea is that the first header is "sticky" (repeated) until the second one, which is sticky until the end
while footers work a bit differently - the first one that appears is "sticky" from the start until when it appears, and the second one is sticky from there until (coincidentally, as that's where it appears) the end

wary knot
#

hell yeah it werk

#

now you can specify hlines on top of or below a row (useful when you have gutter)

bold bluff
#

So i could make a weave pattern with the lines? I cause that would be a very rare use case.

wary knot
#

huh

#

no it's just so u can actually place a line there

#

(see the green arrow)

#

by default lines always go on top of rows / to hte left of columns

#

so the bottom of a row / right of a column were impossible* to customize with gutter

#

*you can use cell stroke but then you cant make it cross gutters or use it to override the default lines

bold bluff
#

is that the cell line/ cells lines

wary knot
#

no

#

it's a table.hline

bold bluff
#

what code?

wary knot
#
#table(
  columns: 3,
  gutter: 3pt,
  stroke: blue,
  table.hline(end: 2, stroke: red),
  table.hline(end: 2, stroke: aqua, position: bottom),
  table.vline(end: 2, stroke: green), [a], table.vline(end: 2, stroke: green), [b], table.vline(end: 2, stroke: aqua, position: right), table.vline(end: 2, stroke: green), [c], table.vline(end: 2, stroke: green),
  table.hline(end: 2, stroke: red),
  [d], [e], [f],
  table.hline(end: 2, stroke: red),
  [g], [h], [i],
  table.hline(end: 2, stroke: red),
)
#

well

#

i didnt realize that an unintended consequence of automatic positioning is that you need to specify position: bottom to place the bottom line when you use gutter

bold bluff
#

I thought that was interstesting.

#

I almost thought it should go at the last row of the table.

wary knot
#

uhhh

#

actually thats weird

#

the last line just disappeared

#

i set it to green and nothing changed

#

lol

#

yeah no it's just a bug

#

gotta figure that out

bold bluff
#

if i place an xy cell before the below does it go below that cell.

wary knot
bold bluff
#

πŸ™‚

wary knot
#

I kept automatic positioning very simple

#

the index of the line will be right after the cell placed last

#

so

#

to its right / on top of the row below it

#

so if u give it position: bottom it will go below the row below

#

and position: right would go after the column after

bold bluff
#

so the default positions are left and top. for vlines and hlines respectivly

wary knot
#

yes

bold bluff
#

Its an acceptable choice.

wary knot
#

for some reason gutter is making lines at maximum index disappear

#

lol

#

that's odd

bold bluff
#

although the position is more like "next" than below/right.

wary knot
#

yea sort of

#

but it makes more sense when you dont rely on automatic positioning

#

when you explicitly specify the index

#

then it truly becomes before the index you specified / after it

bold bluff
#

does the right/below work with col/row/spans