Skip to content

fix: Fade TextComponent shadows and fill together under OpacityEffect - #4042

Closed
Yasser-Ameur wants to merge 3 commits into
flame-engine:mainfrom
Yasser-Ameur:fix/text-component-shadow-opacity
Closed

fix: Fade TextComponent shadows and fill together under OpacityEffect#4042
Yasser-Ameur wants to merge 3 commits into
flame-engine:mainfrom
Yasser-Ameur:fix/text-component-shadow-opacity

Conversation

@Yasser-Ameur

Copy link
Copy Markdown
Contributor

Description

When an OpacityEffect runs on a TextComponent whose TextPaint style defines shadows, the glyphs fade but the shadows keep their original alpha, and the first paint change replaces the text colour with the component's default white paint, so coloured text snaps to white as the fade begins (#4013).

TextPaint.copyWithPaint only swapped the style's foreground for the component's paint, so style.shadows never followed the paint's alpha. The component also derived each new renderer from the previous derived one, so any per-update scaling would have compounded.

This PR:

  • scales every shadow's alpha by the paint's alpha in TextPaint.copyWithPaint, keeping the paint itself as the foreground so colour filters and other paint properties still apply;
  • derives every paint change from the renderer the user gave, so the alpha is always relative to the original style and never compounds. textRenderer now returns that renderer rather than the last derived copy, which also makes textRenderer = textRenderer.copyWith(...) safe after an effect has run; the copy with the paint applied is the new @internal paintedTextRenderer, used for drawing by TextComponent and TextBoxComponent;
  • seeds the component's paint colour from the style's colour at construction and when textRenderer is set, so the fade starts from the text's own colour instead of white. Setting paint.color afterwards still overrides it; note that assigning a new textRenderer reseeds it.

The new tests run OpacityEffect.fadeOut for half its duration and check that the fill keeps its RGB at half alpha and the shadow keeps its RGB at half its own alpha, then that both reach zero; and that a textRenderer round-trip between two opacity changes keeps the original colour and shadow and scales only once. Both fail on main and pass here.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • [-] I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

Closes #4013

TextPaint.copyWithPaint only swapped the style's foreground for the
component's paint, so shadows kept their alpha while the glyphs faded,
and the first paint change replaced the text color with the default
white paint. The shadows now scale with the paint's alpha, the paint
starts from the style's color, and every paint change is derived from
the renderer the user gave, which textRenderer now returns; the copy
with the paint applied is paintedTextRenderer, used for drawing by
TextComponent and TextBoxComponent.

Closes flame-engine#4013
@spydon

spydon commented Sep 8, 2026

Copy link
Copy Markdown
Member

This solution doesn't work for the following cases:

  • Outlined or gradient text. A style that uses foreground instead of color gets that paint replaced by the component's plain white fill the moment the fade begins. The outline or shader disappears and the text snaps to white. Seeding does nothing here because style.color is null.
  • Sprite fonts. SpriteFontRenderer.copyWithPaint still drops scale, letter spacing and the tint, so scaled or colored sprite font text shrinks and loses its color on the first paint change.
  • Reassigning the renderer mid-fade shows one frame at full opacity and silently overwrites any paint color the user had set.

And since we already had an agent running for this issue, this PR has been superseded by #4043 which takes these things into consideration.
Thanks for your contribution though.

@spydon spydon closed this Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TextComponent shadows are not affected by OpacityEffect

2 participants