Last active
August 20, 2024 06:42
-
-
Save runevision/2df4c291c0717a483f7f4b42e01c0313 to your computer and use it in GitHub Desktop.
Text2 extends the Unity UI Text class and makes hyphens and soft hypens work
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.UI; | |
// Text2 extends the Text component in Unity UI. | |
// It makes hyphens and soft hyphens work. | |
// Inserting soft hyphens in text can be tricky and confusing, given they are invisible, | |
// so you can instead also insert Hyphenation Point characters, which will be replaced by soft hyphens: | |
// https://www.compart.com/en/unicode/U+2027 | |
public class Text2 : Text { | |
const char shyChar = '\u00AD'; | |
const string shy = "\u00AD"; | |
const string shySpace = "\u00AD "; | |
const char zwnjChar = '\u200C'; | |
const string zwnj = "\u200C"; | |
const string zwnjSpace = "\u200C "; | |
int LastIndexOfInSubString (string input, int start, int length, string value) { | |
int pos = input.Substring (start, length).LastIndexOf (value); | |
if (pos < 0) | |
return pos; | |
return pos + start; | |
} | |
int GetLineEndCharacterIndex (string text, int line) { | |
int index = line == cachedTextGenerator.lineCount - 1 ? | |
text.Length : | |
cachedTextGenerator.lines[line + 1].startCharIdx; | |
if (text[index - 1] == '\n') | |
index--; | |
if (text[index - 1] == ' ') | |
index--; | |
if (text[index - 1] == shyChar) | |
index--; | |
if (text[index - 1] == zwnjChar) | |
index--; | |
return index; | |
} | |
readonly UIVertex[] m_TempVerts = new UIVertex[4]; | |
protected override void OnPopulateMesh (VertexHelper toFill) { | |
if (font == null) | |
return; | |
// We don't care if we the font Texture changes while we are doing our Update. | |
// The end result of cachedTextGenerator will be valid for this instance. | |
// Otherwise we can get issues like Case 619238. | |
m_DisableFontTextureRebuiltCallback = true; | |
Vector2 extents = rectTransform.rect.size; | |
string text2 = text; | |
if (this.horizontalOverflow == HorizontalWrapMode.Overflow) { | |
// Remove Hyphenation Point characters. | |
text2 = text2.Replace ("\u2027", string.Empty); | |
// Remove soft hyphens since Unity otherwise renders them. | |
text2 = text2.Replace (shy, string.Empty); | |
var settings = GetGenerationSettings (extents); | |
cachedTextGenerator.PopulateWithErrors (text2, settings, gameObject); | |
} | |
else { | |
// Replace Hyphenation Point characters with soft hyphens. | |
text2 = text2.Replace ("\u2027", shy); | |
// Add spaces after soft hyphens. | |
text2 = text2.Replace (shy, shySpace); | |
// Add zero-width non-joiner characters and spaces after hyphens. | |
text2 = text2.Replace ("-", "-" + zwnjSpace); | |
var settings = GetGenerationSettings (extents); | |
cachedTextGenerator.PopulateWithErrors (text2, settings, gameObject); | |
int fixLine = 0; | |
while (fixLine < cachedTextGenerator.lineCount) { | |
var line = cachedTextGenerator.lines[fixLine]; | |
int charA = line.startCharIdx; | |
int charB = GetLineEndCharacterIndex (text2, fixLine); | |
// Remove soft hyphen and space combos from current line. | |
int lengthBefore = text2.Length; | |
text2 = | |
text2.Substring (0, charA) + | |
text2.Substring (charA, charB - charA).Replace (shySpace, "").Replace (zwnjSpace, "") + | |
text2.Substring (charB); | |
// If anything was removed, handle the same line again. | |
if (text2.Length != lengthBefore) { | |
cachedTextGenerator.PopulateWithErrors (text2, settings, gameObject); | |
continue; | |
} | |
// If nothing changed, check if there is room for adding one last bit | |
// to this line by removing the hyphen at the end of the line. | |
bool notLastLine = (fixLine < cachedTextGenerator.lineCount - 1); | |
if (notLastLine && (text2[charB] == shyChar || text2[charB] == zwnjChar)) { | |
// Remove hyphen and space combo at end of line. | |
string text3 = text2.Substring (0, charB) + text2.Substring (charB + 2); | |
cachedTextGenerator.PopulateWithErrors (text3, settings, gameObject); | |
if (fixLine == cachedTextGenerator.lineCount - 1) { | |
// We managed to fit the text on one less line, so clearly | |
// we did manage to add more text tot his line. | |
text2 = text3; | |
} | |
else { | |
int newLineIndex = cachedTextGenerator.lines[fixLine + 1].startCharIdx; | |
int newCharB = GetLineEndCharacterIndex (text3, fixLine); | |
// If we managed to add more to this line | |
// AND the last character of the line without space and hyphens is smaller | |
// than the actual last character (meaning we didn't cut a word improperly) | |
// then accept this modification. | |
if (newCharB >= charB && (newCharB < newLineIndex || text3[newCharB] == '-')) | |
text2 = text3; | |
// Otherwise just proceed to next line. | |
// We need to first set the text generator back to using the unmodified string. | |
else | |
cachedTextGenerator.PopulateWithErrors (text2, settings, gameObject); | |
} | |
} | |
fixLine++; | |
} | |
{ | |
// Remove the zero-width non-joiner characters we added | |
// since Unity actually does render them. | |
int lengthBefore = text2.Length; | |
text2 = text2.Replace ("-" + zwnj, "-"); | |
if (text2.Length < lengthBefore) | |
cachedTextGenerator.PopulateWithErrors (text2, settings, gameObject); | |
} | |
} | |
// The rest of the code is unmodified from the Text class. | |
// Apply the offset to the vertices | |
IList<UIVertex> verts = cachedTextGenerator.verts; | |
float unitsPerPixel = 1 / pixelsPerUnit; | |
int vertCount = verts.Count; | |
// We have no verts to process just return (case 1037923) | |
if (vertCount <= 0) { | |
toFill.Clear (); | |
return; | |
} | |
Vector2 roundingOffset = new Vector2 (verts[0].position.x, verts[0].position.y) * unitsPerPixel; | |
roundingOffset = PixelAdjustPoint (roundingOffset) - roundingOffset; | |
toFill.Clear (); | |
if (roundingOffset != Vector2.zero) { | |
for (int i = 0; i < vertCount; ++i) { | |
int tempVertsIndex = i & 3; | |
m_TempVerts[tempVertsIndex] = verts[i]; | |
m_TempVerts[tempVertsIndex].position *= unitsPerPixel; | |
m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; | |
m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; | |
if (tempVertsIndex == 3) | |
toFill.AddUIVertexQuad (m_TempVerts); | |
} | |
} | |
else { | |
for (int i = 0; i < vertCount; ++i) { | |
int tempVertsIndex = i & 3; | |
m_TempVerts[tempVertsIndex] = verts[i]; | |
m_TempVerts[tempVertsIndex].position *= unitsPerPixel; | |
if (tempVertsIndex == 3) | |
toFill.AddUIVertexQuad (m_TempVerts); | |
} | |
} | |
m_DisableFontTextureRebuiltCallback = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment