Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make each TextSection its own entity. #14572

Closed
wants to merge 24 commits into from

Conversation

Olle-Lukowski
Copy link
Contributor

@Olle-Lukowski Olle-Lukowski commented Aug 1, 2024

Objective

This change makes each TextSection its own entity, which was discussed to be the next step for bevy_text in Discord.

Fixes #7714

Solution

  • Changed the Text component to hold no sections instead of a Vec of TextSection.
  • Made TextSection a component that should be added to children of a Text.
  • Adjustments to make the code compile again.

Testing

Tested out some of the examples that I ported to the new API, they all look fine.


Migration Guide

Any usage of TextBundle::from_section or Text::from_section should be replaced with something like this:

//old
commands.spawn(TextBundle::from_section("My Text", TextSection::default()));

// new
commands.spawn(TextBundle::default()).with_child(TextSection::from("My Text"));

Same goes for the from_sections methods.

@alice-i-cecile alice-i-cecile added this to the 0.15 milestone Aug 1, 2024
@alice-i-cecile alice-i-cecile added C-Code-Quality A section of code that is hard to understand or change C-Usability A targeted quality-of-life change that makes Bevy easier to use A-Text Rendering and layout for characters labels Aug 1, 2024
@alice-i-cecile
Copy link
Member

Alice 🌹: So @Preon, currently, text is handled via the Text component, which is composed of multiple TextSection parts in a vec
[10:07 AM] 
OP
 Alice 🌹: Both @DreamerTalin and @cart have independently found that this is very frustrating to work with in more complex workflows: you end up with two separate hierarchies that need to be managed
[10:07 AM]Preon: yeah that makes sense
[10:08 AM] 
OP
 Alice 🌹: Instead, each TextSection should be its own entity, with a consistent TextStyle
[10:09 AM] 
OP
 Alice 🌹: In 90% of cases, a chunk of text will have a single text style: we should optimize for making that case easy and ergonomic 

Context for why this should be done, from Discord

@alice-i-cecile alice-i-cecile added D-Complex Quite challenging from either a design or technical perspective. Ask for help! X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers labels Aug 1, 2024
@alice-i-cecile
Copy link
Member

This work was previously mentioned in #14437. As discussed above, bringing text sections into the entity hierarchy is required to make them play nicely with scenes, and thus the BSN entity-spawning tools.

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged M-Needs-Release-Note Work that should be called out in the blog due to impact labels Aug 1, 2024
@Olle-Lukowski
Copy link
Contributor Author

Olle-Lukowski commented Aug 1, 2024

I think I will wait with porting over all examples for two reasons:

  • The API will probably change based on suggestions.
  • It is quite some work, that I don't want to re-do.

I did port some of the examples to show the new API, and those will be kept up to date for any changes that happen.

