Skip to content

feat(text): Shape complex single line UI text - #3231

Open
OmarAglan wants to merge 2 commits into
TheSuperHackers:mainfrom
OmarAglan:feature/arabic-ui-text-shaping
Open

feat(text): Shape complex single line UI text#3231
OmarAglan wants to merge 2 commits into
TheSuperHackers:mainfrom
OmarAglan:feature/arabic-ui-text-shaping

Conversation

@OmarAglan

@OmarAglan OmarAglan commented Aug 28, 2026

Copy link
Copy Markdown

Adds contextual shaping and bidirectional ordering for complex single-line UI text in Render2DSentenceClass.

The existing sentence renderer processes text one WCHAR at a time, which prevents Arabic letters from using their contextual forms and breaks the visual order of mixed Arabic and Latin runs. Eligible strings are now measured and rasterized as one Uniscribe run before being copied into the existing A4R4G4B4 sentence textures.

Plain Latin strings continue to use the existing per-character renderer. Multiline text, text requiring wrapping, hot-key parsed text, monospaced text, and editable text entries remain on the legacy path.

The required Uniscribe functions are loaded at runtime through #3241. If Uniscribe is unavailable, rendering falls back to the legacy path.

Before

sshot001

After

sshot_20260828_215246_250

The change was validated with:

  • Generals Release build
  • Zero Hour Release build
  • git diff --check
  • Runtime testing in Zero Hour

The implementation was developed with AI assistance, then manually reviewed and simplified against the nearby renderer and runtime-loader code.

@OmarAglan
OmarAglan marked this pull request as ready for review August 28, 2026 10:21
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 28, 2026

Copy link
Copy Markdown

PR Summary by Qodo

Shape complex single-line UI text with Uniscribe

✨ Enhancement 🐞 Bug fix 🕐 40+ Minutes

Grey Divider

AI Description

• Shape complex single-line UI text with contextual glyphs and bidirectional ordering.
• Measure and rasterize eligible runs through dynamically loaded Windows Uniscribe.
• Preserve legacy rendering for Latin, multiline, hotkey, monospaced, and editable text.
Diagram

graph TD
  A["Display String"] --> B["Sentence Renderer"] --> C{"Complex eligible?"}
  C -- Yes --> D["Windows Uniscribe"] --> E["GDI Raster"] --> F["Sentence Textures"]
  C -- No --> G["Legacy Glyph Path"] --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Adopt DirectWrite rendering
  • ➕ Provides a modern shaping and text-layout stack
  • ➕ Could support multiline layout and caret metrics in one backend
  • ➖ Requires a broader renderer redesign beyond existing GDI HFONT integration
  • ➖ Raises migration, compatibility, and review risk substantially
2. Link usp10 statically
  • ➕ Removes function-pointer wrappers and lazy-load branching
  • ➕ Makes missing APIs fail at build or process load time
  • ➖ Introduces a hard Windows import dependency
  • ➖ Loses graceful fallback to legacy rendering when Uniscribe is unavailable

Recommendation: Keep the runtime-loaded Uniscribe approach for this scoped change. It reuses the existing GDI font pipeline, preserves legacy behavior when shaping is unavailable or unsupported, and avoids a disproportionate DirectWrite migration; editable and multiline shaping can be added once caret and layout metrics are designed.

Files changed (12) +600 / -5

Enhancement (7) +406 / -5
DisplayString.hExpose per-string complex text control +1/-0

Expose per-string complex text control

• Adds an abstract switch allowing display-string implementations and UI controls to enable or disable complex shaping.

Core/GameEngine/Include/GameClient/DisplayString.h

render2dsentence.cppShape and rasterize eligible complex text runs +355/-3

Shape and rasterize eligible complex text runs

• Detects complex single-line text, measures and renders it as one Uniscribe run, and copies bounded raster chunks into existing sentence textures. It preserves the legacy path for excluded modes, unsupported dimensions, non-Windows builds, or shaping failures.

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp

render2dsentence.hDeclare complex shaping renderer APIs +18/-2

Declare complex shaping renderer APIs

