Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@runevision
Last active December 15, 2022 10:01
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save runevision/2df4c291c0717a483f7f4b42e01c0313 to your computer and use it in GitHub Desktop.
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
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