Skip to content

fix: default UiText textWrap to wrap instead of nowrap - #9712

Merged
pravusjif merged 4 commits into
devfrom
fix/uitext-default-textwrap-normal
Aug 18, 2026
Merged

fix: default UiText textWrap to wrap instead of nowrap#9712
pravusjif merged 4 commits into
devfrom
fix/uitext-default-textwrap-normal

Conversation

@pravusjif

@pravusjif pravusjif commented Aug 12, 2026

Copy link
Copy Markdown
Member

Fixes #9671

When a scene omitted 'textWrap', SetupLabel fell back to WhiteSpace.NoWrap, contradicting the proto spec where TW_WRAP is the zero-value default. The generated PBUiText.TextWrap accessor already returns TW_WRAP when the field is absent from the wire, so the HasTextWrap branch was redundant and its else clause actively overrode that default.

Reading TextWrap directly removes the redundant check and derives the default from the proto instead of hardcoding it a second time.

QA TEST STEPS

Use the build from this PR to enter the ZONE/SEPOLIA world sdk7testscenes.dcl.eth at position 80,-3 and confirm at the top-right UI panels for text wrap that you see:

  • top panel correctly wraps the text inside the panel
  • bottom panel doesn't wrap the text
Screenshot 2026-08-15 at 2 14 53 AM

When a scene omitted 'textWrap', SetupLabel fell back to WhiteSpace.NoWrap,
contradicting the proto spec where TW_WRAP is the zero-value default. The
generated PBUiText.TextWrap accessor already returns TW_WRAP when the field
is absent from the wire, so the HasTextWrap branch was redundant and its
else clause actively overrode that default.

Reading TextWrap directly removes the redundant check and derives the
default from the proto instead of hardcoding it a second time.

This restores the behaviour that predated #1387, which introduced the
whiteSpace block: before it, labels kept the WhiteSpace.Normal set by
SetElementDefaultStyle, which that method still applies at instantiation.

Only scenes using the raw SDK are affected -- react-ecs always sends the
field explicitly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@pravusjif pravusjif self-assigned this Aug 12, 2026
@pravusjif pravusjif moved this to In Progress in Creators Tools Aug 12, 2026
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

🚦 CI Status

Build

New build in progress, come back later!

Lint

Warnings not reduced: 13116 => 13116 — remove at least 1 warning to merge.

Warnings/errors in files changed by this PR (15)
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:265  CSharpWarnings::CS8601  Possible null reference assignment
Assets/DCL/SDKComponents/SceneUI/Tests/UITextInstantiationSystemShould.cs:20  CSharpWarnings::CS8618  Non-nullable field 'poolsRegistry' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/SDKComponents/SceneUI/Tests/UITextInstantiationSystemShould.cs:22  CSharpWarnings::CS8618  Non-nullable field 'uiTransformComponent' is uninitialized. Consider adding the 'required' modifier or declaring the field as nullable.
Assets/DCL/SDKComponents/SceneUI/Tests/UITextInstantiationSystemShould.cs:31  CSharpWarnings::CS8625  Cannot convert null literal to non-nullable reference type
Assets/DCL/SDKComponents/SceneUI/Tests/UITextInstantiationSystemShould.cs:39  InconsistentNaming  Name 'InstantiateUIText' does not match rule 'members_should_be_pascal_case'. Suggested name is 'InstantiateUiText'.
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:359  InconsistentNaming  Name 'ReleaseUIDropdownComponent' does not match rule 'members_should_be_pascal_case'. Suggested name is 'ReleaseUiDropdownComponent'.
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:342  InconsistentNaming  Name 'ReleaseUIElement' does not match rule 'members_should_be_pascal_case'. Suggested name is 'ReleaseUiElement'.
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:353  InconsistentNaming  Name 'ReleaseUIInputComponent' does not match rule 'members_should_be_pascal_case'. Suggested name is 'ReleaseUiInputComponent'.
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:345  InconsistentNaming  Name 'ReleaseUITransformComponent' does not match rule 'members_should_be_pascal_case'. Suggested name is 'ReleaseUiTransformComponent'.
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:292  InconsistentNaming  Name 'SetupUIDropdownComponent' does not match rule 'members_should_be_pascal_case'. Suggested name is 'SetupUiDropdownComponent'.
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:268  InconsistentNaming  Name 'SetupUIInputComponent' does not match rule 'members_should_be_pascal_case'. Suggested name is 'SetupUiInputComponent'.
Assets/DCL/SDKComponents/SceneUI/Tests/UITextInstantiationSystemShould.cs:18  InconsistentNaming  Name 'UITextInstantiationSystemShould' does not match rule 'members_should_be_pascal_case'. Suggested name is 'UiTextInstantiationSystemShould'.
Assets/DCL/SDKComponents/SceneUI/Tests/UITextInstantiationSystemShould.cs:92  InconsistentNaming  Name 'UpdateUIText' does not match rule 'members_should_be_pascal_case'. Suggested name is 'UpdateUiText'.
Assets/DCL/SDKComponents/SceneUI/Utils/UiElementUtils.cs:21  InconsistentNaming  Name 'scrollViewMouseWheelScrollSize' does not match rule 'static_readonly_should_be_capital_snake_case'. Suggested name is 'SCROLL_VIEW_MOUSE_WHEEL_SCROLL_SIZE'.
Assets/DCL/SDKComponents/SceneUI/Tests/UITextInstantiationSystemShould.cs:12  RedundantUsingDirective  Using directive is not required by the code and can be safely removed

