Better abstraction for dealing with font styles and variants
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 161
Description
## What problem does this solve or what need does it fill?
Currently the way text styles in Bevy work is that you have to supply a font handle for each span of text. If you want to have a "bold" or "italic" section, you have to choose a different font resource, one that represents that particular style. Worse, not all fonts work this way, "variable" fonts are able to encode multiple styles in a single resource. So the usage is inconsistent - the code will be different depending on what type of font asset you have.
In CSS, there's a layer of abstraction between the raw font files and the available text styles, which is provided by the `@font-face` rule. This is used to register each font family name and style flags in an internal database.
When you specify a font style in a CSS rule, you give it the font name, and optionally style flags such as bold, italic, oblique, and so on. These parameters are then used to query the internal database of font faces that have been registered, and select the proper font resource.
## What solution would you like?
The idea would be to define a new type of asset which corresponds to the CSS `@font-face`. This could be a JSON or RON file which contains a list of references to font files (possibly using relative asset paths), as well as the various style flags. So something like:
```json
[
{
"font": "./Rubik-Medium.ttf",
"weight": 400,
},
{
"font": "./Rubik-Bold.ttf",
"weight": 700,
}
]
```
When this asset is loaded, the resulting asset handle could be supplied to the text span instead of a raw font handle. The text span would have style properties (weight, italic, etc.) that would be used to select the font during rendering. Alternatively, the lookup could be done when the span is created, caching the actual font handle in a private field.
This system would also support variable fonts. It might not be quite as automatic as CSS, I can envision that it might be necessary extra properties to the font-face resource to give hints to the Bevy renderer that this is a variable font.
## What alternative(s) have you considered?
There's a couple of different approaches that could be taken:
1) Leave TextSection alone, and instead have a global resource that produces a Font handle from a FontFace handle. In other words, the code creating the text span would be responsible for calling the method that looks up the font face.
The problem with this approach is that I don't think it will handle variable fonts. To get bold or italic out of a variable font, I suspect you need something more than just a font handle - you likely need some additional rendering parameters which need to be stored in the text span.
This of course assuming that Bevy supports variable fonts at all.
2) Store only the font name in the text section, or in the Style object, rather than a font-face handle. This is more like the way CSS works. However, this now requires some sort of global registry of fonts that is associated with the Style or with the UI node tree.
## Additional context
To make migration easier, we could have a function which takes a raw font handle, and returns a handle to a minimal font-face object. Users who are migrating could create an asset meta file that allows a raw font file to be loaded as if it were a font-face asset with only a single font variation. This would allow them to use their existing assets unchanged.
Contributor guide
Assessment
This issue has not been assessed yet.