split up TextStyle (#15857)

# Objective

Currently text is recomputed unnecessarily on any changes to its color,
which is extremely expensive.

## Solution
Split up `TextStyle` into two separate components `TextFont` and
`TextColor`.

## Testing

I added this system to `many_buttons`:
```rust
fn set_text_colors_changed(mut colors: Query<&mut TextColor>) {
    for mut text_color in colors.iter_mut() {
        text_color.set_changed();
    }
}
```

reports ~4fps on main, ~50fps with this PR.

## Migration Guide
`TextStyle` has been renamed to `TextFont` and its `color` field has
been moved to a separate component named `TextColor` which newtypes
`Color`.
This commit is contained in:
ickshonpe 2024-10-13 18:06:22 +01:00 committed by GitHub
parent 6521e759ea
commit 6f7d0e5725
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
83 changed files with 752 additions and 641 deletions

View file

@ -14,7 +14,7 @@ use bevy_ecs::{
};
use bevy_hierarchy::{BuildChildren, ChildBuild};
use bevy_render::view::Visibility;
use bevy_text::{Font, TextSpan, TextStyle};
use bevy_text::{Font, TextColor, TextFont, TextSpan};
use bevy_ui::{
node_bundles::NodeBundle,
widget::{Text, UiTextWriter},
@ -62,7 +62,9 @@ impl Plugin for FpsOverlayPlugin {
#[derive(Resource, Clone)]
pub struct FpsOverlayConfig {
/// Configuration of text in the overlay.
pub text_config: TextStyle,
pub text_config: TextFont,
/// Color of text in the overlay.
pub text_color: Color,
/// Displays the FPS overlay if true.
pub enabled: bool,
}
@ -70,12 +72,12 @@ pub struct FpsOverlayConfig {
impl Default for FpsOverlayConfig {
fn default() -> Self {
FpsOverlayConfig {
text_config: TextStyle {
text_config: TextFont {
font: Handle::<Font>::default(),
font_size: 32.0,
color: Color::WHITE,
..default()
},
text_color: Color::WHITE,
enabled: true,
}
}
@ -102,6 +104,7 @@ fn setup(mut commands: Commands, overlay_config: Res<FpsOverlayConfig>) {
p.spawn((
Text::new("FPS: "),
overlay_config.text_config.clone(),
TextColor(overlay_config.text_color),
FpsText,
))
.with_child((TextSpan::default(), overlay_config.text_config.clone()));
@ -128,9 +131,10 @@ fn customize_text(
mut writer: UiTextWriter,
) {
for entity in &query {
writer.for_each_style(entity, |mut style| {
*style = overlay_config.text_config.clone();
writer.for_each_font(entity, |mut font| {
*font = overlay_config.text_config.clone();
});
writer.for_each_color(entity, |mut color| color.0 = overlay_config.text_color);
}
}

View file

@ -60,9 +60,9 @@ pub struct FontAtlasKey(pub u32, pub FontSmoothing);
/// A `FontAtlasSet` is an [`Asset`].
///
/// There is one `FontAtlasSet` for each font:
/// - When a [`Font`] is loaded as an asset and then used in [`TextStyle`](crate::TextStyle),
/// - When a [`Font`] is loaded as an asset and then used in [`TextFont`](crate::TextFont),
/// a `FontAtlasSet` asset is created from a weak handle to the `Font`.
/// - ~When a font is loaded as a system font, and then used in [`TextStyle`](crate::TextStyle),
/// - ~When a font is loaded as a system font, and then used in [`TextFont`](crate::TextFont),
/// a `FontAtlasSet` asset is created and stored with a strong handle to the `FontAtlasSet`.~
/// (*Note that system fonts are not currently supported by the `TextPipeline`.*)
///

View file

@ -65,8 +65,8 @@ pub use text_access::*;
pub mod prelude {
#[doc(hidden)]
pub use crate::{
Font, JustifyText, LineBreak, Text2d, TextError, TextLayout, TextReader2d, TextSpan,
TextStyle, TextWriter2d,
Font, JustifyText, LineBreak, Text2d, TextColor, TextError, TextFont, TextLayout,
TextReader2d, TextSpan, TextWriter2d,
};
}

View file

@ -1,6 +1,7 @@
use alloc::sync::Arc;
use bevy_asset::{AssetId, Assets};
use bevy_color::Color;
use bevy_ecs::{
component::Component,
entity::Entity,
@ -17,7 +18,7 @@ use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Wrap};
use crate::{
error::TextError, ComputedTextBlock, Font, FontAtlasSets, FontSmoothing, JustifyText,
LineBreak, PositionedGlyph, TextBounds, TextEntity, TextLayout, TextStyle, YAxisOrientation,
LineBreak, PositionedGlyph, TextBounds, TextEntity, TextFont, TextLayout, YAxisOrientation,
};
/// A wrapper resource around a [`cosmic_text::FontSystem`]
@ -70,7 +71,7 @@ pub struct TextPipeline {
/// Buffered vec for collecting spans.
///
/// See [this dark magic](https://users.rust-lang.org/t/how-to-cache-a-vectors-capacity/94478/10).
spans_buffer: Vec<(usize, &'static str, &'static TextStyle, FontFaceInfo)>,
spans_buffer: Vec<(usize, &'static str, &'static TextFont, FontFaceInfo)>,
/// Buffered vec for collecting info for glyph assembly.
glyph_info: Vec<(AssetId<Font>, FontSmoothing)>,
}
@ -83,7 +84,7 @@ impl TextPipeline {
pub fn update_buffer<'a>(
&mut self,
fonts: &Assets<Font>,
text_spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextStyle)>,
text_spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextFont, Color)>,
linebreak: LineBreak,
justify: JustifyText,
bounds: TextBounds,
@ -96,22 +97,22 @@ 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 spans: Vec<(usize, &str, &TextStyle, FontFaceInfo)> =
let mut spans: Vec<(usize, &str, &TextFont, FontFaceInfo, Color)> =
core::mem::take(&mut self.spans_buffer)
.into_iter()
.map(|_| -> (usize, &str, &TextStyle, FontFaceInfo) { unreachable!() })
.map(|_| -> (usize, &str, &TextFont, FontFaceInfo, Color) { unreachable!() })
.collect();
computed.entities.clear();
for (span_index, (entity, depth, span, style)) in text_spans.enumerate() {
for (span_index, (entity, depth, span, text_font, color)) in text_spans.enumerate() {
// Return early if a font is not loaded yet.
if !fonts.contains(style.font.id()) {
if !fonts.contains(text_font.font.id()) {
spans.clear();
self.spans_buffer = spans
.into_iter()
.map(
|_| -> (usize, &'static str, &'static TextStyle, FontFaceInfo) {
|_| -> (usize, &'static str, &'static TextFont, FontFaceInfo) {
unreachable!()
},
)
@ -124,17 +125,21 @@ impl TextPipeline {
computed.entities.push(TextEntity { entity, depth });
// Get max font size for use in cosmic Metrics.
font_size = font_size.max(style.font_size);
font_size = font_size.max(text_font.font_size);
// Load Bevy fonts into cosmic-text's font system.
let face_info =
load_font_to_fontdb(style, font_system, &mut self.map_handle_to_font_id, fonts);
let face_info = load_font_to_fontdb(
text_font,
font_system,
&mut self.map_handle_to_font_id,
fonts,
);
// Save spans that aren't zero-sized.
if scale_factor <= 0.0 || style.font_size <= 0.0 {
if scale_factor <= 0.0 || text_font.font_size <= 0.0 {
continue;
}
spans.push((span_index, span, style, face_info));
spans.push((span_index, span, text_font, face_info, color));
}
let line_height = font_size * 1.2;
@ -151,12 +156,14 @@ impl TextPipeline {
// The section index is stored in the metadata of the spans, and could be used
// to look up the section the span came from and is not used internally
// in cosmic-text.
let spans_iter = spans.iter().map(|(span_index, span, style, font_info)| {
(
*span,
get_attrs(*span_index, style, font_info, scale_factor),
)
});
let spans_iter = spans
.iter()
.map(|(span_index, span, text_font, font_info, color)| {
(
*span,
get_attrs(*span_index, text_font, *color, font_info, scale_factor),
)
});
// Update the buffer.
let buffer = &mut computed.buffer;
@ -186,7 +193,7 @@ impl TextPipeline {
spans.clear();
self.spans_buffer = spans
.into_iter()
.map(|_| -> (usize, &'static str, &'static TextStyle, FontFaceInfo) { unreachable!() })
.map(|_| -> (usize, &'static str, &'static TextFont, FontFaceInfo) { unreachable!() })
.collect();
Ok(())
@ -201,7 +208,7 @@ impl TextPipeline {
&mut self,
layout_info: &mut TextLayoutInfo,
fonts: &Assets<Font>,
text_spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextStyle)>,
text_spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextFont, Color)>,
scale_factor: f64,
layout: &TextLayout,
bounds: TextBounds,
@ -222,8 +229,8 @@ impl TextPipeline {
// Extract font ids from the iterator while traversing it.
let mut glyph_info = core::mem::take(&mut self.glyph_info);
glyph_info.clear();
let text_spans = text_spans.inspect(|(_, _, _, style)| {
glyph_info.push((style.font.id(), style.font_smoothing));
let text_spans = text_spans.inspect(|(_, _, _, text_font, _)| {
glyph_info.push((text_font.font.id(), text_font.font_smoothing));
});
let update_result = self.update_buffer(
@ -335,7 +342,7 @@ impl TextPipeline {
&mut self,
entity: Entity,
fonts: &Assets<Font>,
text_spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextStyle)>,
text_spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextFont, Color)>,
scale_factor: f64,
layout: &TextLayout,
computed: &mut ComputedTextBlock,
@ -427,12 +434,12 @@ impl TextMeasureInfo {
}
fn load_font_to_fontdb(
style: &TextStyle,
text_font: &TextFont,
font_system: &mut cosmic_text::FontSystem,
map_handle_to_font_id: &mut HashMap<AssetId<Font>, (cosmic_text::fontdb::ID, Arc<str>)>,
fonts: &Assets<Font>,
) -> FontFaceInfo {
let font_handle = style.font.clone();
let font_handle = text_font.font.clone();
let (face_id, family_name) = map_handle_to_font_id
.entry(font_handle.id())
.or_insert_with(|| {
@ -461,10 +468,11 @@ fn load_font_to_fontdb(
}
}
/// Translates [`TextStyle`] to [`Attrs`].
/// Translates [`TextFont`] to [`Attrs`].
fn get_attrs<'a>(
span_index: usize,
style: &TextStyle,
text_font: &TextFont,
color: Color,
face_info: &'a FontFaceInfo,
scale_factor: f64,
) -> Attrs<'a> {
@ -474,8 +482,8 @@ fn get_attrs<'a>(
.stretch(face_info.stretch)
.style(face_info.style)
.weight(face_info.weight)
.metrics(Metrics::relative(style.font_size, 1.2).scale(scale_factor as f32))
.color(cosmic_text::Color(style.color.to_linear().as_u32()));
.metrics(Metrics::relative(text_font.font_size, 1.2).scale(scale_factor as f32))
.color(cosmic_text::Color(color.to_linear().as_u32()));
attrs
}

View file

@ -58,11 +58,11 @@ pub struct ComputedTextBlock {
///
/// Includes:
/// - [`TextLayout`] changes.
/// - [`TextStyle`] or `Text2d`/`Text`/`TextSpan` changes anywhere in the block's entity hierarchy.
/// - [`TextFont`] or `Text2d`/`Text`/`TextSpan` changes anywhere in the block's entity hierarchy.
// TODO: This encompasses both structural changes like font size or justification and non-structural
// changes like text color and font smoothing. This field currently causes UI to 'remeasure' text, even if
// the actual changes are non-structural and can be handled by only rerendering and not remeasuring. A full
// solution would probably require splitting TextLayout and TextStyle into structural/non-structural
// solution would probably require splitting TextLayout and TextFont into structural/non-structural
// components for more granular change detection. A cost/benefit analysis is needed.
pub(crate) needs_rerender: bool,
}
@ -70,7 +70,7 @@ pub struct ComputedTextBlock {
impl ComputedTextBlock {
/// Accesses entities in this block.
///
/// Can be used to look up [`TextStyle`] components for glyphs in [`TextLayoutInfo`] using the `span_index`
/// Can be used to look up [`TextFont`] components for glyphs in [`TextLayoutInfo`] using the `span_index`
/// stored there.
pub fn entities(&self) -> &[TextEntity] {
&self.entities
@ -97,7 +97,7 @@ impl Default for ComputedTextBlock {
/// Component with text format settings for a block of text.
///
/// A block of text is composed of text spans, which each have a separate string value and [`TextStyle`]. Text
/// A block of text is composed of text spans, which each have a separate string value and [`TextFont`]. Text
/// spans associated with a text block are collected into [`ComputedTextBlock`] for layout, and then inserted
/// to [`TextLayoutInfo`] for rendering.
///
@ -159,38 +159,39 @@ impl TextLayout {
///
/// Spans are collected in hierarchy traversal order into a [`ComputedTextBlock`] for layout.
///
/*
```
# use bevy_asset::Handle;
# use bevy_color::Color;
# use bevy_color::palettes::basic::{RED, BLUE};
# use bevy_ecs::World;
# use bevy_text::{Font, TextLayout, TextStyle, TextSection};
# let font_handle: Handle<Font> = Default::default();
# let mut world = World::default();
#
world.spawn((
TextLayout::default(),
TextStyle {
font: font_handle.clone().into(),
font_size: 60.0,
color: BLUE.into(),
}
))
.with_child((
TextSpan::new("Hello!"),
TextStyle {
font: font_handle.into(),
font_size: 60.0,
color: RED.into(),
}
));
```
*/
/// ```
/// # use bevy_asset::Handle;
/// # use bevy_color::Color;
/// # use bevy_color::palettes::basic::{RED, BLUE};
/// # use bevy_ecs::world::World;
/// # use bevy_text::{Font, TextLayout, TextFont, TextSpan, TextColor};
/// # use bevy_hierarchy::BuildChildren;
///
/// # let font_handle: Handle<Font> = Default::default();
/// # let mut world = World::default();
/// #
/// world.spawn((
/// TextLayout::default(),
/// TextFont {
/// font: font_handle.clone().into(),
/// font_size: 60.0,
/// ..Default::default()
/// },
/// TextColor(BLUE.into()),
/// ))
/// .with_child((
/// TextSpan::new("Hello!"),
/// TextFont {
/// font: font_handle.into(),
/// font_size: 60.0,
/// ..Default::default()
/// },
/// TextColor(RED.into()),
/// ));
/// ```
#[derive(Component, Debug, Default, Clone, Deref, DerefMut, Reflect)]
#[reflect(Component, Default, Debug)]
#[require(TextStyle)]
#[require(TextFont, TextColor)]
pub struct TextSpan(pub String);
impl TextSpan {
@ -259,11 +260,11 @@ impl From<JustifyText> for cosmic_text::Align {
}
}
/// `TextStyle` determines the style of a text span within a [`ComputedTextBlock`], specifically
/// `TextFont` determines the style of a text span within a [`ComputedTextBlock`], specifically
/// the font face, the font size, and the color.
#[derive(Component, Clone, Debug, Reflect)]
#[reflect(Component, Default, Debug)]
pub struct TextStyle {
pub struct TextFont {
/// The specific font face to use, as a `Handle` to a [`Font`] asset.
///
/// If the `font` is not specified, then
@ -280,31 +281,52 @@ 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,
/// The color of the text for this section.
pub color: Color,
/// The antialiasing method to use when rendering text.
pub font_smoothing: FontSmoothing,
}
impl TextStyle {
/// Returns this [`TextStyle`] with the specified [`FontSmoothing`].
impl TextFont {
/// Returns this [`TextFont`] with the specified [`FontSmoothing`].
pub const fn with_font_smoothing(mut self, font_smoothing: FontSmoothing) -> Self {
self.font_smoothing = font_smoothing;
self
}
}
impl Default for TextStyle {
impl Default for TextFont {
fn default() -> Self {
Self {
font: Default::default(),
font_size: 20.0,
color: Color::WHITE,
font_smoothing: Default::default(),
}
}
}
/// The color of the text for this section.
#[derive(Component, Copy, Clone, Debug, Deref, DerefMut, Reflect)]
#[reflect(Component, Default, Debug)]
pub struct TextColor(pub Color);
impl Default for TextColor {
fn default() -> Self {
Self::WHITE
}
}
impl<T: Into<Color>> From<T> for TextColor {
fn from(color: T) -> Self {
Self(color.into())
}
}
impl TextColor {
/// Black colored text
pub const BLACK: Self = TextColor(Color::BLACK);
/// White colored text
pub const WHITE: Self = TextColor(Color::WHITE);
}
/// Determines how lines will be broken when preventing text from running out of bounds.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Reflect, Serialize, Deserialize)]
#[reflect(Serialize, Deserialize)]
@ -359,12 +381,12 @@ pub fn detect_text_needs_rerender<Root: Component>(
(
Or<(
Changed<Root>,
Changed<TextStyle>,
Changed<TextFont>,
Changed<TextLayout>,
Changed<Children>,
)>,
With<Root>,
With<TextStyle>,
With<TextFont>,
With<TextLayout>,
),
>,
@ -373,13 +395,13 @@ pub fn detect_text_needs_rerender<Root: Component>(
(
Or<(
Changed<TextSpan>,
Changed<TextStyle>,
Changed<TextFont>,
Changed<Children>,
Changed<Parent>, // Included to detect broken text block hierarchies.
Added<TextLayout>,
)>,
With<TextSpan>,
With<TextStyle>,
With<TextFont>,
),
>,
mut computed: Query<(
@ -390,7 +412,7 @@ pub fn detect_text_needs_rerender<Root: Component>(
) {
// Root entity:
// - Root component changed.
// - TextStyle on root changed.
// - TextFont on root changed.
// - TextLayout changed.
// - Root children changed (can include additions and removals).
for root in changed_roots.iter() {
@ -404,7 +426,7 @@ pub fn detect_text_needs_rerender<Root: Component>(
// Span entity:
// - Span component changed.
// - Span TextStyle changed.
// - Span TextFont changed.
// - Span children changed (can include additions and removals).
for (entity, maybe_span_parent, has_text_block) in changed_spans.iter() {
if has_text_block {

View file

@ -1,8 +1,8 @@
use crate::pipeline::CosmicFontSystem;
use crate::{
ComputedTextBlock, Font, FontAtlasSets, LineBreak, PositionedGlyph, SwashCache, TextBounds,
TextError, TextLayout, TextLayoutInfo, TextPipeline, TextReader, TextRoot, TextSpanAccess,
TextStyle, TextWriter, YAxisOrientation,
TextColor, TextError, TextFont, TextLayout, TextLayoutInfo, TextPipeline, TextReader, TextRoot,
TextSpanAccess, TextWriter, YAxisOrientation,
};
use bevy_asset::Assets;
use bevy_color::LinearRgba;
@ -44,42 +44,43 @@ use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
/// relative position, which is controlled by the [`Anchor`] component.
/// This means that for a block of text consisting of only one line that doesn't wrap, the `justify` field will have no effect.
///
/*
```
# use bevy_asset::Handle;
# use bevy_color::Color;
# use bevy_color::palettes::basic::BLUE;
# use bevy_ecs::World;
# use bevy_text::{Font, JustifyText, Text2d, TextLayout, TextStyle};
#
# let font_handle: Handle<Font> = Default::default();
# let mut world = World::default();
#
// Basic usage.
world.spawn(Text2d::new("hello world!"));
// With non-default style.
world.spawn((
Text2d::new("hello world!"),
TextStyle {
font: font_handle.clone().into(),
font_size: 60.0,
color: BLUE.into(),
}
));
// With text justification.
world.spawn((
Text2d::new("hello world\nand bevy!"),
TextLayout::new_with_justify(JustifyText::Center)
));
```
*/
///
/// ```
/// # use bevy_asset::Handle;
/// # use bevy_color::Color;
/// # use bevy_color::palettes::basic::BLUE;
/// # use bevy_ecs::world::World;
/// # use bevy_text::{Font, JustifyText, Text2d, TextLayout, TextFont, TextColor};
/// #
/// # let font_handle: Handle<Font> = Default::default();
/// # let mut world = World::default();
/// #
/// // Basic usage.
/// world.spawn(Text2d::new("hello world!"));
///
/// // With non-default style.
/// world.spawn((
/// Text2d::new("hello world!"),
/// TextFont {
/// font: font_handle.clone().into(),
/// font_size: 60.0,
/// ..Default::default()
/// },
/// TextColor(BLUE.into()),
/// ));
///
/// // With text justification.
/// world.spawn((
/// Text2d::new("hello world\nand bevy!"),
/// TextLayout::new_with_justify(JustifyText::Center)
/// ));
/// ```
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect)]
#[reflect(Component, Default, Debug)]
#[require(
TextLayout,
TextStyle,
TextFont,
TextColor,
TextBounds,
Anchor,
SpriteSource,
@ -141,7 +142,7 @@ pub fn extract_text2d_sprite(
&GlobalTransform,
)>,
>,
text_styles: Extract<Query<&TextStyle>>,
text_styles: Extract<Query<(&TextFont, &TextColor)>>,
) {
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows
@ -186,7 +187,7 @@ pub fn extract_text2d_sprite(
.map(|t| t.entity)
.unwrap_or(Entity::PLACEHOLDER),
)
.map(|style| LinearRgba::from(style.color))
.map(|(_, text_color)| LinearRgba::from(text_color.0))
.unwrap_or_default();
current_span = *span_index;
}

View file

@ -1,10 +1,11 @@
use bevy_color::Color;
use bevy_ecs::{
prelude::*,
system::{Query, SystemParam},
};
use bevy_hierarchy::Children;
use crate::{TextSpan, TextStyle};
use crate::{TextColor, TextFont, TextSpan};
/// Helper trait for using the [`TextReader`] and [`TextWriter`] system params.
pub trait TextSpanAccess: Component {
@ -49,13 +50,23 @@ impl TextIterScratch {
pub struct TextReader<'w, 's, R: TextRoot> {
// This is a local to avoid system ambiguities when TextReaders run in parallel.
scratch: Local<'s, TextIterScratch>,
roots: Query<'w, 's, (&'static R, &'static TextStyle, Option<&'static Children>)>,
roots: Query<
'w,
's,
(
&'static R,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
spans: Query<
'w,
's,
(
&'static TextSpan,
&'static TextStyle,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
@ -80,18 +91,24 @@ impl<'w, 's, R: TextRoot> TextReader<'w, 's, R> {
&mut self,
root_entity: Entity,
index: usize,
) -> Option<(Entity, usize, &str, &TextStyle)> {
) -> Option<(Entity, usize, &str, &TextFont, Color)> {
self.iter(root_entity).nth(index)
}
/// Gets the text value of a text span within a text block at a specific index in the flattened span list.
pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option<&str> {
self.get(root_entity, index).map(|(_, _, text, _)| text)
self.get(root_entity, index).map(|(_, _, text, _, _)| text)
}
/// Gets the [`TextStyle`] of a text span within a text block at a specific index in the flattened span list.
pub fn get_style(&mut self, root_entity: Entity, index: usize) -> Option<&TextStyle> {
self.get(root_entity, index).map(|(_, _, _, style)| style)
/// Gets the [`TextFont`] of a text span within a text block at a specific index in the flattened span list.
pub fn get_font(&mut self, root_entity: Entity, index: usize) -> Option<&TextFont> {
self.get(root_entity, index).map(|(_, _, _, font, _)| font)
}
/// Gets the [`TextColor`] of a text span within a text block at a specific index in the flattened span list.
pub fn get_color(&mut self, root_entity: Entity, index: usize) -> Option<Color> {
self.get(root_entity, index)
.map(|(_, _, _, _, color)| color)
}
/// Gets the text value of a text span within a text block at a specific index in the flattened span list.
@ -101,11 +118,18 @@ impl<'w, 's, R: TextRoot> TextReader<'w, 's, R> {
self.get_text(root_entity, index).unwrap()
}
/// Gets the [`TextStyle`] of a text span within a text block at a specific index in the flattened span list.
/// Gets the [`TextFont`] of a text span within a text block at a specific index in the flattened span list.
///
/// Panics if there is no span at the requested index.
pub fn style(&mut self, root_entity: Entity, index: usize) -> &TextStyle {
self.get_style(root_entity, index).unwrap()
pub fn font(&mut self, root_entity: Entity, index: usize) -> &TextFont {
self.get_font(root_entity, index).unwrap()
}
/// Gets the [`TextColor`] of a text span within a text block at a specific index in the flattened span list.
///
/// Panics if there is no span at the requested index.
pub fn color(&mut self, root_entity: Entity, index: usize) -> Color {
self.get_color(root_entity, index).unwrap()
}
}
@ -119,13 +143,23 @@ pub struct TextSpanIter<'a, R: TextRoot> {
root_entity: Option<Entity>,
/// Stack of (children, next index into children).
stack: Vec<(&'a Children, usize)>,
roots: &'a Query<'a, 'a, (&'static R, &'static TextStyle, Option<&'static Children>)>,
roots: &'a Query<
'a,
'a,
(
&'static R,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
spans: &'a Query<
'a,
'a,
(
&'static TextSpan,
&'static TextStyle,
&'static TextFont,
&'static TextColor,
Option<&'static Children>,
),
>,
@ -133,15 +167,15 @@ pub struct TextSpanIter<'a, R: TextRoot> {
impl<'a, R: TextRoot> Iterator for TextSpanIter<'a, R> {
/// Item = (entity in text block, hierarchy depth in the block, span text, span style).
type Item = (Entity, usize, &'a str, &'a TextStyle);
type Item = (Entity, usize, &'a str, &'a TextFont, Color);
fn next(&mut self) -> Option<Self::Item> {
// Root
if let Some(root_entity) = self.root_entity.take() {
if let Ok((text, style, maybe_children)) = self.roots.get(root_entity) {
if let Ok((text, style, color, maybe_children)) = self.roots.get(root_entity) {
if let Some(children) = maybe_children {
self.stack.push((children, 0));
}
return Some((root_entity, 0, text.read_span(), style));
return Some((root_entity, 0, text.read_span(), style, color.0));
}
return None;
}
@ -159,7 +193,7 @@ impl<'a, R: TextRoot> Iterator for TextSpanIter<'a, R> {
*idx += 1;
let entity = *child;
let Ok((span, style, maybe_children)) = self.spans.get(entity) else {
let Ok((span, style, color, maybe_children)) = self.spans.get(entity) else {
continue;
};
@ -167,7 +201,7 @@ impl<'a, R: TextRoot> Iterator for TextSpanIter<'a, R> {
if let Some(children) = maybe_children {
self.stack.push((children, 0));
}
return Some((entity, depth, span.read_span(), style));
return Some((entity, depth, span.read_span(), style, color.0));
}
// All children at this stack entry have been iterated.
@ -191,8 +225,26 @@ impl<'a, R: TextRoot> Drop for TextSpanIter<'a, R> {
pub struct TextWriter<'w, 's, R: TextRoot> {
// This is a resource because two TextWriters can't run in parallel.
scratch: ResMut<'w, TextIterScratch>,
roots: Query<'w, 's, (&'static mut R, &'static mut TextStyle), Without<TextSpan>>,
spans: Query<'w, 's, (&'static mut TextSpan, &'static mut TextStyle), Without<R>>,
roots: Query<
'w,
's,
(
&'static mut R,
&'static mut TextFont,
&'static mut TextColor,
),
Without<TextSpan>,
>,
spans: Query<
'w,
's,
(
&'static mut TextSpan,
&'static mut TextFont,
&'static mut TextColor,
),
Without<R>,
>,
children: Query<'w, 's, &'static Children>,
}
@ -202,15 +254,16 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> {
&mut self,
root_entity: Entity,
index: usize,
) -> Option<(Entity, usize, Mut<String>, Mut<TextStyle>)> {
) -> Option<(Entity, usize, Mut<String>, Mut<TextFont>, Mut<TextColor>)> {
// Root
if index == 0 {
let (text, style) = self.roots.get_mut(root_entity).ok()?;
let (text, font, color) = self.roots.get_mut(root_entity).ok()?;
return Some((
root_entity,
0,
text.map_unchanged(|t| t.write_span()),
style,
font,
color,
));
}
@ -257,18 +310,30 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> {
};
// Note: We do this outside the loop due to borrow checker limitations.
let (text, style) = self.spans.get_mut(entity).unwrap();
Some((entity, depth, text.map_unchanged(|t| t.write_span()), style))
let (text, font, color) = self.spans.get_mut(entity).unwrap();
Some((
entity,
depth,
text.map_unchanged(|t| t.write_span()),
font,
color,
))
}
/// Gets the text value of a text span within a text block at a specific index in the flattened span list.
pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option<Mut<String>> {
self.get(root_entity, index).map(|(_, _, text, _)| text)
self.get(root_entity, index).map(|(_, _, text, ..)| text)
}
/// Gets the [`TextStyle`] of a text span within a text block at a specific index in the flattened span list.
pub fn get_style(&mut self, root_entity: Entity, index: usize) -> Option<Mut<TextStyle>> {
self.get(root_entity, index).map(|(_, _, _, style)| style)
/// Gets the [`TextFont`] of a text span within a text block at a specific index in the flattened span list.
pub fn get_font(&mut self, root_entity: Entity, index: usize) -> Option<Mut<TextFont>> {
self.get(root_entity, index).map(|(_, _, _, font, _)| font)
}
/// Gets the [`TextColor`] of a text span within a text block at a specific index in the flattened span list.
pub fn get_color(&mut self, root_entity: Entity, index: usize) -> Option<Mut<TextColor>> {
self.get(root_entity, index)
.map(|(_, _, _, _, color)| color)
}
/// Gets the text value of a text span within a text block at a specific index in the flattened span list.
@ -278,40 +343,54 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> {
self.get_text(root_entity, index).unwrap()
}
/// Gets the [`TextStyle`] of a text span within a text block at a specific index in the flattened span list.
/// Gets the [`TextFont`] of a text span within a text block at a specific index in the flattened span list.
///
/// Panics if there is no span at the requested index.
pub fn style(&mut self, root_entity: Entity, index: usize) -> Mut<TextStyle> {
self.get_style(root_entity, index).unwrap()
pub fn font(&mut self, root_entity: Entity, index: usize) -> Mut<TextFont> {
self.get_font(root_entity, index).unwrap()
}
/// Gets the [`TextColor`] of a text span within a text block at a specific index in the flattened span list.
///
/// Panics if there is no span at the requested index.
pub fn color(&mut self, root_entity: Entity, index: usize) -> Mut<TextColor> {
self.get_color(root_entity, index).unwrap()
}
/// Invokes a callback on each span in a text block, starting with the root entity.
pub fn for_each(
&mut self,
root_entity: Entity,
mut callback: impl FnMut(Entity, usize, Mut<String>, Mut<TextStyle>),
mut callback: impl FnMut(Entity, usize, Mut<String>, Mut<TextFont>, Mut<TextColor>),
) {
self.for_each_until(root_entity, |a, b, c, d| {
(callback)(a, b, c, d);
self.for_each_until(root_entity, |a, b, c, d, e| {
(callback)(a, b, c, d, e);
true
});
}
/// Invokes a callback on each span's string value in a text block, starting with the root entity.
pub fn for_each_text(&mut self, root_entity: Entity, mut callback: impl FnMut(Mut<String>)) {
self.for_each(root_entity, |_, _, text, _| {
self.for_each(root_entity, |_, _, text, _, _| {
(callback)(text);
});
}
/// Invokes a callback on each span's [`TextStyle`] in a text block, starting with the root entity.
pub fn for_each_style(
/// Invokes a callback on each span's [`TextFont`] in a text block, starting with the root entity.
pub fn for_each_font(&mut self, root_entity: Entity, mut callback: impl FnMut(Mut<TextFont>)) {
self.for_each(root_entity, |_, _, _, font, _| {
(callback)(font);
});
}
/// Invokes a callback on each span's [`TextColor`] in a text block, starting with the root entity.
pub fn for_each_color(
&mut self,
root_entity: Entity,
mut callback: impl FnMut(Mut<TextStyle>),
mut callback: impl FnMut(Mut<TextColor>),
) {
self.for_each(root_entity, |_, _, _, style| {
(callback)(style);
self.for_each(root_entity, |_, _, _, _, color| {
(callback)(color);
});
}
@ -322,17 +401,18 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> {
pub fn for_each_until(
&mut self,
root_entity: Entity,
mut callback: impl FnMut(Entity, usize, Mut<String>, Mut<TextStyle>) -> bool,
mut callback: impl FnMut(Entity, usize, Mut<String>, Mut<TextFont>, Mut<TextColor>) -> bool,
) {
// Root
let Ok((text, style)) = self.roots.get_mut(root_entity) else {
let Ok((text, font, color)) = self.roots.get_mut(root_entity) else {
return;
};
if !(callback)(
root_entity,
0,
text.map_unchanged(|t| t.write_span()),
style,
font,
color,
) {
return;
}
@ -362,11 +442,17 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> {
*idx += 1;
let entity = *child;
let Ok((text, style)) = self.spans.get_mut(entity) else {
let Ok((text, font, color)) = self.spans.get_mut(entity) else {
continue;
};
if !(callback)(entity, depth, text.map_unchanged(|t| t.write_span()), style) {
if !(callback)(
entity,
depth,
text.map_unchanged(|t| t.write_span()),
font,
color,
) {
self.scratch.recover(stack);
return;
}

View file

@ -26,7 +26,7 @@ fn calc_name(
for child in children {
let values = text_reader
.iter(child)
.map(|(_, _, text, _)| text.into())
.map(|(_, _, text, _, _)| text.into())
.collect::<Vec<String>>();
if !values.is_empty() {
name = Some(values.join(" "));
@ -120,7 +120,7 @@ fn label_changed(
for (entity, accessible) in &mut query {
let values = text_reader
.iter(entity)
.map(|(_, _, text, _)| text.into())
.map(|(_, _, text, _, _)| text.into())
.collect::<Vec<String>>();
let name = Some(values.join(" ").into_boxed_str());
if let Some(mut accessible) = accessible {

View file

@ -41,7 +41,7 @@ use bevy_render::{
use bevy_sprite::TextureAtlasLayout;
use bevy_sprite::{BorderRect, ImageScaleMode, SpriteAssetEvents, TextureAtlas};
#[cfg(feature = "bevy_text")]
use bevy_text::{ComputedTextBlock, PositionedGlyph, TextLayoutInfo, TextStyle};
use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo};
use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap;
use box_shadow::BoxShadowPlugin;
@ -606,7 +606,7 @@ pub fn extract_text_sections(
&TextLayoutInfo,
)>,
>,
text_styles: Extract<Query<&TextStyle>>,
text_styles: Extract<Query<&TextColor>>,
mapping: Extract<Query<&RenderEntity>>,
) {
let mut start = 0;
@ -681,7 +681,7 @@ pub fn extract_text_sections(
.map(|t| t.entity)
.unwrap_or(Entity::PLACEHOLDER),
)
.map(|style| LinearRgba::from(style.color))
.map(|text_color| LinearRgba::from(text_color.0))
.unwrap_or_default();
current_span = *span_index;
}

View file

@ -2520,7 +2520,7 @@ impl<'w, 's> DefaultUiCamera<'w, 's> {
/// Marker for controlling whether Ui is rendered with or without anti-aliasing
/// in a camera. By default, Ui is always anti-aliased.
///
/// **Note:** This does not affect text anti-aliasing. For that, use the `font_smoothing` property of the [`TextStyle`](bevy_text::TextStyle) component.
/// **Note:** This does not affect text anti-aliasing. For that, use the `font_smoothing` property of the [`TextFont`](bevy_text::TextFont) component.
///
/// ```
/// use bevy_core_pipeline::prelude::*;

View file

@ -3,6 +3,7 @@ use crate::{
NodeMeasure, Style, TargetCamera, UiScale, ZIndex,
};
use bevy_asset::Assets;
use bevy_color::Color;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
change_detection::DetectChanges,
@ -19,8 +20,8 @@ use bevy_render::{camera::Camera, texture::Image, view::Visibility};
use bevy_sprite::TextureAtlasLayout;
use bevy_text::{
scale_value, ComputedTextBlock, CosmicFontSystem, Font, FontAtlasSets, LineBreak, SwashCache,
TextBounds, TextError, TextLayout, TextLayoutInfo, TextMeasureInfo, TextPipeline, TextReader,
TextRoot, TextSpanAccess, TextStyle, TextWriter, YAxisOrientation,
TextBounds, TextColor, TextError, TextFont, TextLayout, TextLayoutInfo, TextMeasureInfo,
TextPipeline, TextReader, TextRoot, TextSpanAccess, TextWriter, YAxisOrientation,
};
use bevy_transform::components::Transform;
use bevy_utils::{tracing::error, Entry};
@ -56,43 +57,44 @@ impl Default for TextNodeFlags {
///
/// Note that [`Transform`] on this entity is managed automatically by the UI layout system.
///
/*
```
# use bevy_asset::Handle;
# use bevy_color::Color;
# use bevy_color::palettes::basic::BLUE;
# use bevy_ecs::World;
# use bevy_text::{Font, JustifyText, TextLayout, TextStyle};
# use bevy_ui::Text;
#
# let font_handle: Handle<Font> = Default::default();
# let mut world = World::default();
#
// Basic usage.
world.spawn(Text::new("hello world!"));
// With non-default style.
world.spawn((
Text::new("hello world!"),
TextStyle {
font: font_handle.clone().into(),
font_size: 60.0,
color: BLUE.into(),
}
));
// With text justification.
world.spawn((
Text::new("hello world\nand bevy!"),
TextLayout::new_with_justify(JustifyText::Center)
));
```
*/
///
/// ```
/// # use bevy_asset::Handle;
/// # use bevy_color::Color;
/// # use bevy_color::palettes::basic::BLUE;
/// # use bevy_ecs::world::World;
/// # use bevy_text::{Font, JustifyText, TextLayout, TextFont, TextColor};
/// # use bevy_ui::prelude::Text;
/// #
/// # let font_handle: Handle<Font> = Default::default();
/// # let mut world = World::default();
/// #
/// // Basic usage.
/// world.spawn(Text::new("hello world!"));
///
/// // With non-default style.
/// world.spawn((
/// Text::new("hello world!"),
/// TextFont {
/// font: font_handle.clone().into(),
/// font_size: 60.0,
/// ..Default::default()
/// },
/// TextColor(BLUE.into()),
/// ));
///
/// // With text justification.
/// world.spawn((
/// Text::new("hello world\nand bevy!"),
/// TextLayout::new_with_justify(JustifyText::Center)
/// ));
/// ```
#[derive(Component, Debug, Default, Clone, Deref, DerefMut, Reflect)]
#[reflect(Component, Default, Debug)]
#[require(
TextLayout,
TextStyle,
TextFont,
TextColor,
TextNodeFlags,
Node,
Style, // TODO: Remove when Node uses required components.
@ -204,7 +206,7 @@ fn create_text_measure<'a>(
entity: Entity,
fonts: &Assets<Font>,
scale_factor: f64,
spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextStyle)>,
spans: impl Iterator<Item = (Entity, usize, &'a str, &'a TextFont, Color)>,
block: Ref<TextLayout>,
text_pipeline: &mut TextPipeline,
mut content_size: Mut<ContentSize>,

View file

@ -2,7 +2,7 @@
A runtime warning.
Separate font atlases are created for each font and font size. This is expensive, and the memory is never reclaimed when e.g. interpolating `TextStyle::font_size` or `UiScale::scale`.
Separate font atlases are created for each font and font size. This is expensive, and the memory is never reclaimed when e.g. interpolating `TextFont::font_size` or `UiScale::scale`.
If you need to smoothly scale font size, use `Transform::scale`.

View file

@ -14,7 +14,7 @@ fn spawn_sprites(
texture_handle: Handle<Image>,
mut position: Vec3,
slice_border: f32,
style: TextStyle,
style: TextFont,
gap: f32,
) {
let cases = [
@ -100,7 +100,7 @@ fn spawn_sprites(
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
let style = TextStyle {
let style = TextFont {
font: font.clone(),
..default()
};

View file

@ -35,7 +35,7 @@ struct AnimateScale;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
let text_style = TextStyle {
let text_font = TextFont {
font: font.clone(),
font_size: 50.0,
..default()
@ -46,27 +46,27 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Demonstrate changing translation
commands.spawn((
Text2d::new("translation"),
text_style.clone(),
text_font.clone(),
TextLayout::new_with_justify(text_justification),
AnimateTranslation,
));
// Demonstrate changing rotation
commands.spawn((
Text2d::new("rotation"),
text_style.clone(),
text_font.clone(),
TextLayout::new_with_justify(text_justification),
AnimateRotation,
));
// Demonstrate changing scale
commands.spawn((
Text2d::new("scale"),
text_style,
text_font,
TextLayout::new_with_justify(text_justification),
Transform::from_translation(Vec3::new(400.0, 0.0, 0.0)),
AnimateScale,
));
// Demonstrate text wrapping
let slightly_smaller_text_style = TextStyle {
let slightly_smaller_text_font = TextFont {
font,
font_size: 35.0,
..default()
@ -81,7 +81,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|builder| {
builder.spawn((
Text2d::new("this text wraps in the box\n(Unicode linebreaks)"),
slightly_smaller_text_style.clone(),
slightly_smaller_text_font.clone(),
TextLayout::new(JustifyText::Left, LineBreak::WordBoundary),
// Wrap text in the rectangle
TextBounds::from(box_size),
@ -100,7 +100,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|builder| {
builder.spawn((
Text2d::new("this text wraps in the box\n(AnyCharacter linebreaks)"),
slightly_smaller_text_style.clone(),
slightly_smaller_text_font.clone(),
TextLayout::new(JustifyText::Left, LineBreak::AnyCharacter),
// Wrap text in the rectangle
TextBounds::from(other_box_size),
@ -112,7 +112,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Demonstrate font smoothing off
commands.spawn((
Text2d::new("FontSmoothing::None"),
slightly_smaller_text_style
slightly_smaller_text_font
.clone()
.with_font_smoothing(FontSmoothing::None),
Transform::from_translation(Vec3::new(-400.0, -250.0, 0.0)),
@ -126,10 +126,8 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
] {
commands.spawn((
Text2d::new(format!(" Anchor::{text_anchor:?} ")),
TextStyle {
color,
..slightly_smaller_text_style.clone()
},
slightly_smaller_text_font.clone(),
TextColor(color),
Transform::from_translation(250. * Vec3::Y),
text_anchor,
));

View file

@ -121,7 +121,7 @@ fn setup(
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
// padding label text style
let text_style: TextStyle = TextStyle {
let text_style: TextFont = TextFont {
font: font.clone(),
font_size: 42.0,
..default()
@ -184,7 +184,7 @@ fn setup(
];
// label text style
let sampling_label_style = TextStyle {
let sampling_label_style = TextFont {
font,
font_size: 25.0,
..default()
@ -275,7 +275,7 @@ fn create_label(
commands: &mut Commands,
translation: (f32, f32, f32),
text: &str,
text_style: TextStyle,
text_style: TextFont,
) {
commands.spawn((
Text2d::new(text),

View file

@ -127,7 +127,7 @@ fn setup(
..default()
});
let text_style = TextStyle::default();
let text_style = TextFont::default();
commands.spawn((Text::new("Left / Right - Rotate Camera\nC - Toggle Compensation Curve\nM - Toggle Metering Mask\nV - Visualize Metering Mask"),
text_style.clone(), Style {

View file

@ -160,18 +160,12 @@ fn setup(
// Controls Text
// We need the full version of this font so we can use box drawing characters.
let font = asset_server.load("fonts/FiraMono-Medium.ttf");
let text_style = TextStyle {
font: font.clone(),
let text_style = TextFont {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
..default()
};
let label_text_style = TextStyle {
font,
color: ORANGE.into(),
..default()
};
let label_text_style = (text_style.clone(), TextColor(ORANGE.into()));
commands.spawn((Text::new("Up / Down — Increase / Decrease Alpha\nLeft / Right — Rotate Camera\nH - Toggle HDR\nSpacebar — Toggle Unlit\nC — Randomize Colors"),
text_style.clone(),

View file

@ -316,7 +316,7 @@ fn add_help_text(
) {
commands.spawn((
Text::new(create_help_text(currently_selected_option)),
TextStyle {
TextFont {
font: font.clone(),
..default()
},
@ -339,12 +339,12 @@ fn add_text<'a>(
) -> EntityCommands<'a> {
parent.spawn((
Text::new(label),
TextStyle {
TextFont {
font: font.clone(),
font_size: 15.0,
color,
..default()
},
TextColor(color),
))
}
@ -598,8 +598,8 @@ fn update_ui_state(
Color::WHITE
};
writer.for_each_style(entity, |mut style| {
style.color = color;
writer.for_each_color(entity, |mut text_color| {
text_color.0 = color;
});
// Update the displayed value, if this is the currently-selected option.

View file

@ -35,7 +35,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// a place to display the extras on screen
commands.spawn((
Text::default(),
TextStyle {
TextFont {
font_size: 15.,
..default()
},

View file

@ -60,7 +60,7 @@ fn setup(
// labels
commands.spawn((
Text::new("Perceptual Roughness"),
TextStyle {
TextFont {
font_size: 30.0,
..default()
},
@ -74,7 +74,7 @@ fn setup(
commands.spawn((
Text::new("Metallic"),
TextStyle {
TextFont {
font_size: 30.0,
..default()
},
@ -92,7 +92,7 @@ fn setup(
commands.spawn((
Text::new("Loading Environment Map..."),
TextStyle {
TextFont {
font_size: 30.0,
..default()
},

View file

@ -172,11 +172,11 @@ fn setup_image_viewer_scene(
commands.spawn((
Text::new("Drag and drop an HDR or EXR file"),
TextStyle {
TextFont {
font_size: 36.0,
color: Color::BLACK,
..default()
},
TextColor(Color::BLACK),
TextLayout::new_with_justify(JustifyText::Center),
Style {
align_self: AlignSelf::Center,

View file

@ -40,7 +40,7 @@ fn main() {
}
impl AnimatableProperty for FontSizeProperty {
type Component = TextStyle;
type Component = TextFont;
type Property = f32;
@ -50,12 +50,12 @@ impl AnimatableProperty for FontSizeProperty {
}
impl AnimatableProperty for TextColorProperty {
type Component = TextStyle;
type Component = TextColor;
type Property = Srgba;
fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> {
match component.color {
match component.0 {
Color::Srgba(ref mut color) => Some(color),
_ => None,
}
@ -172,12 +172,12 @@ fn setup(
builder
.spawn((
Text::new("Bevy"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 24.0,
color: Color::Srgba(Srgba::RED),
..default()
},
TextColor(Color::Srgba(Srgba::RED)),
TextLayout::new_with_justify(JustifyText::Center),
))
// Mark as an animation target.

View file

@ -37,12 +37,12 @@ impl AnimationEvent for MessageEvent {
fn edit_message(
mut event_reader: EventReader<MessageEvent>,
text: Single<(&mut Text2d, &mut TextStyle), With<MessageText>>,
text: Single<(&mut Text2d, &mut TextColor), With<MessageText>>,
) {
let (mut text, mut style) = text.into_inner();
let (mut text, mut color) = text.into_inner();
for event in event_reader.read() {
text.0 = event.value.clone();
style.color = event.color;
color.0 = event.color;
}
}
@ -69,11 +69,11 @@ fn setup(
commands.spawn((
MessageText,
Text2d::default(),
TextStyle {
TextFont {
font_size: 119.0,
color: Color::NONE,
..default()
},
TextColor(Color::NONE),
));
// Create a new animation clip.
@ -108,9 +108,9 @@ fn setup(
}
// Slowly fade out the text opacity.
fn animate_text_opacity(mut styles: Query<&mut TextStyle>, time: Res<Time>) {
for mut style in &mut styles {
let a = style.color.alpha();
style.color.set_alpha(a - time.delta_seconds());
fn animate_text_opacity(mut colors: Query<&mut TextColor>, time: Res<Time>) {
for mut color in &mut colors {
let a = color.0.alpha();
color.0.set_alpha(a - time.delta_seconds());
}
}

View file

@ -272,11 +272,11 @@ fn setup_node_rects(commands: &mut Commands) {
let text = commands
.spawn((
Text::new(node_string),
TextStyle {
TextFont {
font_size: 16.0,
color: ANTIQUE_WHITE.into(),
..default()
},
TextColor(ANTIQUE_WHITE.into()),
TextLayout::new_with_justify(JustifyText::Center),
))
.id();

View file

@ -237,19 +237,18 @@ fn setup_ui(mut commands: Commands) {
// The button will automatically become a child of the parent that owns the
// given `ChildBuilder`.
fn add_mask_group_control(parent: &mut ChildBuilder, label: &str, width: Val, mask_group_id: u32) {
let button_text_style = TextStyle {
font_size: 14.0,
color: Color::WHITE,
..default()
};
let selected_button_text_style = TextStyle {
color: Color::BLACK,
..button_text_style.clone()
};
let label_text_style = TextStyle {
color: Color::Srgba(LIGHT_GRAY),
..button_text_style.clone()
};
let button_text_style = (
TextFont {
font_size: 14.0,
..default()
},
TextColor::WHITE,
);
let selected_button_text_style = (button_text_style.0.clone(), TextColor::BLACK);
let label_text_style = (
button_text_style.0.clone(),
TextColor(Color::Srgba(LIGHT_GRAY)),
);
parent
.spawn(NodeBundle {
@ -493,8 +492,8 @@ fn update_ui(
continue;
};
writer.for_each_style(text, |mut style| {
style.color = if enabled { Color::BLACK } else { Color::WHITE };
writer.for_each_color(text, |mut color| {
color.0 = if enabled { Color::BLACK } else { Color::WHITE };
});
}
}

View file

@ -16,7 +16,7 @@ fn main() {
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
let text_style = TextStyle {
let text_font = TextFont {
font_size: 10.0,
..default()
};
@ -64,10 +64,8 @@ fn setup(mut commands: Commands) {
commands
.spawn((
Text2d(format!("{:?}", function)),
TextStyle {
color,
..text_style.clone()
},
text_font.clone(),
TextColor(color),
Transform::from_xyz(
i as f32 * 113.0 - 1280.0 / 2.0 + 25.0,
-100.0 - ((j as f32 * 250.0) - 300.0),

View file

@ -182,11 +182,11 @@ fn setup_ui(mut commands: Commands) {
.with_children(|b| {
b.spawn((
Text::new("Loading...".to_owned()),
TextStyle {
TextFont {
font_size: 53.0,
color: Color::BLACK,
..Default::default()
},
TextColor(Color::BLACK),
TextLayout::new_with_justify(JustifyText::Right),
LoadingText,
));

View file

@ -54,12 +54,9 @@ fn read_stream(receiver: Res<StreamReceiver>, mut events: EventWriter<StreamEven
}
fn spawn_text(mut commands: Commands, mut reader: EventReader<StreamEvent>) {
let text_style = TextStyle::default();
for (per_frame, event) in reader.read().enumerate() {
commands.spawn((
Text2d::new(event.0.to_string()),
text_style.clone(),
TextLayout::new_with_justify(JustifyText::Center),
Transform::from_xyz(per_frame as f32 * 100.0, 300.0, 0.0),
));

View file

@ -19,16 +19,16 @@ fn main() {
DefaultPlugins,
FpsOverlayPlugin {
config: FpsOverlayConfig {
text_config: TextStyle {
text_config: TextFont {
// Here we define size of our overlay
font_size: 42.0,
// We can also change color of the overlay
color: OverlayColor::GREEN,
// If we want, we can use a custom font
font: default(),
// We could also disable font smoothing,
font_smoothing: FontSmoothing::default(),
},
// We can also change color of the overlay
text_color: OverlayColor::GREEN,
enabled: true,
},
},
@ -67,10 +67,10 @@ fn setup(mut commands: Commands) {
fn customize_config(input: Res<ButtonInput<KeyCode>>, mut overlay: ResMut<FpsOverlayConfig>) {
if input.just_pressed(KeyCode::Digit1) {
// Changing resource will affect overlay
if overlay.text_config.color == OverlayColor::GREEN {
overlay.text_config.color = OverlayColor::RED;
if overlay.text_color == OverlayColor::GREEN {
overlay.text_color = OverlayColor::RED;
} else {
overlay.text_config.color = OverlayColor::GREEN;
overlay.text_color = OverlayColor::GREEN;
}
}
if input.just_pressed(KeyCode::Digit2) {

View file

@ -76,10 +76,6 @@ fn setup(mut commands: Commands) {
"Click on a \"Mine\" to trigger it.\n\
When it explodes it will trigger all overlapping mines.",
),
TextStyle {
color: Color::WHITE,
..default()
},
Style {
position_type: PositionType::Absolute,
top: Val::Px(12.),

View file

@ -106,10 +106,7 @@ fn setup_ui(mut commands: Commands) {
p.spawn(TextSpan::new("Last Triggered: "));
p.spawn((
TextSpan::new("-"),
TextStyle {
color: bevy::color::palettes::css::ORANGE.into(),
..default()
},
TextColor(bevy::color::palettes::css::ORANGE.into()),
));
});
}

View file

@ -177,11 +177,11 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
// scoreboard
commands.spawn((
Text::new("Score:"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.5, 0.5, 1.0),
..default()
},
TextColor(Color::srgb(0.5, 0.5, 1.0)),
Style {
position_type: PositionType::Absolute,
top: Val::Px(5.0),
@ -405,10 +405,10 @@ fn display_score(mut commands: Commands, game: Res<Game>) {
})
.with_child((
Text::new(format!("Cake eaten: {}", game.cake_eaten)),
TextStyle {
TextFont {
font_size: 67.0,
color: Color::srgb(0.5, 0.5, 1.0),
..default()
},
TextColor(Color::srgb(0.5, 0.5, 1.0)),
));
}

View file

@ -219,11 +219,11 @@ fn setup(
commands
.spawn((
Text::new("Score: "),
TextStyle {
TextFont {
font_size: SCOREBOARD_FONT_SIZE,
color: TEXT_COLOR,
..default()
},
TextColor(TEXT_COLOR),
ScoreboardUi,
Style {
position_type: PositionType::Absolute,
@ -234,11 +234,11 @@ fn setup(
))
.with_child((
TextSpan::default(),
TextStyle {
TextFont {
font_size: SCOREBOARD_FONT_SIZE,
color: SCORE_COLOR,
..default()
},
TextColor(SCORE_COLOR),
));
// Walls

View file

@ -128,7 +128,7 @@ fn setup_contributor_selection(mut commands: Commands, asset_server: Res<AssetSe
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
let text_style = TextStyle {
let text_style = TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 60.0,
..default()
@ -148,7 +148,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
))
.with_child((
TextSpan::default(),
TextStyle {
TextFont {
font_size: 30.,
..text_style
},
@ -216,7 +216,7 @@ fn select(
contributor.num_commits,
if contributor.num_commits > 1 { "s" } else { "" }
);
writer.style(entity, 0).color = sprite.color;
writer.color(entity, 0).0 = sprite.color;
}
/// Change the tint color to the "deselected" color and push

View file

@ -107,7 +107,7 @@ fn setup(
// Spawn the text instructions
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
let text_style = TextStyle {
let text_style = TextFont {
font: font.clone(),
font_size: 25.0,
..default()

View file

@ -176,11 +176,11 @@ mod game {
.with_children(|p| {
p.spawn((
Text::new("Will be back to the menu shortly..."),
TextStyle {
TextFont {
font_size: 67.0,
color: TEXT_COLOR,
..default()
},
TextColor(TEXT_COLOR),
Style {
margin: UiRect::all(Val::Px(50.0)),
..default()
@ -196,27 +196,27 @@ mod game {
.with_children(|p| {
p.spawn((
TextSpan(format!("quality: {:?}", *display_quality)),
TextStyle {
TextFont {
font_size: 50.0,
color: BLUE.into(),
..default()
},
TextColor(BLUE.into()),
));
p.spawn((
TextSpan::new(" - "),
TextStyle {
TextFont {
font_size: 50.0,
color: TEXT_COLOR,
..default()
},
TextColor(TEXT_COLOR),
));
p.spawn((
TextSpan(format!("volume: {:?}", *volume)),
TextStyle {
TextFont {
font_size: 50.0,
color: LIME.into(),
..default()
},
TextColor(LIME.into()),
));
});
});
@ -398,9 +398,8 @@ mod menu {
left: Val::Px(10.0),
..default()
};
let button_text_style = TextStyle {
let button_text_font = TextFont {
font_size: 33.0,
color: TEXT_COLOR,
..default()
};
@ -433,11 +432,11 @@ mod menu {
// Display the game name
parent.spawn((
Text::new("Bevy Game Menu UI"),
TextStyle {
TextFont {
font_size: 67.0,
color: TEXT_COLOR,
..default()
},
TextColor(TEXT_COLOR),
Style {
margin: UiRect::all(Val::Px(50.0)),
..default()
@ -464,7 +463,11 @@ mod menu {
image: UiImage::new(icon),
..default()
});
parent.spawn((Text::new("New Game"), button_text_style.clone()));
parent.spawn((
Text::new("New Game"),
button_text_font.clone(),
TextColor(TEXT_COLOR),
));
});
parent
.spawn((
@ -482,7 +485,11 @@ mod menu {
image: UiImage::new(icon),
..default()
});
parent.spawn((Text::new("Settings"), button_text_style.clone()));
parent.spawn((
Text::new("Settings"),
button_text_font.clone(),
TextColor(TEXT_COLOR),
));
});
parent
.spawn((
@ -500,7 +507,11 @@ mod menu {
image: UiImage::new(icon),
..default()
});
parent.spawn((Text::new("Quit"), button_text_style));
parent.spawn((
Text::new("Quit"),
button_text_font,
TextColor(TEXT_COLOR),
));
});
});
});
@ -516,11 +527,13 @@ mod menu {
..default()
};
let button_text_style = TextStyle {
font_size: 33.0,
color: TEXT_COLOR,
..default()
};
let button_text_style = (
TextFont {
font_size: 33.0,
..default()
},
TextColor(TEXT_COLOR),
);
commands
.spawn((
@ -579,11 +592,13 @@ mod menu {
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font_size: 33.0,
color: TEXT_COLOR,
..default()
};
let button_text_style = (
TextFont {
font_size: 33.0,
..default()
},
TextColor(TEXT_COLOR),
);
commands
.spawn((
@ -683,11 +698,13 @@ mod menu {
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font_size: 33.0,
color: TEXT_COLOR,
..default()
};
let button_text_style = (
TextFont {
font_size: 33.0,
..default()
},
TextColor(TEXT_COLOR),
);
commands
.spawn((

View file

@ -77,7 +77,7 @@ fn setup(mut commands: Commands) {
commands.insert_resource(level_data);
// Spawns the UI that will show the user prompts.
let text_style = TextStyle {
let text_style = TextFont {
font_size: 42.0,
..default()
};
@ -239,7 +239,7 @@ struct LoadingScreen;
// Spawns the necessary components for the loading screen.
fn load_loading_screen(mut commands: Commands) {
let text_style = TextStyle {
let text_style = TextFont {
font_size: 67.0,
..default()
};

View file

@ -116,11 +116,11 @@ fn build_ui(
let schedule = schedules.get(*label).unwrap();
text_spans.push((
TextSpan(format!("{label:?}\n")),
TextStyle {
TextFont {
font: asset_server.load(FONT_BOLD),
color: FONT_COLOR,
..default()
},
TextColor(FONT_COLOR),
));
// grab the list of systems in the schedule, in the order the
@ -144,19 +144,15 @@ fn build_ui(
// Add a text section for displaying the cursor for this system
text_spans.push((
TextSpan::new(" "),
TextStyle {
color: FONT_COLOR,
..default()
},
TextFont::default(),
TextColor(FONT_COLOR),
));
// add the name of the system to the ui
text_spans.push((
TextSpan(format!("{}\n", system.name())),
TextStyle {
color: FONT_COLOR,
..default()
},
TextFont::default(),
TextColor(FONT_COLOR),
));
}
}
@ -196,11 +192,11 @@ fn build_stepping_hint(mut commands: Commands) {
// stepping description box
commands.spawn((
Text::new(hint_text),
TextStyle {
TextFont {
font_size: 15.0,
color: FONT_COLOR,
..default()
},
TextColor(FONT_COLOR),
Style {
position_type: PositionType::Absolute,
bottom: Val::Px(5.0),

View file

@ -134,11 +134,11 @@ pub fn spawn_ui_text<'a>(
) -> EntityCommands<'a> {
parent.spawn((
Text::new(label),
TextStyle {
TextFont {
font_size: 18.0,
color,
..default()
},
TextColor(color),
))
}
@ -166,12 +166,12 @@ pub fn update_ui_radio_button(background_color: &mut BackgroundColor, selected:
background_color.0 = if selected { Color::WHITE } else { Color::BLACK };
}
/// Updates the style of the label of a radio button to reflect its selected
/// Updates the color of the label of a radio button to reflect its selected
/// status.
pub fn update_ui_radio_button_text(entity: Entity, writer: &mut UiTextWriter, selected: bool) {
let text_color = if selected { Color::BLACK } else { Color::WHITE };
writer.for_each_style(entity, |mut style| {
style.color = text_color;
writer.for_each_color(entity, |mut color| {
color.0 = text_color;
});
}

View file

@ -55,7 +55,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
p.spawn(TextSpan::new("IME Buffer: "));
p.spawn((
TextSpan::new("\n"),
TextStyle {
TextFont {
font: font.clone(),
..default()
},
@ -64,7 +64,7 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
Text2d::new(""),
TextStyle {
TextFont {
font,
font_size: 100.0,
..default()
@ -137,7 +137,7 @@ fn listen_ime_events(
fn listen_keyboard_input_events(
mut commands: Commands,
mut events: EventReader<KeyboardInput>,
mut edit_text: Query<(&mut Text2d, &TextStyle), (Without<Node>, Without<Bubble>)>,
mut edit_text: Query<(&mut Text2d, &TextFont), (Without<Node>, Without<Bubble>)>,
) {
for event in events.read() {
// Only trigger changes when the key is first pressed.

View file

@ -75,7 +75,7 @@ fn setup(mut commands: Commands) {
C: Toggle cyclic curve construction";
let spline_mode_text = format!("Spline: {spline_mode}");
let cycling_mode_text = format!("{cycling_mode}");
let style = TextStyle::default();
let style = TextFont::default();
commands
.spawn(NodeBundle {

View file

@ -132,11 +132,11 @@ fn setup_scene(
})
.with_child((
Text::new("Test Button"),
TextStyle {
TextFont {
font_size: 30.0,
color: Color::BLACK,
..default()
},
TextColor::BLACK,
TextLayout::new_with_justify(JustifyText::Center),
));
}

View file

@ -156,7 +156,7 @@ fn spawn_text(mut commands: Commands) {
})
.with_child((
Text::new("Move the player with WASD"),
TextStyle {
TextFont {
font_size: 25.0,
..default()
},

View file

@ -42,15 +42,15 @@ fn setup(
},
)
.observe(
|evt: Trigger<Pointer<Out>>, mut texts: Query<&mut TextStyle>| {
let mut style = texts.get_mut(evt.entity()).unwrap();
style.color = Color::WHITE;
|evt: Trigger<Pointer<Out>>, mut texts: Query<&mut TextColor>| {
let mut color = texts.get_mut(evt.entity()).unwrap();
color.0 = Color::WHITE;
},
)
.observe(
|evt: Trigger<Pointer<Over>>, mut texts: Query<&mut TextStyle>| {
let mut style = texts.get_mut(evt.entity()).unwrap();
style.color = CYAN_400.into();
|evt: Trigger<Pointer<Over>>, mut texts: Query<&mut TextColor>| {
let mut color = texts.get_mut(evt.entity()).unwrap();
color.0 = CYAN_400.into();
},
);
// circular base

View file

@ -148,7 +148,7 @@ fn infotext_system(mut commands: Commands) {
commands.spawn(Camera2d);
commands.spawn((
Text::new("Nothing to see in this window! Check the console output!"),
TextStyle {
TextFont {
font_size: 42.0,
..default()
},

View file

@ -229,8 +229,8 @@ fn toggle_prepass_view(
};
let text = text.single();
*writer.text(text, 1) = format!("Prepass Output: {label}\n");
writer.for_each_style(text, |mut style| {
style.color = Color::WHITE;
writer.for_each_color(text, |mut color| {
color.0 = Color::WHITE;
});
let handle = material_handle.single();

View file

@ -370,11 +370,11 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("Play"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
@ -402,11 +402,11 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("Tutorial"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
})
@ -500,11 +500,11 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("Paused"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
});
@ -532,11 +532,11 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("TURBO MODE"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.3, 0.1),
..default()
},
TextColor(Color::srgb(0.9, 0.3, 0.1)),
));
});
}
@ -574,37 +574,37 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("Move the bevy logo with the arrow keys"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.3, 0.3, 0.7),
..default()
},
TextColor(Color::srgb(0.3, 0.3, 0.7)),
));
parent.spawn((
Text::new("Press T to enter TURBO MODE"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.3, 0.3, 0.7),
..default()
},
TextColor(Color::srgb(0.3, 0.3, 0.7)),
));
parent.spawn((
Text::new("Press SPACE to pause"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.3, 0.3, 0.7),
..default()
},
TextColor(Color::srgb(0.3, 0.3, 0.7)),
));
parent.spawn((
Text::new("Press ESCAPE to return to the menu"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.3, 0.3, 0.7),
..default()
},
TextColor(Color::srgb(0.3, 0.3, 0.7)),
));
});
}
@ -631,20 +631,20 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("Press SPACE to resume"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.3, 0.3, 0.7),
..default()
},
TextColor(Color::srgb(0.3, 0.3, 0.7)),
));
parent.spawn((
Text::new("Press ESCAPE to return to the menu"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.3, 0.3, 0.7),
..default()
},
TextColor(Color::srgb(0.3, 0.3, 0.7)),
));
});
}

View file

@ -272,11 +272,11 @@ fn setup_menu(mut commands: Commands) {
.with_children(|parent| {
parent.spawn((
Text::new("Play"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
})

View file

@ -80,11 +80,11 @@ fn setup_menu(mut commands: Commands) {
.with_children(|parent| {
parent.spawn((
Text::new("Play"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
})

View file

@ -185,11 +185,11 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("Play"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
})
@ -237,11 +237,11 @@ mod ui {
.with_children(|parent| {
parent.spawn((
Text::new("Paused"),
TextStyle {
TextFont {
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
});

View file

@ -255,16 +255,9 @@ fn setup(
transform_rng: ChaCha8Rng::seed_from_u64(42),
};
let lime_text = TextStyle {
let font = TextFont {
font_size: 40.0,
color: LIME.into(),
..default()
};
let aqua_text = TextStyle {
font_size: 40.0,
color: LIME.into(),
..default()
..Default::default()
};
commands.spawn(Camera2d);
@ -283,14 +276,30 @@ fn setup(
))
.with_children(|p| {
p.spawn((Text::default(), StatsText)).with_children(|p| {
p.spawn((TextSpan::new("Bird Count: "), lime_text.clone()));
p.spawn((TextSpan::new(""), aqua_text.clone()));
p.spawn((TextSpan::new("\nFPS (raw): "), lime_text.clone()));
p.spawn((TextSpan::new(""), aqua_text.clone()));
p.spawn((TextSpan::new("\nFPS (SMA): "), lime_text.clone()));
p.spawn((TextSpan::new(""), aqua_text.clone()));
p.spawn((TextSpan::new("\nFPS (EMA): "), lime_text.clone()));
p.spawn((TextSpan::new(""), aqua_text.clone()));
p.spawn((
TextSpan::new("Bird Count: "),
font.clone(),
TextColor(LIME.into()),
));
p.spawn((TextSpan::new(""), font.clone(), TextColor(AQUA.into())));
p.spawn((
TextSpan::new("\nFPS (raw): "),
font.clone(),
TextColor(LIME.into()),
));
p.spawn((TextSpan::new(""), font.clone(), TextColor(AQUA.into())));
p.spawn((
TextSpan::new("\nFPS (SMA): "),
font.clone(),
TextColor(LIME.into()),
));
p.spawn((TextSpan::new(""), font.clone(), TextColor(AQUA.into())));
p.spawn((
TextSpan::new("\nFPS (EMA): "),
font.clone(),
TextColor(LIME.into()),
));
p.spawn((TextSpan::new(""), font.clone(), TextColor(AQUA.into())));
});
});

View file

@ -5,6 +5,7 @@ use bevy::{
color::palettes::css::ORANGE_RED,
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
text::TextColor,
window::{PresentMode, WindowResolution},
winit::{UpdateMode, WinitSettings},
};
@ -69,7 +70,7 @@ fn main() {
focused_mode: UpdateMode::Continuous,
unfocused_mode: UpdateMode::Continuous,
})
.add_systems(Update, button_system);
.add_systems(Update, (button_system, set_text_colors_changed));
if args.grid {
app.add_systems(Startup, setup_grid);
@ -96,6 +97,12 @@ fn main() {
app.insert_resource(args).run();
}
fn set_text_colors_changed(mut colors: Query<&mut TextColor>) {
for mut text_color in colors.iter_mut() {
text_color.set_changed();
}
}
#[derive(Component)]
struct IdleColor(Color);
@ -264,11 +271,11 @@ fn spawn_button(
builder.with_children(|parent| {
parent.spawn((
Text(format!("{column}, {row}")),
TextStyle {
TextFont {
font_size: FONT_SIZE,
color: Color::srgb(0.2, 0.2, 0.2),
..default()
},
TextColor(Color::srgb(0.2, 0.2, 0.2)),
));
});
}

View file

@ -46,7 +46,7 @@ fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
let text_string = "0123456789".repeat(10_000);
let text_style = TextStyle {
let text_font = TextFont {
font_size: 4.,
..Default::default()
};
@ -74,15 +74,12 @@ fn setup(mut commands: Commands) {
},
..Default::default()
})
.with_child((Text(text_string.clone()), text_style.clone(), text_block));
.with_child((Text(text_string.clone()), text_font.clone(), text_block));
});
commands.spawn((
Text2d::new(text_string),
TextStyle {
color: RED.into(),
..text_style
},
TextColor(RED.into()),
bevy::sprite::Anchor::Center,
TextBounds::new_horizontal(1000.),
text_block,

View file

@ -44,21 +44,21 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
[
(
TextSpan("text".repeat(i)),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: (4 + i % 10) as f32,
color: BLUE.into(),
..Default::default()
},
TextColor(BLUE.into()),
),
(
TextSpan("pipeline".repeat(i)),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: (4 + i % 11) as f32,
color: YELLOW.into(),
..default()
},
TextColor(YELLOW.into()),
),
]
};

View file

@ -93,7 +93,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut time: ResMu
// real time info
builder.spawn((
Text::default(),
TextStyle {
TextFont {
font_size,
..default()
},
@ -103,22 +103,22 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut time: ResMu
// keybindings
builder.spawn((
Text::new("CONTROLS\nUn/Pause: Space\nSpeed+: Up\nSpeed-: Down"),
TextStyle {
TextFont {
font_size,
color: Color::srgb(0.85, 0.85, 0.85),
..default()
},
TextColor(Color::srgb(0.85, 0.85, 0.85)),
TextLayout::new_with_justify(JustifyText::Center),
));
// virtual time info
builder.spawn((
Text::default(),
TextStyle {
TextFont {
font_size,
color: virtual_color,
..default()
},
TextColor(virtual_color),
TextLayout::new_with_justify(JustifyText::Right),
VirtualTime,
));

View file

@ -297,7 +297,7 @@ fn setup_sticks(
Transform::from_xyz(dead_mid, dead_mid, 3.),
));
// text
let style = TextStyle {
let style = TextFont {
font_size: 13.,
..default()
};
@ -362,7 +362,7 @@ fn setup_triggers(
parent.spawn((
Transform::from_xyz(0., 0., 1.),
Text(format!("{:.3}", 0.)),
TextStyle {
TextFont {
font_size: 13.,
..default()
},

View file

@ -122,7 +122,7 @@ impl fmt::Display for Target {
}
}
impl Target {
fn text_span(&self, key: &str, style: TextStyle) -> (String, TextStyle) {
fn text_span(&self, key: &str, style: TextFont) -> (String, TextFont) {
(format!("[{key}] {self}\n"), style)
}
fn new(
@ -253,7 +253,7 @@ fn detect_morphs(
detected.extend(targets);
}
detected.truncate(AVAILABLE_KEYS.len());
let style = TextStyle {
let style = TextFont {
font_size: 13.0,
..default()
};

View file

@ -161,7 +161,7 @@ fn setup(mut commands: Commands) {
let label_node = commands
.spawn((
Text::new(label),
TextStyle {
TextFont {
font_size: 9.0,
..Default::default()
},
@ -230,7 +230,7 @@ fn setup(mut commands: Commands) {
let label_node = commands
.spawn((
Text::new(label),
TextStyle {
TextFont {
font_size: 9.0,
..Default::default()
},
@ -267,7 +267,7 @@ fn setup(mut commands: Commands) {
.with_children(|builder| {
builder.spawn((
Text::new("Borders"),
TextStyle {
TextFont {
font_size: 20.0,
..Default::default()
},
@ -292,7 +292,7 @@ fn setup(mut commands: Commands) {
.with_children(|builder| {
builder.spawn((
Text::new("Borders Rounded"),
TextStyle {
TextFont {
font_size: 20.0,
..Default::default()
},

View file

@ -85,12 +85,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
})
.with_child((
Text::new("Button"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
}

View file

@ -76,7 +76,7 @@ impl TargetUpdate for Target<Visibility> {
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let palette: [Color; 4] = PALETTE.map(|hex| Srgba::hex(hex).unwrap().into());
let text_style = TextStyle {
let text_font = TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
..default()
};
@ -95,7 +95,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
..Default::default()
}).with_children(|parent| {
parent.spawn((Text::new("Use the panel on the right to change the Display and Visibility properties for the respective nodes of the panel on the left"),
text_style.clone(),
text_font.clone(),
TextLayout::new_with_justify(JustifyText::Center),
Style {
margin: UiRect::bottom(Val::Px(10.)),
@ -133,7 +133,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
},
..Default::default()
}).with_children(|parent| {
spawn_right_panel(parent, text_style, &palette, target_ids);
spawn_right_panel(parent, text_font, &palette, target_ids);
});
});
@ -147,21 +147,23 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
},
..default() })
.with_children(|builder| {
let text_style = TextStyle {
let text_font = TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
..default()
};
builder.spawn((Text::new("Display::None\nVisibility::Hidden\nVisibility::Inherited"),
TextStyle { color: HIDDEN_COLOR, ..text_style.clone() },
text_font.clone(),
TextColor(HIDDEN_COLOR),
TextLayout::new_with_justify(JustifyText::Center),
));
builder.spawn((Text::new("-\n-\n-"),
TextStyle { color: DARK_GRAY.into(), ..text_style.clone() },
text_font.clone(),
TextColor(DARK_GRAY.into()),
TextLayout::new_with_justify(JustifyText::Center),
));
builder.spawn((Text::new("The UI Node and its descendants will not be visible and will not be allotted any space in the UI layout.\nThe UI Node will not be visible but will still occupy space in the UI layout.\nThe UI node will inherit the visibility property of its parent. If it has no parent it will be visible."),
text_style
text_font
));
});
});
@ -275,13 +277,13 @@ fn spawn_left_panel(builder: &mut ChildBuilder, palette: &[Color; 4]) -> Vec<Ent
fn spawn_right_panel(
parent: &mut ChildBuilder,
text_style: TextStyle,
text_font: TextFont,
palette: &[Color; 4],
mut target_ids: Vec<Entity>,
) {
let spawn_buttons = |parent: &mut ChildBuilder, target_id| {
spawn_button::<Display>(parent, text_style.clone(), target_id);
spawn_button::<Visibility>(parent, text_style.clone(), target_id);
spawn_button::<Display>(parent, text_font.clone(), target_id);
spawn_button::<Visibility>(parent, text_font.clone(), target_id);
};
parent
.spawn(NodeBundle {
@ -392,7 +394,7 @@ fn spawn_right_panel(
});
}
fn spawn_button<T>(parent: &mut ChildBuilder, text_style: TextStyle, target: Entity)
fn spawn_button<T>(parent: &mut ChildBuilder, text_font: TextFont, target: Entity)
where
T: Default + std::fmt::Debug + Send + Sync + 'static,
Target<T>: TargetUpdate,
@ -413,7 +415,7 @@ where
.with_children(|builder| {
builder.spawn((
Text(format!("{}::{:?}", Target::<T>::NAME, T::default())),
text_style,
text_font,
TextLayout::new_with_justify(JustifyText::Center),
));
});
@ -422,7 +424,7 @@ where
fn buttons_handler<T>(
mut left_panel_query: Query<&mut <Target<T> as TargetUpdate>::TargetComponent>,
mut visibility_button_query: Query<(&Target<T>, &Interaction, &Children), Changed<Interaction>>,
mut text_query: Query<(&mut Text, &mut TextStyle)>,
mut text_query: Query<(&mut Text, &mut TextColor)>,
) where
T: Send + Sync,
Target<T>: TargetUpdate + Component,
@ -431,9 +433,9 @@ fn buttons_handler<T>(
if matches!(interaction, Interaction::Pressed) {
let mut target_value = left_panel_query.get_mut(target.id).unwrap();
for &child in children {
if let Ok((mut text, mut style)) = text_query.get_mut(child) {
if let Ok((mut text, mut text_color)) = text_query.get_mut(child) {
**text = target.update_target(target_value.as_mut());
style.color = if text.contains("None") || text.contains("Hidden") {
text_color.0 = if text.contains("None") || text.contains("Hidden") {
Color::srgb(1.0, 0.7, 0.7)
} else {
Color::WHITE
@ -446,24 +448,24 @@ fn buttons_handler<T>(
fn text_hover(
mut button_query: Query<(&Interaction, &mut BackgroundColor, &Children), Changed<Interaction>>,
mut text_query: Query<(&Text, &mut TextStyle)>,
mut text_query: Query<(&Text, &mut TextColor)>,
) {
for (interaction, mut color, children) in button_query.iter_mut() {
match interaction {
Interaction::Hovered => {
*color = Color::BLACK.with_alpha(0.6).into();
for &child in children {
if let Ok((_, mut style)) = text_query.get_mut(child) {
if let Ok((_, mut text_color)) = text_query.get_mut(child) {
// Bypass change detection to avoid recomputation of the text when only changing the color
style.bypass_change_detection().color = YELLOW.into();
text_color.bypass_change_detection().0 = YELLOW.into();
}
}
}
_ => {
*color = Color::BLACK.with_alpha(0.5).into();
for &child in children {
if let Ok((text, mut style)) = text_query.get_mut(child) {
style.bypass_change_detection().color =
if let Ok((text, mut text_color)) = text_query.get_mut(child) {
text_color.bypass_change_detection().0 =
if text.contains("None") || text.contains("Hidden") {
HIDDEN_COLOR
} else {

View file

@ -175,11 +175,8 @@ fn spawn_nested_text_bundle(
.with_children(|builder| {
builder.spawn((
Text::new(text),
TextStyle {
font,
color: Color::BLACK,
..default()
},
TextFont { font, ..default() },
TextColor::BLACK,
));
});
}

View file

@ -98,12 +98,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut state: ResM
.with_children(|parent| {
parent.spawn((
Text::new("a"),
TextStyle {
TextFont {
font: font_handle,
font_size: 50.0,
color: YELLOW.into(),
..default()
},
TextColor(YELLOW.into()),
));
});
// We're seeding the PRNG here to make this example deterministic for testing purposes.

View file

@ -87,15 +87,15 @@ fn create_button() -> ButtonBundle {
}
}
fn create_label(text: &str, font: Handle<Font>) -> (Text, TextStyle) {
fn create_label(text: &str, font: Handle<Font>) -> (Text, TextFont, TextColor) {
(
Text::new(text),
TextStyle {
TextFont {
font,
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
)
}

View file

@ -137,13 +137,13 @@ fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
})
.with_children(|builder| {
builder.spawn((Text::new("Sidebar"),
TextStyle {
TextFont {
font: font.clone(),
..default()
},
));
builder.spawn((Text::new("A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely. A paragraph of text which ought to wrap nicely."),
TextStyle {
TextFont {
font: font.clone(),
font_size: 13.0,
..default()
@ -210,10 +210,7 @@ fn item_rect(builder: &mut ChildBuilder, color: Srgba) {
fn spawn_nested_text_bundle(builder: &mut ChildBuilder, font: Handle<Font>, text: &str) {
builder.spawn((
Text::new(text),
TextStyle {
font,
color: Color::BLACK,
..default()
},
TextFont { font, ..default() },
TextColor::BLACK,
));
}

View file

@ -15,7 +15,7 @@ fn main() {
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
let text_style = TextStyle::default();
let text_style = TextFont::default();
let image = asset_server.load("branding/icon.png");

View file

@ -81,14 +81,14 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Instructions
let text_style = TextStyle::default();
let text_font = TextFont::default();
commands
.spawn((
Text::new(
"Next Overflow Setting (O)\nNext Container Size (S)\nToggle Animation (space)\n\n",
),
text_style.clone(),
text_font.clone(),
Style {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
@ -99,7 +99,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
))
.with_child((
TextSpan::new(format!("{:?}", Overflow::clip())),
text_style.clone(),
text_font.clone(),
));
// Overflow Debug
@ -168,7 +168,7 @@ fn spawn_text(
spawn_container(parent, update_transform, |parent| {
parent.spawn((
Text::new("Bevy"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 100.0,
..default()

View file

@ -56,12 +56,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
parent.spawn((
Text::new("(0.0, 0.0)"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
}
@ -69,11 +69,11 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
/// This systems polls the relative cursor position and displays its value in a text component.
fn relative_cursor_position_system(
relative_cursor_position_query: Query<&RelativeCursorPosition>,
mut output_query: Query<(&mut Text, &mut TextStyle)>,
mut output_query: Query<(&mut Text, &mut TextColor)>,
) {
let relative_cursor_position = relative_cursor_position_query.single();
let (mut output, mut style) = output_query.single_mut();
let (mut output, mut text_color) = output_query.single_mut();
**output = if let Some(relative_cursor_position) = relative_cursor_position.normalized {
format!(
@ -84,7 +84,7 @@ fn relative_cursor_position_system(
"unknown".to_string()
};
style.color = if relative_cursor_position.mouse_over() {
text_color.0 = if relative_cursor_position.mouse_over() {
Color::srgb(0.1, 0.9, 0.1)
} else {
Color::srgb(0.9, 0.1, 0.1)

View file

@ -83,11 +83,11 @@ fn setup(
.with_children(|parent| {
parent.spawn((
Text::new("This is a cube"),
TextStyle {
TextFont {
font_size: 40.0,
color: Color::BLACK,
..default()
},
TextColor::BLACK,
));
});

View file

@ -56,7 +56,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// header
parent.spawn((
Text::new("Horizontally Scrolling list (Ctrl + Mousewheel)"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: FONT_SIZE,
..default()
@ -80,7 +80,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|parent| {
for i in 0..100 {
parent.spawn((Text(format!("Item {i}")),
TextStyle {
TextFont {
font: asset_server
.load("fonts/FiraSans-Bold.ttf"),
..default()
@ -138,7 +138,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Title
parent.spawn((
Text::new("Vertically Scrolling List"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: FONT_SIZE,
..default()
@ -178,7 +178,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
parent
.spawn((
Text(format!("Item {i}")),
TextStyle {
TextFont {
font: asset_server
.load("fonts/FiraSans-Bold.ttf"),
..default()
@ -213,7 +213,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Title
parent.spawn((
Text::new("Bidirectionally Scrolling List"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: FONT_SIZE,
..default()
@ -251,7 +251,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
parent
.spawn((
Text(format!("Item {}", (oi * 25) + i)),
TextStyle {
TextFont {
font: asset_server.load(
"fonts/FiraSans-Bold.ttf",
),
@ -288,7 +288,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Title
parent.spawn((
Text::new("Nested Scrolling Lists"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: FONT_SIZE,
..default()
@ -333,7 +333,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
parent
.spawn((
Text(format!("Item {}", (oi * 25) + i)),
TextStyle {
TextFont {
font: asset_server.load(
"fonts/FiraSans-Bold.ttf",
),

View file

@ -42,12 +42,14 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// ui camera
commands.spawn(Camera2d);
let text_style = TextStyle {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
};
let text_font = (
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
..Default::default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
);
commands
.spawn(NodeBundle {
@ -75,7 +77,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|parent| {
parent.spawn((
Text::new("Size Constraints Example"),
text_style.clone(),
text_font.clone(),
Style {
margin: UiRect::bottom(Val::Px(25.)),
..Default::default()
@ -103,7 +105,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
Constraint::Width,
Constraint::MaxWidth,
] {
spawn_button_row(parent, constraint, text_style.clone());
spawn_button_row(parent, constraint, text_font.clone());
}
});
});
@ -150,7 +152,11 @@ fn spawn_bar(parent: &mut ChildBuilder) {
});
}
fn spawn_button_row(parent: &mut ChildBuilder, constraint: Constraint, text_style: TextStyle) {
fn spawn_button_row(
parent: &mut ChildBuilder,
constraint: Constraint,
text_style: (TextFont, TextColor),
) {
let label = match constraint {
Constraint::FlexBasis => "flex_basis",
Constraint::Width => "size",
@ -229,7 +235,7 @@ fn spawn_button(
constraint: Constraint,
action: ButtonValue,
label: String,
text_style: TextStyle,
text_style: (TextFont, TextColor),
active: bool,
) {
parent
@ -271,14 +277,12 @@ fn spawn_button(
})
.with_child((
Text::new(label),
TextStyle {
color: if active {
ACTIVE_TEXT_COLOR
} else {
UNHOVERED_TEXT_COLOR
},
..text_style
},
text_style.0,
TextColor(if active {
ACTIVE_TEXT_COLOR
} else {
UNHOVERED_TEXT_COLOR
}),
TextLayout::new_with_justify(JustifyText::Center),
));
});
@ -290,7 +294,7 @@ fn update_buttons(
Changed<Interaction>,
>,
mut bar_query: Query<&mut Style, With<Bar>>,
mut text_query: Query<&mut TextStyle>,
mut text_query: Query<&mut TextColor>,
children_query: Query<&Children>,
mut button_activated_event: EventWriter<ButtonActivatedEvent>,
) {
@ -319,9 +323,9 @@ fn update_buttons(
for &child in children {
if let Ok(grand_children) = children_query.get(child) {
for &grandchild in grand_children {
if let Ok(mut style) = text_query.get_mut(grandchild) {
if style.color != ACTIVE_TEXT_COLOR {
style.color = HOVERED_TEXT_COLOR;
if let Ok(mut text_color) = text_query.get_mut(grandchild) {
if text_color.0 != ACTIVE_TEXT_COLOR {
text_color.0 = HOVERED_TEXT_COLOR;
}
}
}
@ -334,9 +338,9 @@ fn update_buttons(
for &child in children {
if let Ok(grand_children) = children_query.get(child) {
for &grandchild in grand_children {
if let Ok(mut style) = text_query.get_mut(grandchild) {
if style.color != ACTIVE_TEXT_COLOR {
style.color = UNHOVERED_TEXT_COLOR;
if let Ok(mut text_color) = text_query.get_mut(grandchild) {
if text_color.0 != ACTIVE_TEXT_COLOR {
text_color.0 = UNHOVERED_TEXT_COLOR;
}
}
}
@ -353,14 +357,14 @@ fn update_radio_buttons_colors(
button_query: Query<(Entity, &Constraint, &Interaction)>,
mut border_query: Query<&mut BorderColor>,
mut color_query: Query<&mut BackgroundColor>,
mut text_query: Query<&mut TextStyle>,
mut text_query: Query<&mut TextColor>,
children_query: Query<&Children>,
) {
for &ButtonActivatedEvent(button_id) in event_reader.read() {
let (_, target_constraint, _) = button_query.get(button_id).unwrap();
for (id, constraint, interaction) in button_query.iter() {
if target_constraint == constraint {
let (border_color, inner_color, text_color) = if id == button_id {
let (border_color, inner_color, label_color) = if id == button_id {
(ACTIVE_BORDER_COLOR, ACTIVE_INNER_COLOR, ACTIVE_TEXT_COLOR)
} else {
(
@ -378,8 +382,8 @@ fn update_radio_buttons_colors(
for &child in children_query.get(id).into_iter().flatten() {
color_query.get_mut(child).unwrap().0 = inner_color;
for &grandchild in children_query.get(child).into_iter().flatten() {
if let Ok(mut style) = text_query.get_mut(grandchild) {
style.color = text_color;
if let Ok(mut text_color) = text_query.get_mut(grandchild) {
text_color.0 = label_color;
}
}
}

View file

@ -32,7 +32,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
// Accepts a `String` or any type that converts into a `String`, such as `&str`
Text::new("hello\nbevy!"),
TextStyle {
TextFont {
// This font is loaded and will be used instead of the default font.
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 67.0,
@ -55,7 +55,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.spawn((
// Create a Text with multiple child spans.
Text::new("FPS: "),
TextStyle {
TextFont {
// This font is loaded and will be used instead of the default font.
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 42.0,
@ -65,20 +65,24 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_child((
TextSpan::default(),
if cfg!(feature = "default_font") {
TextStyle {
font_size: 33.0,
color: GOLD.into(),
// If no font is specified, the default font (a minimal subset of FiraMono) will be used.
..default()
}
(
TextFont {
font_size: 33.0,
// If no font is specified, the default font (a minimal subset of FiraMono) will be used.
..default()
},
TextColor(GOLD.into()),
)
} else {
// "default_font" feature is unavailable, load a font to use instead.
TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 33.0,
color: GOLD.into(),
..default()
}
(
// "default_font" feature is unavailable, load a font to use instead.
TextFont {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 33.0,
..Default::default()
},
TextColor(GOLD.into()),
)
},
FpsText,
));
@ -99,7 +103,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
#[cfg(not(feature = "default_font"))]
commands.spawn((
Text::new("Default font disabled"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
..default()
},
@ -112,12 +116,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
));
}
fn text_color_system(time: Res<Time>, mut query: Query<&mut TextStyle, With<ColorText>>) {
for mut style in &mut query {
fn text_color_system(time: Res<Time>, mut query: Query<&mut TextColor, With<ColorText>>) {
for mut text_color in &mut query {
let seconds = time.elapsed_seconds();
// Update the color of the ColorText span.
style.color = Color::srgb(
text_color.0 = Color::srgb(
ops::sin(1.25 * seconds) / 2.0 + 0.5,
ops::sin(0.75 * seconds) / 2.0 + 0.5,
ops::sin(0.50 * seconds) / 2.0 + 0.5,

View file

@ -60,7 +60,7 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
}).with_children(|builder| {
builder.spawn((
Text::new("This is\ntext with\nline breaks\nin the top left."),
TextStyle {
TextFont {
font: font.clone(),
font_size: 25.0,
..default()
@ -71,12 +71,12 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
Text::new(
"This text is right-justified. The `JustifyText` component controls the horizontal alignment of the lines of multi-line text relative to each other, and does not affect the text node's position in the UI layout.",
),
TextStyle {
TextFont {
font: font.clone(),
font_size: 25.0,
color: YELLOW.into(),
..default()
},
TextColor(YELLOW.into()),
TextLayout::new_with_justify(JustifyText::Right),
Style {
max_width: Val::Px(300.),
@ -87,7 +87,7 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
builder.spawn((
Text::new(
"This\ntext has\nline breaks and also a set width in the bottom left."),
TextStyle {
TextFont {
font: font.clone(),
font_size: 25.0,
..default()
@ -115,12 +115,12 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
builder.spawn((Text::new(
"This text is very long, has a limited width, is center-justified, is positioned in the top right and is also colored pink."),
TextStyle {
TextFont {
font: font.clone(),
font_size: 33.0,
color: Color::srgb(0.8, 0.2, 0.7),
..default()
},
TextColor(Color::srgb(0.8, 0.2, 0.7)),
TextLayout::new_with_justify(JustifyText::Center),
Style {
max_width: Val::Px(400.),
@ -130,12 +130,12 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
builder.spawn((Text::new(
"This text is left-justified and is vertically positioned to distribute the empty space equally above and below it."),
TextStyle {
TextFont {
font: font.clone(),
font_size: 29.0,
color: YELLOW.into(),
..default()
},
TextColor(YELLOW.into()),
TextLayout::new_with_justify(JustifyText::Left),
Style {
max_width: Val::Px(300.),
@ -146,13 +146,13 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
builder.spawn((
Text::new("This text is fully justified and is positioned in the same way."),
TextStyle {
TextFont {
font: font.clone(),
font_size: 29.0,
color: GREEN_YELLOW.into(),
..default()
},
TextLayout::new_with_justify(JustifyText::Justified),
TextColor(GREEN_YELLOW.into()),
Style {
max_width: Val::Px(300.),
..default()
@ -163,7 +163,7 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
builder.spawn((
Text::default(),
TextStyle {
TextFont {
font: font.clone(),
font_size: 21.0,
..default()
@ -174,7 +174,7 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|p| {
p.spawn((
TextSpan::new("\nThis text changes in the bottom right"),
TextStyle {
TextFont {
font: font.clone(),
font_size: 21.0,
..default()
@ -182,66 +182,66 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
));
p.spawn((
TextSpan::new(" this text has zero fontsize"),
TextStyle {
TextFont {
font: font.clone(),
font_size: 0.0,
color: BLUE.into(),
..default()
},
TextColor(BLUE.into()),
));
p.spawn((
TextSpan::new("\nThis text changes in the bottom right - "),
TextStyle {
TextFont {
font: font.clone(),
font_size: 21.0,
color: RED.into(),
..default()
},
TextColor(RED.into()),
));
p.spawn((
TextSpan::default(),
TextStyle {
TextFont {
font: font.clone(),
font_size: 21.0,
color: ORANGE_RED.into(),
..default()
}
},
TextColor(ORANGE_RED.into()),
));
p.spawn((
TextSpan::new(" fps, "),
TextStyle {
TextFont {
font: font.clone(),
font_size: 10.0,
color: YELLOW.into(),
..default()
},
TextColor(YELLOW.into()),
));
p.spawn((
TextSpan::default(),
TextStyle {
TextFont {
font: font.clone(),
font_size: 21.0,
color: LIME.into(),
..default()
}
},
TextColor(LIME.into()),
));
p.spawn((
TextSpan::new(" ms/frame"),
TextStyle {
TextFont {
font: font.clone(),
font_size: 42.0,
color: BLUE.into(),
..default()
},
TextColor(BLUE.into()),
));
p.spawn((
TextSpan::new(" this text has negative fontsize"),
TextStyle {
TextFont {
font: font.clone(),
font_size: -42.0,
color: BLUE.into(),
..default()
},
TextColor(BLUE.into()),
));
});
})

View file

@ -45,7 +45,7 @@ fn main() {
fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
let text_style = TextStyle {
let text_font = TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 12.0,
..default()
@ -121,7 +121,7 @@ fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
for (j, message) in messages.into_iter().enumerate() {
commands.entity(column_id).with_child((
Text(message.clone()),
text_style.clone(),
text_font.clone(),
TextLayout::new(JustifyText::Left, linebreak),
BackgroundColor(Color::srgb(0.8 - j as f32 * 0.2, 0., 0.)),
));

View file

@ -43,13 +43,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|parent| {
parent.spawn((
Text::new("Button 1"),
TextStyle {
TextFont {
font: font_handle.clone(),
font_size: 33.0,
// Alpha channel of the color controls transparency.
color: Color::srgba(1.0, 1.0, 1.0, 0.2),
..default()
},
// Alpha channel of the color controls transparency.
TextColor(Color::srgba(1.0, 1.0, 1.0, 0.2)),
));
});
@ -70,13 +70,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|parent| {
parent.spawn((
Text::new("Button 2"),
TextStyle {
TextFont {
font: font_handle.clone(),
font_size: 33.0,
// Alpha channel of the color controls transparency.
color: Color::srgba(1.0, 1.0, 1.0, 0.2),
..default()
},
// Alpha channel of the color controls transparency.
TextColor(Color::srgba(1.0, 1.0, 1.0, 0.2)),
));
});
});

View file

@ -75,7 +75,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// text
parent.spawn((
Text::new("Text Example"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 25.0,
..default()
@ -90,7 +90,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Debug overlay text
parent.spawn((
Text::new("Press Space to enable debug outlines."),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
..default()
},
@ -100,7 +100,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
#[cfg(not(feature = "bevy_dev_tools"))]
parent.spawn((
Text::new("Try enabling feature \"bevy_dev_tools\"."),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
..default()
},
@ -124,7 +124,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Title
parent.spawn((
Text::new("Scrolling list"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 21.,
..default()
@ -150,7 +150,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
parent
.spawn((
Text(format!("Item {i}")),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
..default()
},

View file

@ -23,9 +23,8 @@ fn main() {
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
let text_style = TextStyle {
let text_font = TextFont {
font_size: 13.,
color: Color::BLACK,
..default()
};
@ -56,7 +55,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
..default()
})
.with_children(|parent| {
parent.spawn((Text::new("Size!"), text_style));
parent.spawn((Text::new("Size!"), text_font, TextColor::BLACK));
});
parent.spawn(NodeBundle {
style: Style {

View file

@ -25,7 +25,7 @@ fn setup(
// Camera
commands.spawn(Camera2d);
let text_style = TextStyle::default();
let text_font = TextFont::default();
let texture_handle = asset_server.load("textures/rpg/chars/gabe/gabe-idle-run.png");
let texture_atlas = TextureAtlasLayout::from_grid(UVec2::splat(24), 7, 1, None, None);
@ -40,7 +40,7 @@ fn setup(
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
row_gap: Val::Px(text_style.font_size * 2.),
row_gap: Val::Px(text_font.font_size * 2.),
..default()
},
..default()
@ -61,15 +61,13 @@ fn setup(
Outline::new(Val::Px(8.0), Val::ZERO, CRIMSON.into()),
));
parent
.spawn((Text::new("press "), text_style.clone()))
.spawn((Text::new("press "), text_font.clone()))
.with_child((
TextSpan::new("space"),
TextStyle {
color: YELLOW.into(),
..text_style.clone()
},
TextColor(YELLOW.into()),
text_font.clone(),
))
.with_child((TextSpan::new(" to advance frames"), text_style));
.with_child((TextSpan::new(" to advance frames"), text_font));
});
}

View file

@ -104,12 +104,12 @@ fn setup(
.with_children(|parent| {
parent.spawn((
Text::new("Button"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
}

View file

@ -88,12 +88,12 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_children(|parent| {
parent.spawn((
Text::new("Button"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
));
});
}

View file

@ -29,7 +29,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
// Accepts a `String` or any type that converts into a `String`, such as `&str`
Text::new("Hit 'P' then scroll/click around!"),
TextStyle {
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 83.0, // Nice and big so you can see it!
..default()

View file

@ -200,27 +200,9 @@ pub(crate) mod test_setup {
))
.with_children(|p| {
p.spawn(TextSpan::new("Press space bar to cycle modes\n"));
p.spawn((
TextSpan::default(),
TextStyle {
color: LIME.into(),
..default()
},
));
p.spawn((
TextSpan::new("\nFrame: "),
TextStyle {
color: YELLOW.into(),
..default()
},
));
p.spawn((
TextSpan::new(""),
TextStyle {
color: YELLOW.into(),
..default()
},
));
p.spawn((TextSpan::default(), TextColor(LIME.into())));
p.spawn((TextSpan::new("\nFrame: "), TextColor(YELLOW.into())));
p.spawn((TextSpan::new(""), TextColor(YELLOW.into())));
});
}
}

View file

@ -53,7 +53,7 @@ fn setup(mut commands: Commands) {
.with_child((
CustomText,
Text::new("Example text"),
TextStyle {
TextFont {
font_size: 25.0,
..default()
},

View file

@ -45,7 +45,7 @@ fn setup_ui(mut commands: Commands) {
// Text where we display current resolution
.with_child((
Text::new("Resolution"),
TextStyle {
TextFont {
font_size: 42.0,
..default()
},