• Adds font-level complexity, measurement, and rasterization methods plus renderer eligibility, sizing, texture-building, and enablement state.

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h

W3DDisplayString.hExpose complex shaping in Generals display strings +1/-0

Expose complex shaping in Generals display strings

• Adds the W3D display-string override for controlling complex text shaping.

Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplayString.h

W3DDisplayString.cppUse shaped widths and propagate shaping state +15/-0

Use shaped widths and propagate shaping state

• Returns whole-run Uniscribe width when applicable and applies shaping enablement to normal and hotkey renderers. State changes invalidate cached text geometry.

Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayString.cpp

W3DDisplayString.hExpose complex shaping in Zero Hour display strings +1/-0

Expose complex shaping in Zero Hour display strings

• Adds the W3D display-string override for controlling complex text shaping.

GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DDisplayString.h

W3DDisplayString.cppUse shaped widths and propagate shaping state +15/-0

Use shaped widths and propagate shaping state

• Returns whole-run Uniscribe width when applicable and applies shaping enablement to normal and hotkey renderers. State changes invalidate cached text geometry.

GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayString.cpp

Bug fix (2) +10 / -0
GameWindowManager.cppKeep Generals text entries on legacy rendering +5/-0

Keep Generals text entries on legacy rendering

• Disables complex shaping for editable, selected, and composition display strings because caret and partial-character metrics remain per-character.

Generals/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp

GameWindowManager.cppKeep Zero Hour text entries on legacy rendering +5/-0

Keep Zero Hour text entries on legacy rendering

• Disables complex shaping for editable, selected, and composition display strings until shaped caret positioning is supported.

GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp

Other (3) +184 / -0
CMakeLists.txtBuild the Uniscribe loader on Windows +2/-0

Build the Uniscribe loader on Windows

• Registers the new runtime loader sources in the Windows-only WWLib source list.

Core/Libraries/Source/WWVegas/WWLib/CMakeLists.txt

Usp10Loader.cppLoad Uniscribe APIs safely at runtime +111/-0

Load Uniscribe APIs safely at runtime

• Implements one-time, lock-protected loading of the system usp10.dll and resolves the shaping APIs used by the renderer. Wrappers return failures when the library or required exports are unavailable.

Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp

Usp10Loader.hDefine the runtime Uniscribe interface +71/-0

Define the runtime Uniscribe interface

• Declares the required Uniscribe flags, opaque analysis types, API wrappers, and resolved function-pointer storage without adding a static import dependency.

Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 28, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Mixed text uses fallback font ✓ Resolved 🐞 Bug ≡ Correctness
Description
The complex path selects AlternateUnicodeFont for the entire string, so Latin characters in mixed
Arabic/Latin UI text no longer use the requested primary font. Existing font behavior delegates only
non-ASCII characters to the configured Unicode fallback, so this changes Latin styling and metrics
whenever the two fonts differ.
Code

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[R1418-1419]

+	FontCharsClass *render_font = AlternateUnicodeFont && this != AlternateUnicodeFont ?
+		AlternateUnicodeFont : this;
Evidence
The normal font lookup keeps characters below 256 in the primary font and delegates only non-ASCII
characters to the alternate font. The new code instead analyzes and outputs the whole string using
the alternate font's DC/HFONT, while game font loading identifies that font specifically as the
Unicode fallback.

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1312-1319]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1418-1427]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1492-1539]
Core/GameEngineDevice/Source/W3DDevice/GameClient/GUI/W3DGameFont.cpp[80-99]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Complex mixed-script strings are measured and rendered entirely with `AlternateUnicodeFont`, replacing the requested primary font for Latin runs.
## Issue Context
The existing character path uses the primary font for ASCII and delegates only non-ASCII characters to `AlternateUnicodeFont`. Preserve that division while shaping the complete bidi string, using run-level font selection/fallback consistently for both measurement and rendering.
## Fix Focus Areas
- Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1418-1427]
- Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1492-1539]
- Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1312-1319]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Unsupported runs rasterize twice 🐞 Bug ➹ Performance
Description
Build_Complex_Sentence creates the complete GDI bitmap and A4R4G4B4 raster before checking whether
its width exceeds WrapWidth. Every single-line complex string requiring wrapping therefore pays
for a full-run shape, bitmap allocation, pixel conversion, and discard before Build_Sentence
renders it again through the legacy wrapped path, with particularly high cost for long text.
Code

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[R687-688]