Comment on lines 179 to 182
let sections = if let Some(children) = children {
children_changed = children.is_changed();

text2d_section_query.iter_many(&children).collect()
Copy link
Contributor

Choose a reason for hiding this comment

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

This vec can be cached in a Local. Then use .clear() and .extend().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah right, will do that!

Comment on lines 323 to 328
let sections = if let Some(children) = children {
children_changed = children.is_changed();
text_section_query.iter_many(&children).collect()
} else {
vec![]
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, cache the vec.

Comment on lines +117 to +119
let section = text2d_section_query
.get(children.unwrap()[*section_index])
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

It is not valid to access via [] here, because non-TextSection children may be interleaved. You need to find the position in the children that filters for entities with TextSection.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I guess that's true. What would be the best approach for that?

Comment on lines 186 to 191
let sections = if let Some(children) = children {
children_changed = children.is_changed();
section_query.iter_many(children.iter()).collect()
} else {
vec![]
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Here as well, cache the vec.

Comment on lines +864 to +866
let section = text2d_section_query
.get(children.unwrap()[*section_index])
.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Invalid children access, need to filter for entities with TextSection.

let values = text
.sections
if let Ok(maybe_children) = texts.get(*child) {
let mut sections = Vec::new();
Copy link
Contributor

Choose a reason for hiding this comment

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

Cache this vec if possible. Or convert to a chained iterator for the values collection.

Comment on lines 72 to 84
.spawn(TextBundle {
style: Style {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
}),
);
},
..default()
})
.with_child(TextSection::new(
"Press space to toggle wireframes",
TextStyle::default(),
));
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be nice if you could optionally put the first text section on the parent entity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We initially had this supported, but in discord we decided to not do it.

@Olle-Lukowski
Copy link
Contributor Author

Marking this as ready for review, the main thing that we still need to do is find a way to prevent repeated allocations in the systems where the text is enqueued.

@Olle-Lukowski Olle-Lukowski marked this pull request as ready for review August 21, 2024 16:20
@Olle-Lukowski Olle-Lukowski changed the title WIP PR for making each TextSection its own entity. Make each TextSection its own entity. Aug 21, 2024
@Olle-Lukowski Olle-Lukowski added S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Aug 21, 2024
@ickshonpe ickshonpe self-requested a review August 21, 2024 16:40
@@ -127,7 +52,7 @@ impl Text {
}

/// Contains the value of the text in a section and how it should be styled.
#[derive(Debug, Default, Clone, Reflect)]
#[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Default)]
pub struct TextSection {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be split up I think, TextStyle needs to be a separate elidable component imo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure how I feel about that, it would require a LOT more changes, that I don't want to update 100 examples for again.

Copy link
Contributor

@ickshonpe ickshonpe Aug 21, 2024

Choose a reason for hiding this comment

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

I know but I think it's really necessary.

Maybe if TextSection is made into a bundle with Text (and rename the existing component, I agree with TotalKrill that it's potentially confusing) and TextStyle components it would just work without any changes to most of the examples?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

honestly great idea!

Copy link
Contributor

Choose a reason for hiding this comment

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

I know but I think it's really necessary.

Could you elaborate on this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I know but I think it's really necessary.

Could you elaborate on this?

Different styling options, with Text2d style propagation and responsive sizes might not make sense or need different solutions. More granular change detection.

Copy link
Contributor

@rparrett rparrett Aug 22, 2024

Choose a reason for hiding this comment

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

@ickshonpe
That sounds good to me, but it also sounds like something that should be discussed/designed. Would be it acceptable to catalog this in an issue and implement it in a followup PR?

Copy link
Member

Choose a reason for hiding this comment

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

Totally agree on splitting this, strongly feel this should be moved to followup.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep I've no problem with moving this to a followup, it would be good if we can avoid multiple rewrites of the api and examples though.

Copy link
Contributor

Choose a reason for hiding this comment

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

It probably should be split up more even, previously it's really bugged me how changing the colour of a single section causes a relayout of the whole text node. Separate TextColor component disappears that problem.

Copy link
Contributor

@TotalKrill TotalKrill left a comment

Choose a reason for hiding this comment

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

The docs should really indicate the new relationship between the components. And the component should be renamed since it has completely changed its meaning with this change.

And the newly created issue for this should indicate what was lost and point to this PR so it can be tracked

@@ -127,7 +52,7 @@ impl Text {
}

/// Contains the value of the text in a section and how it should be styled.
Copy link
Contributor

@TotalKrill TotalKrill Aug 21, 2024

Choose a reason for hiding this comment

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

This is the first time we are adding a parent/child hierarchy requirement as far as I know, the docs should indicate this relationship on the related components.

Also with this change, the name "Text" is actually more confusing than helping, since it only contains the information for how text will be justified, and contains no text whatsoever

@@ -127,7 +52,7 @@ impl Text {
}

/// Contains the value of the text in a section and how it should be styled.
#[derive(Debug, Default, Clone, Reflect)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Same on this component, should probably indicate that this component is required to be a child of the other Component.

@@ -207,26 +205,6 @@ pub struct TextBundle {

#[cfg(feature = "bevy_text")]
impl TextBundle {
/// Create a [`TextBundle`] from a single section.
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed in discord.

The removal of these ergonomics is a huge loss. both on TextBundle and on Text and should be addressed before 0.15 if this PR is to land

@@ -29,8 +29,6 @@ impl Default for CosmicBuffer {
#[derive(Component, Debug, Clone, Default, Reflect)]
#[reflect(Component, Default)]
pub struct Text {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub struct Text {
pub struct TextNode {

Copy link
Contributor

Choose a reason for hiding this comment

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

This suggestion seems confusing because the component is also used in Text2dBundle, and we don't use this naming convention in any other UI bundles.

Copy link
Contributor

Choose a reason for hiding this comment

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

What do we think of TextLayout?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll go with TextLayout, makes the most sense to me.

@alice-i-cecile alice-i-cecile self-requested a review August 22, 2024 01:25
@Olle-Lukowski
Copy link
Contributor Author

There are currently warnings about the children of a UI node not having UI components themselves, how do I fix these?

2024-08-22T08:10:22.518891Z  WARN bevy_ui::layout::ui_surface: Unstyled child `10v1` in a UI entity hierarchy. You are using an entity without UI components as a child of an entity with UI components, results may be unexpected.

@ickshonpe
Copy link
Contributor

ickshonpe commented Aug 22, 2024

There are currently warnings about the children of a UI node not having UI components themselves, how do I fix these?

2024-08-22T08:10:22.518891Z  WARN bevy_ui::layout::ui_surface: Unstyled child `10v1` in a UI entity hierarchy. You are using an entity without UI components as a child of an entity with UI components, results may be unexpected.

Not tested it but changing these queries in ui_layout_system:

children_query: Query<(Entity, Ref<Children>), With<Node>>,
just_children_query: Query<&Children>,

to filter for the text bundle's marker component with:

children_query: Query<(Entity, Ref<Children>), (With<Node>, Without<TextLayout>)>,
just_children_query: Query<&Children, Without<TextLayout>>,

should be sufficient.

@alice-i-cecile
Copy link
Member

Can you do a pass on the comments and resolve those that are addressed to make this easier to review? :)

@tigregalis
Copy link
Contributor

Is this the right time to rename sections to spans?

@alice-i-cecile
Copy link
Member

alice-i-cecile commented Aug 22, 2024

Is this the right time to rename sections to spans?

IMO yes. Edit: I don't feel strongly, and getting this PR as small as possible would be nice.

@UkoeHB
Copy link
Contributor

UkoeHB commented Sep 2, 2024

See this discussion for a required-components-based redesign.

@alice-i-cecile
Copy link
Member

Merging #15591, which continued this work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Text Rendering and layout for characters C-Code-Quality A section of code that is hard to understand or change C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Complex Quite challenging from either a design or technical perspective. Ask for help! M-Needs-Release-Note Work that should be called out in the blog due to impact S-Needs-Review Needs reviewer attention (from anyone!) to move forward X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Text should not store a flat list of sections
7 participants