diff --git a/examples/ui/text/text.rs b/examples/ui/text/text.rs index 35dbf4f650b47..ec4f25302eac2 100644 --- a/examples/ui/text/text.rs +++ b/examples/ui/text/text.rs @@ -7,6 +7,7 @@ use bevy::{ color::palettes::css::GOLD, diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}, prelude::*, + text::FontSourceTemplate, text::{FontFeatureTag, FontFeatures, FontSize, Underline}, }; @@ -19,158 +20,176 @@ fn main() { } // Marker struct to help identify the FPS UI component, since there may be many Text components -#[derive(Component)] +#[derive(Component, Default, Clone)] struct FpsText; // Marker struct to help identify the color-changing Text component -#[derive(Component)] +#[derive(Component, Default, Clone)] struct AnimatedText; -fn setup(mut commands: Commands, asset_server: Res) { - // UI camera - commands.spawn(Camera2d); - // Text with one section - commands.spawn(( +fn setup(world: &mut World) -> Result { + world.spawn_scene_list(bsn_list![ + Camera2d, + text_with_one_section(), + text_with_multiple_sections(), + text_with_open_type_features(), + ])?; + + #[cfg(feature = "default_font")] + world.spawn_scene_list(bsn_list![default_font(),])?; + + #[cfg(not(feature = "default_font"))] + world.spawn_scene_list(bsn_list![default_font_disabled()])?; + Ok(()) +} + +fn text_with_one_section() -> impl Scene { + bsn! { // Accepts a `String` or any type that converts into a `String`, such as `&str` - Text::new("hello\nbevy!"), - Underline, + Text::new("hello\nbevy!") + Underline TextFont { // This font is loaded and will be used instead of the default font. - font: asset_server.load("fonts/FiraSans-Bold.ttf").into(), + font: FontSourceTemplate::Handle("fonts/FiraSans-Bold.ttf"), // The size of the text will be 20% of the viewport height. font_size: FontSize::Vh(20.0), - ..default() - }, - TextShadow::default(), + } + TextShadow::default() // Set the justification of the Text - TextLayout::new_with_justify(Justify::Center), + TextLayout::new_with_justify(Justify::Center) // Set the style of the Node itself. Node { position_type: PositionType::Absolute, bottom: px(5), right: px(5), - ..default() - }, - AnimatedText, - )); - - // Text with multiple sections - commands - .spawn(( - // Create a Text with multiple child spans. - Text::new("FPS: "), - TextFont { - // This font is loaded and will be used instead of the default font. - font: asset_server.load("fonts/FiraSans-Bold.ttf").into(), - font_size: FontSize::Px(42.0), - ..default() - }, - )) - .with_child(( - TextSpan::default(), + } + AnimatedText + } +} + +fn text_with_multiple_sections() -> impl Scene { + bsn! { + // Create a Text with multiple child spans. + Text::new("FPS: ") + TextFont { + // This font is loaded and will be used instead of the default font. + font: FontSourceTemplate::Handle("fonts/FiraSans-Bold.ttf"), + font_size: FontSize::Px(42.0), + } + Children [ ( - TextFont { - // If the "default_font" feature is unavailable, load a font to use instead. - #[cfg(not(feature = "default_font"))] - font: asset_server.load("fonts/FiraMono-Medium.ttf").into(), - font_size: FontSize::Px(33.0), - ..Default::default() - }, - TextColor(GOLD.into()), - ), - FpsText, - )); - - // Text with OpenType features - let opentype_font_handle: FontSource = - asset_server.load("fonts/EBGaramond12-Regular.otf").into(); - commands - .spawn(( - Node { - margin: UiRect::all(px(12.0)), - position_type: PositionType::Absolute, - top: px(5.0), - right: px(5.0), - ..default() - }, - Text::new("Opentype features:\n"), - TextFont { - font: opentype_font_handle.clone(), - font_size: FontSize::Px(32.0), - ..default() - }, - )) - .with_children(|parent| { - let text_rows = [ - ("Smallcaps: ", FontFeatureTag::SMALL_CAPS, "Hello World"), - ( - "Ligatures: ", - FontFeatureTag::STANDARD_LIGATURES, - "fi fl ff ffi ffl", - ), - ("Fractions: ", FontFeatureTag::FRACTIONS, "12/134"), - ("Superscript: ", FontFeatureTag::SUPERSCRIPT, "Up here!"), - ("Subscript: ", FontFeatureTag::SUBSCRIPT, "Down here!"), - ( - "Oldstyle figures: ", - FontFeatureTag::OLDSTYLE_FIGURES, - "1234567890", - ), - ( - "Lining figures: ", - FontFeatureTag::LINING_FIGURES, - "1234567890", - ), - ]; - - for (title, feature, text) in text_rows { - parent.spawn(( - TextSpan::new(title), - TextFont { - font: opentype_font_handle.clone(), - font_size: FontSize::Px(24.0), - ..default() - }, - )); - parent.spawn(( - TextSpan::new(format!("{text}\n")), + TextSpan + Children [( TextFont { - font: opentype_font_handle.clone(), - font_size: FontSize::Px(24.0), - font_features: FontFeatures::builder().enable(feature).build(), - ..default() - }, - )); - } - }); + // If the "default_font" feature is unavailable, load a font to use instead. + //#[cfg(not(feature = "default_font"))] + font: FontSourceTemplate::Handle("fonts/FiraMono-Medium.ttf"), + font_size: FontSize::Px(33.0), + } + )] + TextColor(Color::from(GOLD)) + FpsText + ) + ] + } +} - #[cfg(feature = "default_font")] - commands.spawn(( +type TextRows = (&'static str, FontFeatureTag, &'static str); + +fn text_with_open_type_features() -> impl Scene { + let text_rows: [TextRows; 7] = [ + ("Smallcaps: ", FontFeatureTag::SMALL_CAPS, "Hello World"), + ( + "Ligatures: ", + FontFeatureTag::STANDARD_LIGATURES, + "fi fl ff ffi ffl", + ), + ("Fractions: ", FontFeatureTag::FRACTIONS, "12/134"), + ("Superscript: ", FontFeatureTag::SUPERSCRIPT, "Up here!"), + ("Subscript: ", FontFeatureTag::SUBSCRIPT, "Down here!"), + ( + "Oldstyle figures: ", + FontFeatureTag::OLDSTYLE_FIGURES, + "1234567890", + ), + ( + "Lining figures: ", + FontFeatureTag::LINING_FIGURES, + "1234567890", + ), + ]; + + bsn! { + Node { + margin: UiRect::all(px(12.0)), + position_type: PositionType::Absolute, + top: px(5.0), + right: px(5.0), + } + Text::new("Opentype features:\n") + TextFont { + font: FontSourceTemplate::Handle("fonts/EBGaramond12-Regular.otf"), + font_size: FontSize::Px(32.0), + } + Children [ + title(text_rows[0]), text(text_rows[0]), + title(text_rows[1]), text(text_rows[1]), + title(text_rows[2]), text(text_rows[2]), + title(text_rows[3]), text(text_rows[3]), + title(text_rows[4]), text(text_rows[4]), + title(text_rows[5]), text(text_rows[5]), + title(text_rows[6]), text(text_rows[6]), + ] + } +} + +fn title(row: TextRows) -> impl Scene { + bsn! { + TextSpan::new(row.0) + TextFont { + font: FontSourceTemplate::Handle("fonts/EBGaramond12-Regular.otf"), + font_size: FontSize::Px(24.0), + } + } +} + +fn text(row: TextRows) -> impl Scene { + bsn! { + TextSpan::new(format!("{0}\n", row.2)) + TextFont { + font: FontSourceTemplate::Handle("fonts/EBGaramond12-Regular.otf"), + font_size: FontSize::Px(24.0), + font_features: { FontFeatures::builder().enable(row.1).build() }, + } + } +} + +fn default_font() -> impl Scene { + bsn! { // Here we are able to call the `From` method instead of creating a new `TextSection`. // This will use the default font (a minimal subset of FiraMono) and apply the default styling. - Text::new("From an &str into a Text with the default font!"), + Text::new("From an &str into a Text with the default font!") Node { position_type: PositionType::Absolute, bottom: px(5), left: px(15), - ..default() - }, - )); + } + } +} - #[cfg(not(feature = "default_font"))] - commands.spawn(( - Text::new("Default font disabled"), +#[expect(dead_code, reason = "demonstration purpose")] +fn default_font_disabled() -> impl Scene { + bsn! { + Text::new("Default font disabled") TextFont { - font: asset_server.load("fonts/FiraMono-Medium.ttf"), - ..default() - }, + font: FontSourceTemplate::Handle("fonts/FiraMono-Medium.ttf"), + } Node { position_type: PositionType::Absolute, bottom: px(5), left: px(15), - ..default() - }, - )); + } + } } fn text_color_system(time: Res