mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
6ea4119a3ceb36f009af1486e41b47f08c2239bd
This commit ensures that any given paragraph of text shaped by Gio will use a single internal line height. This line height is determined (by default) by the text size, rather than the fonts involved. This is a breaking change, as previously we would blindly use the largest line height of any font in a line for that line, leading to lines within the same paragraph with extremely uneven spacing. This commit also updates some test expectations in package widget. I thought pretty hard about how to implement line spacing, and consulted a few sources: [0] https://www.figma.com/blog/line-height-changes/ [1] https://practicaltypography.com/line-spacing.html [2] https://developer.mozilla.org/en-US/docs/Web/CSS/line-height There is no single, universal way to think about line spacing. Fonts internally specify a line height as the sum of their ascent, descent, and gap, but the line height of two fonts at the same pixel size (say 20 Sp) can vary wildy (especially across writing systems). There are two strategies we could pursue to establish the line height of a paragraph of text: - derive the line height from the fonts involved (our old behavior, and the behavior of many word processors) - derive the line height from the requested text size provided by the user (the behavior of the web). The challenge with the first option is that for a given piece of text in the UI, there can be a silly number of fonts involved. If a label dispays user-generated content, the user can put an emoji in it, and emoji fonts have different line heights from latin ones. This can cause unexpected and nasty layout shift. Gio would previously do exactly this, on a line-by-line basis, resulting in unevenly spaced lines within a paragraph depending on which fonts were used on which lines. Choosing one of the fonts and enforcing its line height would make things consistent, but it isn't clear how to choose that canonical font. There is no 1:1 mapping between the input text.Font provided in the shaping parameters and a single font.Face. Instead, that mapping depends upon the runes being shaped. I think the only sane way to implement the first option would be to synthesize some text in the provided system.Locale (mapping the language to a script and then generating a rune from that script), shape that single rune, and then enforce the line height of the resulting face on the entire paragraph. This would require doing a fair bit more work per paragraph than Gio does today, so I've opted not to do it. Instead, the second option allows us to choose a line height based on the size of the text that the user wants to display. While this can potentially interact poorly with unusually tall fonts, it means that text will always have a consistent line height. I've provided two knobs to control line height: - text.Parameters.LineHeight lets you set a specific height in pixels with a default value of text.Parameters.PxPerEm. - text.Parameters.LineHeightScale applies a scaling factor to the LineHeight, allowing you to easily space out text without hard-coding a specific pixel size. The default value here (drawn from the recommendations of [1]) is 1.2, which looks pretty good across many fonts. I've chosen this two-value API because many users will want to set one or the other value. I considered instead a single value field and a "mode" that would specify how it was used, but that felt uglier. Also, you *can* set both of these two fields and get predictable results. I'd like to revisit using the line height of the chosen fonts in the future, but it seems a little too complex to be worthwhile right now. An interesting option would be making the select-a-face-using-locale strategy described above an opt-in feature, though some users might instead want to just use the tallest line height among fonts in use. Something like this Android API might be appropriate: [3] https://learn.microsoft.com/en-us/dotnet/api/android.widget.textview.fallbacklinespacing?view=xamarin-android-sdk-13 I'd like to thank Dominik Honnef for some good discussion around this feature, and for pointing me to some good sources on the subject. Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
Gio - https://gioui.org
Immediate mode GUI programs in Go for Android, iOS, macOS, Linux, FreeBSD, OpenBSD, Windows, and WebAssembly (experimental).
Installation, examples, documentation
Go to gioui.org.
Issues
File bugs and TODOs through the issue tracker or send an email to ~eliasnaur/gio@todo.sr.ht. For general discussion, use the mailing list: ~eliasnaur/gio@lists.sr.ht.
Contributing
Post discussion to the mailing list and patches to gio-patches. No Sourcehut account is required and you can post without being subscribed.
See the contribution guide for more details.
An official GitHub mirror is available.
Tags
Pre-1.0 tags are provided for reference only, and do not designate releases with ongoing support. Bugfixes will not be backported to older tags.
Tags follow semantic versioning. In particular, as the major version is zero:
- breaking API or behavior changes will increment the minor version component.
- non-breaking changes will increment the patch version component.
Description
Languages
Go
89.6%
C
7%
Java
1.7%
Objective-C
1.6%