+	if (!Font->Rasterize_Complex_Text(text, &raster, &text_width, &text_height) ||
+		!Is_Complex_Text_Size_Supported(text_width, text_height))
Evidence
The complex predicate does not exclude wrapped renderers, while the support check rejects runs whose
measured width reaches WrapWidth. Build_Complex_Sentence invokes the allocating rasterizer
before that support check; after it returns false, Build_Sentence immediately continues into the
existing centered/non-centered renderer, proving the discarded first rendering is repeated work.

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[621-632]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[642-645]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[680-692]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1292-1302]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1632-1645]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Complex text is fully rasterized before checking whether its dimensions qualify for the complex single-line path. If its width reaches the configured wrapping width, that raster is discarded and the legacy path renders the text again.
## Issue Context
Use the lightweight Uniscribe extent query and `Is_Complex_Text_Size_Supported` before allocating/rasterizing the full run. Retain a post-rasterization dimension check for defensive consistency if needed.
## Fix Focus Areas
- Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[680-692]
- Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[642-645]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Run rerasterized per chunk ✓ Resolved 🐞 Bug ➹ Performance
Description
Every texture-width chunk calls Blit_Complex_Text, which remeasures, reshapes, allocates a
full-run bitmap, and rasterizes the entire string before copying one slice. Because chunks are
capped by the texture width, rendering cost and allocated pixel work grow quadratically with long
single-line strings and repeat even for ordinary runs wider than one texture.
Code

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[R679-680]

+		if (!Font->Blit_Complex_Text(text, LockedPtr, LockedStride, TextureOffset.I,
+			TextureOffset.J, source_x, chunk_width))
Evidence
The outer loop advances source_x by at most the available texture width, but each iteration
invokes a helper that recomputes full extents, allocates a text_width-wide bitmap, reruns
Uniscribe analysis, and outputs the complete string. Thus a run split into N chunks performs N
full-run rasterizations rather than one rasterization plus N slice copies.

Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[664-690]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1494-1539]
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1542-1552]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The complex string is fully analyzed and rasterized once for every texture chunk, making long-run construction scale quadratically.
## Issue Context
`Build_Complex_Sentence` iterates over texture-sized slices, while `Blit_Complex_Text` recreates a full-width DIB and repeats `ScriptStringAnalyse`/`ScriptStringOut` on every call. Produce the full raster once, then copy each slice into its destination surface.
## Fix Focus Areas
- Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[664-690]
- Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp[1494-1552]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp Outdated
@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds contextual shaping and bidirectional ordering for eligible complex single-line UI text while retaining the legacy renderer for wrapping, editable, hot-key, monospaced, multiline, unsupported, and unavailable-Uniscribe cases.

  • Adds runtime loading for the required Uniscribe APIs.
  • Measures and rasterizes eligible strings as complete shaped runs.
  • Splits shaped rasters safely across existing sentence textures.
  • Keeps Generals and Zero Hour display-string implementations aligned.
  • Disables shaping for editable text until shaped caret metrics are supported.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp Adds complex-text eligibility, measurement, rasterization, bounded surface allocation, and clean legacy fallback; the previously reported oversized-height failures are no longer reachable.
Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.h Extends font and sentence-renderer interfaces with complex-text operations and per-renderer enablement state.
Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.cpp Loads and validates the required Uniscribe functions at runtime, returning failure when the DLL or an export is unavailable.
Core/Libraries/Source/WWVegas/WWLib/Usp10Loader.h Defines the dynamically loaded Uniscribe API contract and constants used by the shaped-text path.
Core/GameEngine/Source/GameClient/GUI/GameWindowManager.cpp Explicitly keeps editable text-entry display strings on the legacy renderer.
Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayString.cpp Integrates shaped width measurement and complex-text enablement into the Generals display-string implementation.
GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplayString.cpp Mirrors the display-string integration for Zero Hour.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Text[Display string] --> Eligible{Eligible complex single-line text?}
    Eligible -- No --> Legacy[Legacy per-character renderer]
    Eligible -- Yes --> Available{Uniscribe available and dimensions supported?}
    Available -- No --> Legacy
    Available -- Yes --> Shape[Measure and rasterize shaped run]
    Shape --> Split[Split raster across sentence textures]
    Split --> Draw[Draw sentence chunks]
    Legacy --> Draw
