diff --git a/extern/include/utf8.h b/extern/include/utf8.h index e6101b3..84f50d2 100644 --- a/extern/include/utf8.h +++ b/extern/include/utf8.h @@ -337,6 +337,12 @@ static inline ECharDir GetCharDirSmart(const wchar_t* s, int n, int i) if (ch == L'(' || ch == L')') return ECharDir::LTR; + // Common punctuation: treat as strong LTR to prevent jumping around in mixed text + // This makes "Hello + اختبار" and "اختبار + Hello" both keep punctuation in place + if (ch == L'+' || ch == L'-' || ch == L'=' || ch == L'*' || ch == L'/' || + ch == L'<' || ch == L'>' || ch == L'&' || ch == L'|' || ch == L'@' || ch == L'#') + return ECharDir::LTR; + // Percentage sign: attach to numbers (scan nearby for digits/minus/plus) // Handles: "%20", "20%", "-6%", "%d%%", etc. if (ch == L'%') @@ -755,8 +761,9 @@ static inline std::vector BuildVisualChatMessage( } else { - // Apply BiDi to message based on its content - std::vector msgVisual = BuildVisualBidiText_Tagless(msg, msgLen, msgHasRTL); + // Apply BiDi to message with auto-detection (don't force RTL) + // Let the BiDi algorithm detect base direction from first strong character + std::vector msgVisual = BuildVisualBidiText_Tagless(msg, msgLen, false); visual.insert(visual.end(), msgVisual.begin(), msgVisual.end()); } visual.push_back(L' '); @@ -778,8 +785,9 @@ static inline std::vector BuildVisualChatMessage( } else { - // Apply BiDi to message based on its content - std::vector msgVisual = BuildVisualBidiText_Tagless(msg, msgLen, msgHasRTL); + // Apply BiDi to message with auto-detection (don't force RTL) + // Let the BiDi algorithm detect base direction from first strong character + std::vector msgVisual = BuildVisualBidiText_Tagless(msg, msgLen, false); visual.insert(visual.end(), msgVisual.begin(), msgVisual.end()); } } diff --git a/src/EterLib/GrpTextInstance.cpp b/src/EterLib/GrpTextInstance.cpp index 96f0bd3..a33e8e8 100644 --- a/src/EterLib/GrpTextInstance.cpp +++ b/src/EterLib/GrpTextInstance.cpp @@ -260,8 +260,9 @@ void CGraphicTextInstance::Update() // Flush current segment with BiDi before changing color if (!currentSegment.empty()) { + // Use auto-detection for BiDi (don't force RTL) std::vector visual = BuildVisualBidiText_Tagless( - currentSegment.data(), (int)currentSegment.size(), m_computedRTL); + currentSegment.data(), (int)currentSegment.size(), false); for (size_t j = 0; j < visual.size(); ++j) { int w = __DrawCharacter(pFontTexture, visual[j], currentColor); @@ -277,8 +278,9 @@ void CGraphicTextInstance::Update() // Flush segment before restoring color if (!currentSegment.empty()) { + // Use auto-detection for BiDi (don't force RTL) std::vector visual = BuildVisualBidiText_Tagless( - currentSegment.data(), (int)currentSegment.size(), m_computedRTL); + currentSegment.data(), (int)currentSegment.size(), false); for (size_t j = 0; j < visual.size(); ++j) { int w = __DrawCharacter(pFontTexture, visual[j], currentColor); @@ -303,8 +305,9 @@ void CGraphicTextInstance::Update() // Flush any pending non-hyperlink segment first if (!currentSegment.empty()) { + // Use auto-detection for BiDi (don't force RTL) std::vector visual = BuildVisualBidiText_Tagless( - currentSegment.data(), (int)currentSegment.size(), m_computedRTL); + currentSegment.data(), (int)currentSegment.size(), false); for (size_t j = 0; j < visual.size(); ++j) { int w = __DrawCharacter(pFontTexture, visual[j], currentColor); @@ -417,8 +420,9 @@ void CGraphicTextInstance::Update() // Flush any remaining segment if (!currentSegment.empty()) { + // Use auto-detection for BiDi (don't force RTL) std::vector visual = BuildVisualBidiText_Tagless( - currentSegment.data(), (int)currentSegment.size(), m_computedRTL); + currentSegment.data(), (int)currentSegment.size(), false); for (size_t j = 0; j < visual.size(); ++j) { int w = __DrawCharacter(pFontTexture, visual[j], currentColor);