Tests

All Unity tests passed ✅

TESTS SUITE Result Passed Failed Skipped
EditMode ✅ Passed 25187 0 13
PlayMode ✅ Passed 238 0 37

@pravusjif
pravusjif marked this pull request as ready for review August 15, 2026 00:16
@pravusjif
pravusjif requested review from a team as code owners August 15, 2026 00:16
@github-actions
github-actions Bot requested a review from anicalbano August 15, 2026 00:16
@decentraland-bot
decentraland-bot self-requested a review August 15, 2026 00:16
@pravusjif pravusjif moved this from In Progress to QA in Creators Tools Aug 15, 2026

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — PR #9712

STEP 2 — Root-cause check: PASS

The PR fixes the actual root cause. The old code:

if (model.HasTextWrap)
    labelToSetup.style.whiteSpace = model.TextWrap == TextWrap.TwWrap ? WhiteSpace.Normal : WhiteSpace.NoWrap;
else
    labelToSetup.style.whiteSpace = WhiteSpace.NoWrap;

The else branch hardcoded WhiteSpace.NoWrap as the fallback when TextWrap was absent from the wire. However, the proto spec defines TW_WRAP (value 0) as the zero-value default — confirmed in the generated UiText.gen.cs:

private readonly static TextWrap TextWrapDefaultValue = TextWrap.TwWrap;

public TextWrap TextWrap {
    get { if ((_hasBits0 & 8) != 0) { return textWrap_; } else { return TextWrapDefaultValue; } }
}

The accessor already returns TwWrap when the field is absent, so reading model.TextWrap directly is correct — the redundant HasTextWrap check is removed and the proto-defined default flows through naturally.

STEP 3 — Design & integration: PASS

No new units, types, or long-lived state introduced. The fix is correctly located in SetupLabel (UiElementUtils.cs), which is the utility method responsible for mapping proto model fields to UI Toolkit style properties. No lifecycle, subscription, or ownership changes.

I also checked TMPProSdkExtensions.cs — it uses HasTextWrapping on a different component (PBTextShape, a boolean field for the legacy TextShape component), not the TextWrap enum on PBUiText. No similar bug pattern there.

STEP 4 — Member audit: PASS

No new public members added or changed.

STEP 5 — Line-level review: No issues found

Code change: Clean one-liner that correctly simplifies the conditional by trusting the proto-generated default. Both the absent case (TwWrapWhiteSpace.Normal) and the explicit TwNoWrap case (WhiteSpace.NoWrap) are handled correctly.

Tests: Two well-structured tests following AAA pattern with NUnit:

  • WrapTextWhenTextWrapIsAbsent — verifies the default (absent field → WhiteSpace.Normal) ✅
  • NotWrapTextWhenTextWrapIsExplicitlyNoWrap — verifies explicit TwNoWrapWhiteSpace.NoWrap

Both tests cover the behavioral boundary that the bug affected. No resource leaks, no allocation issues, no security concerns.

STEP 6 — Complexity: SIMPLE

Two files, ~36 lines of meaningful changes (mostly test additions), straightforward bug fix in a utility method.

STEP 7 — QA: YES

Changes runtime UI text rendering behavior — scenes that previously had unwrapped text by default will now correctly wrap.

STEP 8 — Non-blocking warnings: None

Security review: No security issues found

Pure UI rendering logic change with no user input handling, authentication, secrets, or network changes.


REVIEW_RESULT: PASS ✅
COMPLEXITY: SIMPLE
COMPLEXITY_REASON: Bug fix in a static utility method (UiElementUtils.SetupLabel) with corresponding unit tests — no ECS system, async, or architectural changes.
QA_REQUIRED: YES


Reviewed by Jarvis 🤖 · Requested by decentraland-bot via GitHub

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Auto-approved based on Jarvis review — simple fix/chore with no blocking issues. QA approval is still required.

@decentraland-bot

This comment has been minimized.

@DafGreco DafGreco left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✔️ PR reviewed and approved by QA on both platforms following instructions playing both happy and un-happy path

Regressions for this ticket had been performed in order to verify that the normal flow is working as expected:

  • [✔️ ] Backpack and wearables in world
  • [✔️ ] Emotes in world and in backpack
  • [ ✔️] Teleport with map/coordinates/Jump In
  • [✔️ ] Chat and multiplayer
  • [ ✔️] Profile card

Evidence:

Image

@pravusjif pravusjif added the no-warning-ratchet Prevent CI linting checks label Aug 17, 2026
@decentraland-bot

Copy link
Copy Markdown
Contributor

PR #9712, run #32129566024

Builds: Windows change, Windows baseline, macOS change, macOS baseline

How to read this table
  • Each build is measured 3 times. The values are the median, and (min–max) is the lowest and highest of those runs — a wide range means the metric is noisy and small differences are not trustworthy.
  • Δ is Change minus Baseline (a negative Δ means Change is faster).
  • 🟢 faster / 🔴 slower — a real difference: larger than both 3% and the run-to-run range.
  • ⚪ within noise — the difference is smaller than how much the build varies between its own runs, so it cannot be told apart from random variation. Treat it as no change.
  • Exceptions per run — the average number of exceptions in a run's log; more than the baseline is flagged 🔴 even when frame times look fine. The Exception breakdown under each table groups them by the explorer's report category and exception type (as totals across the runs).
  • A run that logged unusually many exceptions (at least 10 and 5× the median of its build's runs — e.g. a service was down during it) is excluded from all numbers and called out under the table.

Intel Core i5

Metric Baseline Change Δ Result
Samples 2701 (×3) 2325 (×3)
CPU average 33.2 ms (33.2–34.6) 38.5 ms (35.4–40.2) 5.4 ms 🔴 16% slower
CPU 1% worst 34.3 ms (33.5–184.5) 327.6 ms (257.4–399.1) 293.4 ms 🔴 856% slower
CPU 0.1% worst 41.4 ms (33.7–332.0) 396.8 ms (364.6–410.0) 355.4 ms 🔴 858% slower
GPU average 9.3 ms (9.2–9.4) 9.5 ms (9.4–9.7) 0.2 ms ⚪ within noise
GPU 1% worst 20.7 ms (19.8–26.9) 35.2 ms (33.9–43.6) 14.5 ms 🔴 70% slower
GPU 0.1% worst 36.3 ms (31.6–37.7) 47.8 ms (42.7–52.9) 11.5 ms 🔴 32% slower
Exceptions per run 66 66 0 ⚪ none new
Exception breakdown
Exception Baseline (3 runs) Change (3 runs)
[UI] DllNotFoundException 192 192
[ENGINE] NullReferenceException 3 3
[ENGINE] ObjectDisposedException 3 3

Apple M1

Metric Baseline Change Δ Result
Samples 4368 (×3) 4023 (×3)
CPU average 20.5 ms (20.3–21.6) 22.3 ms (21.8–22.4) 1.8 ms 🔴 9% slower
CPU 1% worst 34.7 ms (33.9–34.7) 232.0 ms (170.3–233.5) 197.4 ms 🔴 569% slower
CPU 0.1% worst 34.9 ms (34.9–35.3) 238.7 ms (235.2–239.7) 203.7 ms 🔴 583% slower
GPU average 1.0 ms (0.1–1.6) 8.6 ms (1.6–9.0) 7.7 ms 🔴 792% slower
GPU 1% worst 34.2 ms (7.7–34.8) 35.1 ms (35.0–35.3) 0.9 ms ⚪ within noise
GPU 0.1% worst 35.9 ms (35.1–37.2) 36.9 ms (36.1–38.2) 1.0 ms ⚪ within noise
Exceptions per run 0 0 0 ⚪ none new

@pravusjif
pravusjif requested review from dalkia and davidejensen and removed request for lorenzo-ranciaffi August 18, 2026 20:06
@pravusjif pravusjif changed the title fix: default UiText textWrap to wrap instead of nowrap (#9671) fix: default UiText textWrap to wrap instead of nowrap Aug 18, 2026
@pravusjif
pravusjif enabled auto-merge (squash) August 18, 2026 20:27
@pravusjif
pravusjif merged commit a1012b4 into dev Aug 18, 2026
25 of 26 checks passed
@pravusjif
pravusjif deleted the fix/uitext-default-textwrap-normal branch August 18, 2026 21:20
@github-project-automation github-project-automation Bot moved this from QA to To Release in Creators Tools Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-approved no-warning-ratchet Prevent CI linting checks

Projects

Status: To Release

Development

Successfully merging this pull request may close these issues.

UiText: default textWrap fallback should be Normal (wrap), not NoWrap

3 participants