@@ -68,7 +68,7 @@ impl ComputedTextBlock {
68
68
/// Can be used to look up [`TextStyle`] components for glyphs in [`TextLayoutInfo`] using the `span_index`
69
69
/// stored there.
70
70
pub fn entities ( & self ) -> & [ TextEntity ] {
71
- self . entities . iter ( )
71
+ & self . entities
72
72
}
73
73
74
74
/// Indicates if the text needs to be refreshed in [`TextLayoutInfo`].
@@ -112,8 +112,16 @@ pub struct TextBlock {
112
112
113
113
impl TextBlock {
114
114
/// Makes a new [`TextBlock`].
115
- pub const fn new ( justify : JustifyText , linebreak : LineBreak , font_smoothing : FontSmoothing ) -> Self {
116
- Self { justify, linebreak, font_smoothing }
115
+ pub const fn new (
116
+ justify : JustifyText ,
117
+ linebreak : LineBreak ,
118
+ font_smoothing : FontSmoothing ,
119
+ ) -> Self {
120
+ Self {
121
+ justify,
122
+ linebreak,
123
+ font_smoothing,
124
+ }
117
125
}
118
126
119
127
/// Makes a new [`TextBlock`] with the specified [`JustifyText`].
@@ -277,3 +285,71 @@ pub enum FontSmoothing {
277
285
// TODO: Add subpixel antialias support
278
286
// SubpixelAntiAliased,
279
287
}
288
+
289
+ /// System that detects changes to text blocks and sets `ComputedTextBlock::should_rerender`.
290
+ ///
291
+ /// Generic over the root text component and text span component. For example, [`Text2d`]/[`TextSpan2d`] for 2d or
292
+ /// `Text`/`TextSpan` for UI.
293
+ pub fn detect_text_needs_rerender < Root : Component , Span : Component > (
294
+ changed_roots : Query <
295
+ Entity ,
296
+ (
297
+ Or < (
298
+ Changed < Root > ,
299
+ Changed < TextStyle > ,
300
+ Changed < TextBlock > ,
301
+ Changed < Children > ,
302
+ ) > ,
303
+ With < Root > ,
304
+ With < TextStyle > ,
305
+ With < TextBlock > ,
306
+ ) ,
307
+ > ,
308
+ changed_spans : Query <
309
+ & Parent ,
310
+ Or < ( Changed < Span > , Changed < TextStyle > , Changed < Children > ) > ,
311
+ With < Span > ,
312
+ With < TextStyle > ,
313
+ > ,
314
+ mut computed : Query < ( Option < & Parent > , Option < & mut ComputedTextBlock > ) > ,
315
+ ) {
316
+ // Root entity:
317
+ // - Root component changed.
318
+ // - TextStyle on root changed.
319
+ // - TextBlock changed.
320
+ // - Root children changed (can include additions and removals).
321
+ for root in changed_roots. iter ( ) {
322
+ // TODO: ComputedTextBlock *should* be here. Log a warning?
323
+ let Ok ( ( _, Some ( mut computed) ) ) = computed. get_mut ( root) else {
324
+ continue ;
325
+ } ;
326
+ computed. needs_rerender = true ;
327
+ }
328
+
329
+ // Span entity:
330
+ // - Span component changed.
331
+ // - Span TextStyle changed.
332
+ // - Span children changed (can include additions and removals).
333
+ for span_parent in changed_spans. iter ( ) {
334
+ let mut parent: Entity = * * span_parent;
335
+
336
+ // Search for the nearest ancestor with ComputedTextBlock.
337
+ // Note: We assume the perf cost from duplicate visits in the case that multiple spans in a block are visited
338
+ // is outweighed by the expense of tracking visited spans.
339
+ loop {
340
+ // TODO: If this lookup fails then there is a hierarchy error. Log a warning?
341
+ let Ok ( ( maybe_parent, maybe_computed) ) = computed. get_mut ( parent) else {
342
+ break ;
343
+ } ;
344
+ if let Some ( computed) = maybe_computed {
345
+ computed. needs_rerender = true ;
346
+ break ;
347
+ }
348
+ // TODO: If there is no parent then a span is floating without an owning TextBlock. Log a warning?
349
+ let Some ( next_parent) = maybe_parent else {
350
+ break ;
351
+ } ;
352
+ parent = * * next_parent;
353
+ }
354
+ }
355
+ }
0 commit comments