Skip to content

Conversation

@ickshonpe
Copy link
Contributor

@ickshonpe ickshonpe commented Aug 21, 2023

Objective

Replace the font_size field of TextStyle with an enum similar to Bevy UI's Val type enabling users to set responsive font sizes that depend on the size of the viewport or the text's parent container.

Fixes #9525

Solution

Replaces f32 font size with a FontSize enum. Currently implemented variants:

  • Px: size in logical pixels,
  • Vh: size in percentage of viewport height,
  • Default: use default font size contained in the new DefaultFontSize resource.

Only updated text.rs example so far, want feedback on the API etc before rewriting every example.

Many problems left to solve:

  • Expect a lot of bugs with scale factor and Text2d.
  • The hello bevy text in the text example scales correctly when you resize the window but almost immediately hits the font atlas limit.
  • Should there be Vw, VMin and VMax variants, or is only Vh really useful?
  • How should percentage values be implemented?
  • Is using Default for the name of an enum variant bad style? Maybe should just go with an explicit UseDefaultFontSize.

Implementation is working though, small window with FontSize::Vh(50.):
font_size_small

Larger window with FontSize::Vh(50.):
font_size_big


Changelog

  • Added FontSize enum with Px, Vh and Default variants.
  • The font_size field of TextStyle is now a FontSize.
  • Added DefaultFontSize resource. Also implemented as an enum with Px and Vh variants but no Default variant.

Migration Guide

The font_size field of TextStyle is now a FontSize. The FontSize::Px variant is the equivalent to setting an f32 value previously.

* `Px`: size in logical pixels,
* `Vh`: size in percentage of viewport height,
* `Default`: use default font size contained in the new `DefaultFontSize` resource.

Only updated `text.rs` example so far, want feedback on the API etc before rewriting every example.

Many problems left to solve:
* Expect a lot of bugs with scale factor and Text2d.
* The `hello bevy` text in the `text` example scales correctly when you resize the window but almost immediately runs out of font atlases.
* Should there be `Vw`, `VMin` and `VMax` variants, or is only `Vh` really useful?
* How should percentage values be implemented?
* Is using `Default` for the name of an enum variant bad style?
@alice-i-cecile alice-i-cecile added A-UI Graphical user interfaces, styles, layouts, and widgets C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Aug 21, 2023
@IceSentry
Copy link
Contributor

Is there a reason why you aren't using the Val enum here?

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible D-Complex Quite challenging from either a design or technical perspective. Ask for help! and removed C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Aug 21, 2023
@ickshonpe
Copy link
Contributor Author

ickshonpe commented Aug 21, 2023

