Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/bevy_text/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub use text2d::*;

pub mod prelude {
#[doc(hidden)]
pub use crate::{Font, Text, Text2dBundle, TextAlignment, TextError, TextSection, TextStyle};
pub use crate::{Font, Text, Text2dBundle, TextAlignment, TextError, TextSection, TextStyle, DefaultFontSize, FontSize};
}

use bevy_app::prelude::*;
Expand Down Expand Up @@ -88,6 +88,7 @@ impl Plugin for TextPlugin {
.init_asset_loader::<FontLoader>()
.init_resource::<TextSettings>()
.init_resource::<FontAtlasWarning>()
.init_resource::<DefaultFontSize>()
.insert_resource(TextPipeline::default())
.add_systems(
PostUpdate,
Expand Down
14 changes: 10 additions & 4 deletions crates/bevy_text/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use bevy_utils::HashMap;
use glyph_brush_layout::{FontId, GlyphPositioner, SectionGeometry, SectionText};

use crate::{
compute_text_bounds, error::TextError, glyph_brush::GlyphBrush, scale_value, BreakLineOn, Font,
compute_text_bounds, error::TextError, glyph_brush::GlyphBrush, BreakLineOn, Font,
FontAtlasSet, FontAtlasWarning, PositionedGlyph, TextAlignment, TextSection, TextSettings,
YAxisOrientation,
YAxisOrientation, DefaultFontSize,
};

#[derive(Default, Resource)]
Expand Down Expand Up @@ -54,6 +54,8 @@ impl TextPipeline {
text_settings: &TextSettings,
font_atlas_warning: &mut FontAtlasWarning,
y_axis_orientation: YAxisOrientation,
viewport_height: f32,
default_font_size: DefaultFontSize,
) -> Result<TextLayoutInfo, TextError> {
let mut scaled_fonts = Vec::with_capacity(sections.len());
let sections = sections
Expand All @@ -63,7 +65,9 @@ impl TextPipeline {
.get(&section.style.font)
.ok_or(TextError::NoSuchFont)?;
let font_id = self.get_or_insert_font_id(&section.style.font, font);
let font_size = scale_value(section.style.font_size, scale_factor);
// let resolved_font_size = section.style.font_size.resolve(default_font_size, viewport_height);
// let font_size = scale_value(resolved_font_size, scale_factor);
let font_size = section.style.font_size.resolve(default_font_size, viewport_height, scale_factor);

scaled_fonts.push(ab_glyph::Font::as_scaled(&font.font, font_size));

Expand Down Expand Up @@ -109,6 +113,8 @@ impl TextPipeline {
scale_factor: f64,
text_alignment: TextAlignment,
linebreak_behaviour: BreakLineOn,
viewport_height: f32,
default_font_size: DefaultFontSize,
) -> Result<TextMeasureInfo, TextError> {
let mut auto_fonts = Vec::with_capacity(sections.len());
let mut scaled_fonts = Vec::with_capacity(sections.len());
Expand All @@ -119,7 +125,7 @@ impl TextPipeline {
let font = fonts
.get(&section.style.font)
.ok_or(TextError::NoSuchFont)?;
let font_size = scale_value(section.style.font_size, scale_factor);
let font_size = section.style.font_size.resolve(default_font_size, viewport_height, scale_factor);
auto_fonts.push(font.font.clone());
let px_scale_font = ab_glyph::Font::into_scaled(font.font.clone(), font_size);
scaled_fonts.push(px_scale_font);
Expand Down
55 changes: 52 additions & 3 deletions crates/bevy_text/src/text.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bevy_asset::Handle;
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
use bevy_ecs::{prelude::Component, reflect::ReflectComponent, system::Resource};
use bevy_reflect::prelude::*;
use bevy_render::color::Color;
use bevy_utils::default;
Expand Down Expand Up @@ -174,20 +174,69 @@ pub struct TextStyle {
///
/// 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,
pub font_size: FontSize,
pub color: Color,
}

impl Default for TextStyle {
fn default() -> Self {
Self {
font: DEFAULT_FONT_HANDLE.typed(),
font_size: 12.0,
font_size: FontSize::Default,
color: Color::WHITE,
}
}
}

/// 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.

/// 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.

/// Default font size expressed as a percentage of the height of the viewport.
Vh(f32),
}

impl Default for DefaultFontSize {
fn default() -> Self {
DefaultFontSize::Px(12.)
}
}

/// The size of a font
#[derive(Default, Copy, Clone, Debug, Reflect)]
pub enum FontSize {
/// The size of the font in logical pixels
Px(f32),
/// The size of the font expressed as a percentage of the height of the viewport.
Vh(f32),
/// Use the size from the `DefaultFontSize` resource
#[default]
Default,
}

impl From<DefaultFontSize> for FontSize {
fn from(value: DefaultFontSize) -> Self {
match value {
DefaultFontSize::Px(px) => FontSize::Px(px),
DefaultFontSize::Vh(vh) => FontSize::Vh(vh),
}
}
}


impl FontSize {
/// Resolve the `FontSize` to a numeric value in logical pixels.
pub fn resolve(self, default_font_size: DefaultFontSize, viewport_height: f32, scale_factor: f64) -> f32 {
match self {
FontSize::Px(px) => (px as f64 * scale_factor) as f32,
FontSize::Vh(vh) => viewport_height * vh / 100.,
FontSize::Default => Self::from(default_font_size).resolve(default_font_size, viewport_height, scale_factor),
}
}
}

/// Determines how lines will be broken when preventing text from running out of bounds.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Serialize, Deserialize)]
#[reflect(Serialize, Deserialize)]
Expand Down
11 changes: 7 additions & 4 deletions crates/bevy_text/src/text2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};

use crate::{
BreakLineOn, Font, FontAtlasSet, FontAtlasWarning, PositionedGlyph, Text, TextError,
TextLayoutInfo, TextPipeline, TextSettings, YAxisOrientation,
TextLayoutInfo, TextPipeline, TextSettings, YAxisOrientation, DefaultFontSize,
};

/// The maximum width and height of text. The text will wrap according to the specified size.
Expand Down Expand Up @@ -161,15 +161,16 @@ pub fn update_text2d_layout(
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<TextPipeline>,
mut text_query: Query<(Entity, Ref<Text>, Ref<Text2dBounds>, &mut TextLayoutInfo)>,
default_font_size: Res<DefaultFontSize>,
) {
// We need to consume the entire iterator, hence `last`
let factor_changed = scale_factor_changed.iter().last().is_some();

// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows
let (scale_factor, view_height) = windows
.get_single()
.map(|window| window.resolution.scale_factor())
.unwrap_or(1.0);
.map(|window| (window.resolution.scale_factor(), window.resolution.height()))
.unwrap_or((1.0, 600.));

for (entity, text, bounds, mut text_layout_info) in &mut text_query {
if factor_changed || text.is_changed() || bounds.is_changed() || queue.remove(&entity) {
Expand All @@ -195,6 +196,8 @@ pub fn update_text2d_layout(
text_settings.as_ref(),
&mut font_atlas_warning,
YAxisOrientation::BottomToTop,
view_height,
*default_font_size,
) {
Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, let's add this entity to the
Expand Down
32 changes: 25 additions & 7 deletions crates/bevy_ui/src/widget/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use bevy_render::texture::Image;
use bevy_sprite::TextureAtlas;
use bevy_text::{
BreakLineOn, Font, FontAtlasSet, FontAtlasWarning, Text, TextError, TextLayoutInfo,
TextMeasureInfo, TextPipeline, TextSettings, YAxisOrientation,
TextMeasureInfo, TextPipeline, TextSettings, YAxisOrientation, DefaultFontSize,
};
use bevy_window::{PrimaryWindow, Window};
use taffy::style::AvailableSpace;
Expand Down Expand Up @@ -82,13 +82,17 @@ fn create_text_measure(
text: Ref<Text>,
mut content_size: Mut<ContentSize>,
mut text_flags: Mut<TextFlags>,
view_height: f32,
default_font_size: DefaultFontSize,
) {
match text_pipeline.create_text_measure(
fonts,
&text.sections,
scale_factor,
text.alignment,
text.linebreak_behavior,
view_height,
default_font_size
) {
Ok(measure) => {
if text.linebreak_behavior == BreakLineOn::NoWrap {
Expand Down Expand Up @@ -129,11 +133,12 @@ pub fn measure_text_system(
ui_scale: Res<UiScale>,
mut text_pipeline: ResMut<TextPipeline>,
mut text_query: Query<(Ref<Text>, &mut ContentSize, &mut TextFlags), With<Node>>,
default_font_size: Res<DefaultFontSize>,
) {
let window_scale_factor = windows
let (window_scale_factor, view_height) = windows
.get_single()
.map(|window| window.resolution.scale_factor())
.unwrap_or(1.);
.map(|window| (window.resolution.scale_factor(), window.resolution.height()))
.unwrap_or((1., 600.));

let scale_factor = ui_scale.0 * window_scale_factor;

Expand All @@ -149,6 +154,8 @@ pub fn measure_text_system(
text,
content_size,
text_flags,
view_height,
*default_font_size
);
}
}
Expand All @@ -164,6 +171,8 @@ pub fn measure_text_system(
text,
content_size,
text_flags,
view_height,
*default_font_size
);
}
}
Expand All @@ -184,6 +193,8 @@ fn queue_text(
node: Ref<Node>,
mut text_flags: Mut<TextFlags>,
mut text_layout_info: Mut<TextLayoutInfo>,
view_height: f32,
default_font_size: DefaultFontSize,
) {
// Skip the text node if it is waiting for a new measure func
if !text_flags.needs_new_measure_func {
Expand All @@ -208,6 +219,8 @@ fn queue_text(
text_settings,
font_atlas_warning,
YAxisOrientation::TopToBottom,
view_height,
default_font_size,
) {
Err(TextError::NoSuchFont) => {
// There was an error processing the text layout, try again next frame
Expand Down Expand Up @@ -245,12 +258,13 @@ pub fn text_system(
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<TextPipeline>,
mut text_query: Query<(Ref<Node>, &Text, &mut TextLayoutInfo, &mut TextFlags)>,
default_font_size: Res<DefaultFontSize>,
) {
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let window_scale_factor = windows
let (window_scale_factor, view_height) = windows
.get_single()
.map(|window| window.resolution.scale_factor())
.unwrap_or(1.);
.map(|window| (window.resolution.scale_factor(), window.height()))
.unwrap_or((1., 600.));

let scale_factor = ui_scale.0 * window_scale_factor;

Expand All @@ -271,6 +285,8 @@ pub fn text_system(
node,
text_flags,
text_layout_info,
view_height,
*default_font_size,
);
}
}
Expand All @@ -292,6 +308,8 @@ pub fn text_system(
node,
text_flags,
text_layout_info,
view_height,
*default_font_size,
);
}
}
Expand Down
6 changes: 3 additions & 3 deletions examples/ui/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
TextStyle {
// This font is loaded and will be used instead of the default font.
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 100.0,
font_size: FontSize::Vh(50.0),
color: Color::WHITE,
},
) // Set the alignment of the Text
Expand All @@ -59,12 +59,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
TextStyle {
// This font is loaded and will be used instead of the default font.
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 60.0,
font_size: FontSize::Px(60.0),
color: Color::WHITE,
},
),
TextSection::from_style(TextStyle {
font_size: 60.0,
font_size: FontSize::Px(60.0),
color: Color::GOLD,
// If no font is specified, it will use the default font.
..default()
Expand Down