-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Curly and colored underlines #1145
Comments
(As per kovidgoyal/kitty#226 (comment), mintty decided that |
If someone wants to try put a PR together for this you can search the repo for "underline" to give you an idea of what has to happen. Basically support the escape sequence, enable storing it in the buffer and then support drawing it using the renderers. |
As an aside, I note that both neovim and tmux now fully support undercurl in current releases as of this writing. It's pretty great when used with coc.nvim for LSP support, plus the appropriate undercurl highlight definitions. |
We should implement this, its very useful for editors, debuggers etc. 😸 There are 3 gems hidden here though:
|
Really? Even two years ago, when I played with it, Firefox and Chromium supported curly and colored underlines. (Chromium's one is much uglier, though. It can only draw entire periods, the last, partial period of the curly underline is completely omitted. It also stops and restart the curly underline on every DOM element boundary.) Maybe even for browsers you could resort to some background image-like drawing? :) Re bits: I've updated the original post. |
Yeah, well its a mixed picture. I tested it with dashed/dotted, which leads to really ugly output or no underlines at all. Support for curly might be better. This needs some testing beforehand, still my guess is that we will have to resort to "drawing-by-hand" here to avoid box borders artefacts and such.
Thx for clarification. Yeah the attribute storage is an issue of its own, I tested several different approaches during the buffer rework. My favorite approach, storing them in a separate RB tree, led to lousy performance due to the needed additional tree lookup, while storing them in continous memory in the cells array itself is much much faster. Memory sacrifice for performance, as always. But with the underlines I wonder, if we should go that path again for a simple reason - underlines are very rare, thus the memory is wasted most of the time. Therefore a bit-notion in the cell and the additional lookup into a foreign storage might be sufficient here. Edit: I wonder if the reduced underline palettes in VTE will create you color matching issues later on - the colors will slightly differ due to the reduced resolution, thus ppl cannot use FG/BG coloring tricks to temporarily hide those lines. Unlikely and rather an edge case, I know, but still a valid assumption from the sequence definitions. |
FYI, there's a Chromium bug related to fixing the really poor rendering of So theoretically, it might be possible to avoid a custom renderer (for Chrome*, at least) if someone could be prodded into fixing that issue. A cursory check via MDN's text-decoration page in FF vs Chrome shows that Firefox's wavy underline rendering is way better than Chromium's. Does FF also exhibit box boundary problems? |
Clarification: there's a good argument based on use cases like this one that wavy underline should render the wave angle based on a notion of absolute/viewport coordinates(?), not box-relative coordinates. If FF "does the right thing" on that point, it's additional ammunition to make that argument re: the Chromium issue. |
Whoever uses underline color = bg color to hide the underline, rather than disabling it: screw them! :) (I mean: you have a valid point, we haven't thought of it. I guess we'll extend to 8+8+8 bits if it really becomes an issue.) This leads us to the next question: if the underline crosses the letter, whose color should matter? VTE draws the underline fist, followed by the letter, so that the letter's color wins. That is, an underline color that equals to the background is a no-op (except for the color reduction). If we picked the other drawing order, it would be different. Followup question: what to do with skip-ink? It would not only be a PITA for us to implement, but I don't even like the end result, I prefer not to skip. :) All up to you to decide, I guess.
The announcement article you linked has a section "Joining across element boundaries", what it demonstrates seems to require pretty much the same architecture as the one needed for continuous undercurl. Let's hope they'll address it soon. Another possibility could be if CSS had a text-decoration-underline-wavelength, or Chrome chose the font width (at least for monospace fonts), in that case the current problems its implementation has would not be visible with monospace fonts. Of course these are unlikely to happen. |
Haha yepp, still ppl gonna do funky things and think it was a smart move ;)
Wasnt even aware of the skip-ink capability. Well both questions dive deep into typesetting realms, which I have no educated opinion about - can only tell you if something pleases my eyes lol. How would a typesetter put a colored, not "skip-inked" underline here, above or below? I cannot really answer that, in hand-written documents an underline would end up above most of the time (ppl would write the text first and underline afterwards, not the way around, but this is more a technical restriction not knowing the runwidth beforehand). Which gives the impression that the underline has a stronger meaning (strikes through letters), where an underline below is a weaker metaphor (letters strike through the line). Skip-ink looks indeed nice, I think ppl would use that mostly for the looks in the hand-written case (strike-through always looks intrusive). Thus the stronger vs. weaker metaphor might not be intended at all. Transferred to the static nature of a terminal - I think below is the better way to handle it (if skip-ink is no option) to not disturb the letter flow to much. Skip-ink seems to be the holy grail here (for my eyes at least), but since we are used to underlines touching letters (+20ys staring at word documents or hyperlinks in browsers), not a big deal. Not sure how you implemented the glyph output in VTE, maybe a matrix transformation adding a hard glow/shadow could be used as erase mask? In general I think below is just fine. If we care enough we might look into skip-ink. Just abit scared that it might open the next can of worms. About the buffer needs: I think we should put this information into a separate storage, something like
Only downsides I see are the higher implementation needs (all cell actions must respect it) and higher memory and runtime costs for a cell, that holds those attributes (rare case). Gonna try to do a PR addressing the core changes first. |
I agree. The question reminds me of the "end of town" road signs in my country where usually the diagonal strikethrough is in the front, sometimes making it non-obvious what the exact name of the town is.
We don't have skip-ink in VTE, we just draw the underline first, followed by the letter. But if we were to implement skip-ink, we'd probably print the underline, then a boldified version of the letter with the background color (or the letter a couple of times, at small offsets in different directions from the desired position, which I think is effectively the same as your matrix idea), then finally the letter as desired.
Like, for example: especially if the underline is wider than 1px and the underline crosses the letter at a sharp angle, the erase mask approach results in pointy underline ends. An even nicer looking result could imitate hand-drawing and lifting up the pen, by making the end always a rounded semi-circle. Probably extremely hard to implement; needs to be done inside the font renderer, or with the rendered variant at a higher resolution. And might not make too much sense, unless the letters in the font also imitate hand drawing. Let's quickly close this can of worms :) |
How about flourishes at the beginning and the end? We also have a strong need for medieval majuscles in the terminal. 🤣 |
Definitely! 😉 |
Made a quick hack on the DOM renderer with #2751 to get a first impression. Well, thats what I get in Chrome: In comparison to FF: Urgh, thats really bad for both. While FF still manages to output something useful (only shows box border artefacts) chrome is totally off. It cannot render curly/dashed/dotted reliably depending on the selected font size. A nightmare... The CSS classes use So thats where we start from with the DOM renderer, not quite promising... |
I was a bit afraid of that from rather less in-depth testing, just faffing with the MDN |
@jwhitley What Chrome does is def. too unreliable, not showing the wavy line for certain font sizes is a full showstopper, the artefacts in dashed and dotted are also bad. FF does alot better, still the box border issues would lead to very poor output and should not be used. I think FF would do alot better if we would not put every char into its own box, but if we change that in the DOM renderer, we will re-introduce old grid-alignment bugs. To me it seems we cannot get this working with CSS styles. But before resorting to self-drawing - feel free to mess around with the DOM renderer and PR #2751, maybe you have a better idea how to get away cheaper. |
@jerch maybe we should shift the underline down a little if the user has lineHeight > 1? |
@Tyriar Yes looks like. Well I have currently no time to work on this, so things have to wait. Ofc if anyone wants to step in, that would be great. Still gonna add myself to this issue to not lose track. |
Fixed kitty docs link: https://github.com/kovidgoyal/kitty/blob/master/docs/underlines.rst |
Test echo for colors: echo -e '\x1b[4;58:5:203mred underline (256)\x1b[0m \x1b[4;58:2:0:255:0:0mred underline (true color)\x1b[0m' |
This is originally a feature of Kitty, now also adopted by VTE (GNOME Terminal and friends). Technically two separate features, but they mostly make sense together, e.g. for spell checking.
Apparently vim and neovim have already / are about to support these, see e.g. vim undercurl, vim color, neovim.
The new
SGR 4:3
(\e[4:3m
) attribute, strictly with a colon as separator, was introduced to start a curly underline.In the mean time,
4:0
,4:1
and4:2
were also added as aliases for the standard24
(turn off all kinds of underlining),4
(single underline) and21
(double underline), respectively.At some point in the future, probably
4:4
and4:5
could also stand for dotted and dashed underlines in some order (these are the five types of underlining supported by HTML/CSS).The new
SGR 58
and59
sequences specify the color of the underline, following the pattern of38
and39
. That is,58;5;idx
for an entry of the 256-color palette, or58;2;r;g;b
for direct RGB. There's no shortcut notation for the first 16 entries (corresponding to SGR 30-37 and 90-97), use the 256-color mode with indices of 0-15 instead.59
reverts to the default, that is, the underline's color auto-following the text color.In case you're short of bits, I believe it's okay to drop some precision, e.g. store only 4 bits per color channel. We were also considering this in the VTE bug.
Update: In VTE we ended up approximating the truecolor underline to 4+5+4 bits (R, G, B respectively). This means: 8+8+8+1 = 25 bits for foreground (in addition to truecolor, we have to be able to denote palette and default values too). Same for background. 4+5+4+1 = 14 bits for underline (again, an extra bit to denote palette and default values). That's a total of 64 bits for the colors, stored in an int64 (bold, italic etc. are additional bits are next to this in another variable). With multiplication instead of bitpacking, it could have been 5+5+5 bits for the underline. Of couse it's not a necessity to put all the color and no other attributes in the same integer, we just decided it's nice this way (and results in some performance improvement).
The text was updated successfully, but these errors were encountered: