From 4ba09f3dd90ac52ba7b5753693156a2c55a6c6ea Mon Sep 17 00:00:00 2001 From: Cyborus04 Date: Mon, 6 Jan 2025 18:11:38 -0500 Subject: [PATCH] add line height to `TextFont` (#16614) # Objective - Allow users to customize the line height of text. - Implements #16085 ## Solution - Add a `line_height` field to `TextFont` to feed into `cosmic_text`'s `Metrics`. ## Testing - Tested on my own game, and worked exactly as I wanted. - My game is only 2D, so I only tested `Text2d`. `Text` still needs tested, but I imagine it'll work fine. - An example is available [here](https://code.cartoon-aa.xyz/Cyborus/custom-line-height-example) --- ## Showcase
Click to view showcase With font: ```rust TextFont { font: /* unimportant */, font_size: 16.0, line_height: None, ..default() } ``` ![image](https://github.com/user-attachments/assets/d12d8334-72ae-44b4-9b2e-993bbfd19da6) With font: ```rust TextFont { font: /* unimportant */, font_size: 16.0, line_height: Some(16.0), ..default() } ``` ![image](https://github.com/user-attachments/assets/6bc843b0-b633-4c30-bf77-6bbad774c1e5)
## Migration Guide `TextFont` now has a `line_height` field. Any instantiation of `TextFont` that doesn't have `..default()` will need to add this field. --- crates/bevy_text/src/lib.rs | 1 + crates/bevy_text/src/pipeline.rs | 11 +++++++++-- crates/bevy_text/src/text.rs | 33 +++++++++++++++++++++++++++++++ examples/dev_tools/fps_overlay.rs | 1 + 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index d50cd45cf25e5..72f51421f5dc8 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -110,6 +110,7 @@ impl Plugin for TextPlugin { app.init_asset::() .register_type::() .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() diff --git a/crates/bevy_text/src/pipeline.rs b/crates/bevy_text/src/pipeline.rs index f8e7274ef31f2..6ff1e306dbdb8 100644 --- a/crates/bevy_text/src/pipeline.rs +++ b/crates/bevy_text/src/pipeline.rs @@ -98,6 +98,7 @@ impl TextPipeline { // Collect span information into a vec. This is necessary because font loading requires mut access // to FontSystem, which the cosmic-text Buffer also needs. let mut font_size: f32 = 0.; + let mut line_height: f32 = 0.0; let mut spans: Vec<(usize, &str, &TextFont, FontFaceInfo, Color)> = core::mem::take(&mut self.spans_buffer) .into_iter() @@ -130,6 +131,7 @@ impl TextPipeline { // Get max font size for use in cosmic Metrics. font_size = font_size.max(text_font.font_size); + line_height = line_height.max(text_font.line_height.eval(text_font.font_size)); // Load Bevy fonts into cosmic-text's font system. let face_info = load_font_to_fontdb( @@ -146,7 +148,6 @@ impl TextPipeline { spans.push((span_index, span, text_font, face_info, color)); } - let line_height = font_size * 1.2; let mut metrics = Metrics::new(font_size, line_height).scale(scale_factor as f32); // Metrics of 0.0 cause `Buffer::set_metrics` to panic. We hack around this by 'falling // through' to call `Buffer::set_rich_text` with zero spans so any cached text will be cleared without @@ -486,7 +487,13 @@ fn get_attrs<'a>( .stretch(face_info.stretch) .style(face_info.style) .weight(face_info.weight) - .metrics(Metrics::relative(text_font.font_size, 1.2).scale(scale_factor as f32)) + .metrics( + Metrics { + font_size: text_font.font_size, + line_height: text_font.line_height.eval(text_font.font_size), + } + .scale(scale_factor as f32), + ) .color(cosmic_text::Color(color.to_linear().as_u32())); attrs } diff --git a/crates/bevy_text/src/text.rs b/crates/bevy_text/src/text.rs index bfefe283cf71c..1c0eaa83d8581 100644 --- a/crates/bevy_text/src/text.rs +++ b/crates/bevy_text/src/text.rs @@ -286,6 +286,11 @@ pub struct TextFont { /// A new font atlas is generated for every combination of font handle and scaled font size /// which can have a strong performance impact. pub font_size: f32, + /// The vertical height of a line of text, from the top of one line to the top of the + /// next. + /// + /// Defaults to `LineHeight::RelativeToFont(1.2)` + pub line_height: LineHeight, /// The antialiasing method to use when rendering text. pub font_smoothing: FontSmoothing, } @@ -325,11 +330,39 @@ impl Default for TextFont { Self { font: Default::default(), font_size: 20.0, + line_height: LineHeight::default(), font_smoothing: Default::default(), } } } +/// Specifies the height of each line of text for `Text` and `Text2d` +/// +/// Default is 1.2x the font size +#[derive(Debug, Clone, Copy, Reflect)] +#[reflect(Debug)] +pub enum LineHeight { + /// Set line height to a specific number of pixels + Px(f32), + /// Set line height to a multiple of the font size + RelativeToFont(f32), +} + +impl LineHeight { + pub(crate) fn eval(self, font_size: f32) -> f32 { + match self { + LineHeight::Px(px) => px, + LineHeight::RelativeToFont(scale) => scale * font_size, + } + } +} + +impl Default for LineHeight { + fn default() -> Self { + LineHeight::RelativeToFont(1.2) + } +} + /// The color of the text for this section. #[derive(Component, Copy, Clone, Debug, Deref, DerefMut, Reflect)] #[reflect(Component, Default, Debug)] diff --git a/examples/dev_tools/fps_overlay.rs b/examples/dev_tools/fps_overlay.rs index 0dcc5e5218be5..a1c11bb3ce026 100644 --- a/examples/dev_tools/fps_overlay.rs +++ b/examples/dev_tools/fps_overlay.rs @@ -26,6 +26,7 @@ fn main() { font: default(), // We could also disable font smoothing, font_smoothing: FontSmoothing::default(), + ..default() }, // We can also change color of the overlay text_color: OverlayColor::GREEN,