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 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; } }