Loading

Reviews (7): Last reviewed commit: "feat(text): Shape complex single-line UI..." | Re-trigger Greptile

Comment thread Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp
@tintinhamans

Copy link
Copy Markdown

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a852d41fbb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp Outdated
@stephanmeesters

Copy link
Copy Markdown

I can't tell from the text and images what the problems were and how this fixes it.

The main-menu test confirms correct contextual shaping, bidirectional ordering, digit ordering, centering, and clipping.

Can you give before and afters of each of these individually?

@OmarAglan

Copy link
Copy Markdown
Author

I can't tell from the text and images what the problems were and how this fixes it.

The main-menu test confirms correct contextual shaping, bidirectional ordering, digit ordering, centering, and clipping.

Can you give before and afters of each of these individually?

will provide examples of it as soon as possible

@OmarAglan

OmarAglan commented Aug 28, 2026

Copy link
Copy Markdown
Author

I can't tell from the text and images what the problems were and how this fixes it.

The main-menu test confirms correct contextual shaping, bidirectional ordering, digit ordering, centering, and clipping.

Can you give before and afters of each of these individually?

will provide examples of it as soon as possible

the Arabic text as for now!

before

sshot001

after

sshot_20260828_215246_250

@OmarAglan
OmarAglan force-pushed the feature/arabic-ui-text-shaping branch from f88c715 to d25f054 Compare August 28, 2026 19:08
Comment thread Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp Outdated
@OmarAglan
OmarAglan force-pushed the feature/arabic-ui-text-shaping branch from d25f054 to eff4156 Compare August 28, 2026 19:33
@OmarAglan
OmarAglan marked this pull request as draft August 30, 2026 20:13
@OmarAglan

Copy link
Copy Markdown
Author

draft to fix the vc6 issue