Is there a reason why you aren't using the Val enum here?

  • It's not clear if all the variants of Val would make sense or be useful for text. Even if they do, we might add other variants to Val in the future that don't (or FontSize variants that don't make sense for Val).
  • Val doesn't have a Default variant. Maybe we could use Auto but might be ambiguous.
  • Even if all the variants of Val do make sense for text, the behaviour is probably going to be quite different and sharing the same docs for both could be confusing. The types need different helper methods as well.

@ickshonpe ickshonpe changed the title Replace the f32 font_size value of TextStyle with a FontSize enum Replace the f32 font_size value of TextStyle with an enum value Aug 21, 2023
@alice-i-cecile alice-i-cecile self-requested a review August 21, 2023 18:17
Copy link
Contributor

@bushrat011899 bushrat011899 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I like the idea of trying to convert parameters with implicit units into types with explicit ones.

/// The default font size.
/// Used when the size field of TextStyle is set to Default.
#[derive(Resource, Clone, Copy, Debug)]
pub enum DefaultFontSize {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this instead be a new-type over FontSize, with error handling to warn about a recursive definition? It would remove a few lines worth of repetition.

#[derive(Resource, Clone, Copy, Debug)]
pub enum DefaultFontSize {
/// Default font size in logical pixels
Px(f32),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be any notes here about reasonable values? e.g., negative values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's implicit that negative values would be nonsense but we could add some sort of error message. In main atm if font_size is set to a negative we just fail silently and draw nothing. Perhaps there should be a warning there too.

@mockersf
Copy link
Member

I would be very happy to see vh for font size, but I'm not so sure about adding DefaultFontSize

@alice-i-cecile
Copy link
Member

I'm feeling similarly iffy about DefaultFontSize. It's a very implicit global override, without a clear use or unification. Global theming should probably be a thing, but I'd want something more cohesive than scattered resources for some but not all properties.

@ickshonpe ickshonpe marked this pull request as draft August 31, 2023 11:00
@ickshonpe
Copy link
Contributor Author

ickshonpe commented Aug 31, 2023

Yep this PR is very experimental. I didn't think about it too much and hacked together this implementation to identify the potential problems. It's an important feature.

Agree that DefaultFontSize is a deadend. Should be part of global theming like you say.

The main blocker is how we create a new texture atlas for every different font_size even if there is only a fraction of a pixel difference. I think we need some combination of:

  1. Scaling of glyphs instead of creation of a new font atlas when the sizes are close
  2. Primitive garbage collection that disposes of font atlases with sizes no longer in use
  3. Draw text that uses the responsive size variants using distance fields

None of which seems that difficult to implement but I don't have unlimited time or that much experience to draw on.

(1) is conceptually easy but the implementation details might be very tricky (like how to determine whether the fonts are close enough, what if we have multiple windows each with a different scale factor?).

(2) Looks like it could be hacked together quite easily, just delete the oldest font atlas that isn't in use if we go past the font atlas limit. Perhaps this should be part of the changes to assets though, I'm not sure?

(3) I thought this would be challenging but not so sure now. There is plenty of prior art on how to render sdf fonts. It might be the simplest approach.

@alice-i-cecile
Copy link
Member

This PR would fix #5471 if merged.

@me-self
Copy link

me-self commented Sep 7, 2023

As a response to the third point I could see practical use for Vh, Vw, and Vmin. Those being the following:

  • Vh; is the smaller of the two in most cases. If the software is meant for a landscape orientation, this works well as the text will be certain to always fit on the screen.
  • Vw; this can be useful for windows that are taller than wide (such as they would be on phone) as this would be the smaller of the two, once again insuring that the text is always visible
  • Vmin; it's the minimum of the two above, so it's ideal for when your GUI should be adaptive and work across multiple platforms.

I don't see any practical use for Vmax, but there is no reason to be limiting with no reason. If it would work and have no issues... why not let it be there?

@ickshonpe
Copy link
Contributor Author

As a response to the third point I could see practical use for Vh, Vw, and Vmin. Those being the following:

  • Vh; is the smaller of the two in most cases. If the software is meant for a landscape orientation, this works well as the text will be certain to always fit on the screen.
  • Vw; this can be useful for windows that are taller than wide (such as they would be on phone) as this would be the smaller of the two, once again insuring that the text is always visible
  • Vmin; it's the minimum of the two above, so it's ideal for when your GUI should be adaptive and work across multiple platforms.

I don't see any practical use for Vmax, but there is no reason to be limiting with no reason. If it would work and have no issues... why not let it be there?

Well no features is also a feature. It can seem harmless to add extra variants but everything has to be maintained and documented and kept up to date etc. A huge range of similar options can be overwhelming for users too.

Your argument makes sense about Vw and VMin though. I think I agree now, they should both be included.

@JustAnotherCodemonkey
Copy link

I'd also like to throw in a potential Percent variant version if possible in regards to my own issue on this subject, #9280. This has killed game projects in the past where I've been at a loss on how to reliably size the text for a variety of viewing sizes. If anyone has a recommendation in the mean time on how to display cards on a screen as UI buttons without resizing the window screwing up the cards via their text, please let me know.

@Selene-Amanita
Copy link
Member

I agree with @JustAnotherCodemonkey a Percent (of the height of the parent) value would be really useful.

Our use case is to have a UI node have a shrink animation, but it's really tedious to have the characters on it shrink at the same time with the way font size is implemented.

@BenjaminBrienen BenjaminBrienen added D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed D-Complex Quite challenging from either a design or technical perspective. Ask for help! labels Jan 23, 2025
@viridia
Copy link
Contributor

viridia commented Sep 10, 2025

With respect to the issue of using Val I think we can learn a few lessons from CSS here:

  • In CSS, the length units for font sizes are different from most other length units. In particular, there are a number of units that operate relative to the inherited base font size, and these are often used. So we already can see that fonts are an exception.
  • "rem" units are considered the best practice for responsive apps - that is, metrics relative to the font size on the root element.

The difficulty, of course, is that Bevy doesn't have style inheritance. (Note that CSS doesn't have style inheritance either, except for a handful of properties - all of which are related to text styling. You can't inherit margins or padding for example.)

Bevy Feathers dips its toe into these waters a little bit with its theming solution, but is not a complete solution for this problem.

@viridia
Copy link
Contributor

viridia commented Sep 15, 2025

Suggestion: Replace the Default option with GlobalFontSize(f32). This produces an effective font size that is a multiplier of the global size.

This has the same use case as rem units in CSS. For users who are visually impaired and have trouble reading, they want to be able to increase the size of all fonts proportionally, but they don't want to globally scale the borders and other decorations in the UI since that just takes up more space.

Thus, I could define a body text which is GlobalFontSize(1.0) and a header text which is GlobalFontSize(1.2).

I thought about naming this WindowFontSize or RootFontSize but neither of those are technically correct. It really is a global variable.

@viridia
Copy link
Contributor

viridia commented Sep 18, 2025

Some bikeshedding on naming:

The global resource could be called BaseFontSize, and the variant that specifies a font size relative to that could just be called Base.

Note that all of the variants (both here and in Val) represent units of measurement, so we don't need to include any suffixes that convey that this is a unit; all we need is the name of the unit (Percent, Px, etc.)

@ickshonpe
Copy link
Contributor Author

Closed in favour of #22614

@ickshonpe ickshonpe closed this Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace the f32 font_size value of TextStyle with a FontSize enum

10 participants