uint16 *raster = nullptr;
int text_width = 0;
int text_height = 0;
if (!Font->Rasterize_Complex_Text(text, &raster, &text_width, &text_height) ||

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Get_Complex_Text_Extents and Rasterize_Complex_Text each run ScriptStringAnalyse. If they disagree, rendering falls back to the old path even though layout may have already used the shaped size. Can we get the size and raster from the same analysis?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

it now fixed!
Build_Sentence() no longer measures the shaped text with Get_Complex_Text_Extents() and then performs a second analysis for rasterization and compares the two results.

It now checks only whether the string is eligible for complex rendering. Build_Complex_Sentence() rasterizes the text once and uses the width and height returned by that same analysis for texture admission and chunking. This removes the disagreement fallback described in the review.


if ( font )
{
if ( charPos == -1 )

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This sends every full string through Get_Text_Extents, not just complex text. It also changes multiline width from the sum of all lines to the widest line. Is that intended?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

it now fixed!

For a complete string, getWidth() now asks specifically whether complex-text extents are available. If the string is not eligible for shaping—including plain Latin, multiline text, partial charPos measurements, or strings with complex rendering disabled—it falls through to the original per-character width loop unchanged.

Therefore ordinary Latin strings retain the legacy path, and multiline width retains the previous behavior of summing the widths of its lines.

@bobtista

Copy link
Copy Markdown

We can handle VC6 in a small prerequisite PR by runtime-loading usp10.dll, using DbgHelpLoader as an example. Then this PR can drop the usp10 link and guard the Uniscribe code with _WIN32.

@OmarAglan
OmarAglan force-pushed the feature/arabic-ui-text-shaping branch from eff4156 to 74c1e27 Compare August 31, 2026 20:08
@OmarAglan
OmarAglan marked this pull request as ready for review August 31, 2026 20:10
Comment thread Core/Libraries/Source/WWVegas/WW3D2/render2dsentence.cpp
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 74c1e27

@OmarAglan

OmarAglan commented Aug 31, 2026

Copy link
Copy Markdown
Author

We can handle VC6 in a small prerequisite PR by runtime-loading usp10.dll, using DbgHelpLoader as an example. Then this PR can drop the usp10 link and guard the Uniscribe code with _WIN32.

yes im working on it!

@xezon

xezon commented Sep 1, 2026

Copy link
Copy Markdown

In what shape is the arabic text you tested with? As far as I am aware the old translations had the words reversed to accomodate the game implementation. Can arabic text now be supplied normally?

@OmarAglan

Copy link
Copy Markdown
Author

In what shape is the arabic text you tested with? As far as I am aware the old translations had the words reversed to accomodate the game implementation. Can arabic text now be supplied normally?

i used noraml text arabic, i didnt reverse the text, this fixes the hack that is to reverse the arabic text!
still i need to extand upon this on adding maybe support for arabic text in chat and multi line, this can be addressed in follow up pr.

@OmarAglan

Copy link
Copy Markdown
Author

needs rebase and conflict fix, working on it.

@OmarAglan
OmarAglan force-pushed the feature/arabic-ui-text-shaping branch from 74c1e27 to a845dfe Compare September 2, 2026 18:10
@OmarAglan
OmarAglan force-pushed the feature/arabic-ui-text-shaping branch from a845dfe to 80cebad Compare September 2, 2026 18:24
@OmarAglan

Copy link
Copy Markdown
Author

Rebased and fixed and addressed all the changes required

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 80cebad841

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return false;
}

HFONT old_font = (HFONT)::SelectObject(text_dc, GDIFont);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor the configured Unicode font when shaping

When a locale sets UnicodeFontName, W3DFontLibrary::loadFontData() stores that face in FontCharsClass::AlternateUnicodeFont, and the legacy path routes every non-ASCII glyph through it. This new rasterizer instead selects the primary GDIFont and relies solely on Windows fallback, so complex text can use an unintended system face or show missing glyphs even though the configured Unicode font supports them. Use the configured alternate font, or explicitly include it in the shaping fallback strategy.

Useful? React with 👍 / 👎.

Comment on lines +626 to +627
if (!ComplexTextEnabled || Font == nullptr || text == nullptr || text[0] == 0 || wcschr(text, L'\n') != nullptr ||
ParseHotKey || MonoSpaced)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Shape hotkey-enabled labels that have no accelerator

When a window has WIN_STATUS_HOTKEY_TEXT but its localized string contains no usable accelerator marker, W3DStaticText.cpp still calls setUseHotkey(TRUE), which sets ParseHotKey; W3DDisplayString::draw() later clears only m_useHotKey when searchHotKey() returns empty and never clears the renderer flag. Consequently this condition permanently rejects the complex-text path for such Arabic or other complex-script labels even though there is no hotkey to parse. Gate this exclusion on an actual marker, or clear ParseHotKey and rebuild when no hotkey is found.

Useful? React with 👍 / 👎.

Comment on lines +273 to +275
Vector2 complexExtents;
if ( charPos == -1 && m_textRenderer.Get_Complex_Text_Extents( m_textString.str(), &complexExtents ) )
return (Int)complexExtents.X;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cache shaped width instead of reanalyzing every query

For every full-width query on complex text, this calls Get_Complex_Text_Extents(), which performs a fresh ScriptStringAnalyse even though computeExtents() already calculated and cached the same width in m_size. This occurs in frame-time paths such as InGameUI::drawPlayerInfoList(), which calls getWidth() for every displayed value and calls it twice for each player name on every draw, so Arabic names repeatedly run Uniscribe analysis despite unchanged text. Cache whether the current extent is shaped and return the cached width for these queries.

Useful? React with 👍 / 👎.

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.

5 participants