UGUI Text에 수평 줄임표(...) 직접 구현하기
텍스트 길이가 텍스트 영역보다 길면 줄임표(ellipsis) 처리가 필요하다.
TextMeshProUGUI는 Overflow 옵션으로 Ellipsis를 제공하지만, 기본 클래스인 Text에는 이 기능이 없다. Text 클래스의 Overflow는 다음 기능만 제공한다.
- Horizontal(가로 영역): Wrap(다음 행으로 개행), Overflow(영역 초과 허용)
- Vertical(세로 영역): Truncate(영역 초과 자르기), Overflow(영역 초과 허용)
Best Fit(글자 크기를 자동으로 줄여 주는 기능)도 있지만, 수평 줄임표(…)를 써야만 하는 경우가 있다. 또 글자 수가 같은 두 문자열이라도 글자마다 폭이 달라 실제 가로 길이는 다를 수 있으므로, 글자 수로 자르는 방식은 맞지 않는다.
Text 클래스를 상속한 EllipsisUGUIText를 만들어 사용한다.
- 수평 줄임표(…)는 유니코드 8230이므로
char타입으로 명시적으로 캐스팅한다. - UGUI
Text클래스는preferredWidth프로퍼티로 실제 글자의 가로 길이를 제공한다. - 이를 이용해
UpdateGenerateText()에서RectTransform의rect.size.x와 비교하고, 영역보다 길면while을 돌며 끝 글자를 하나씩 잘라 낸 뒤 줄임표를 붙인다. OnPopulateMesh()는 텍스트 mesh의 정점을 직접 채울 때 오버라이드하는 메서드다.
using UnityEngine;using UnityEngine.UI;using System.Collections.Generic;using System.Text;
[ExecuteAlways]public class EllipsisUGUIText : Text{ private readonly StringBuilder updatedStringBuilder = new StringBuilder(50); private const char ellipsis = (char)8230;
private string cachedText = ""; public override string text { get { return cachedText; } set { if (string.IsNullOrEmpty(value)) { if (string.IsNullOrEmpty(cachedText)) return; cachedText = ""; UpdateGenerateText();
SetVerticesDirty(); } else if (cachedText != value) { cachedText = value; UpdateGenerateText();
SetVerticesDirty(); SetLayoutDirty(); } } }
#if UNITY_EDITOR protected override void OnValidate() { base.OnValidate();
if (text != m_Text) text = m_Text; }#endif
private void UpdateGenerateText() { m_Text = cachedText;
Vector2 extents = rectTransform.rect.size; extents.x = extents.x < 0 ? -extents.x : extents.x;
if (extents.x / preferredWidth < 1) { float tempPrefferedWidth = preferredWidth;
var settings = GetGenerationSettings(Vector2.zero);
string tempString = m_Text;
while (extents.x / tempPrefferedWidth < 1) { tempString = tempString.Substring(0, tempString.Length - 1); tempPrefferedWidth = cachedTextGeneratorForLayout.GetPreferredWidth(tempString + ellipsis, settings) / pixelsPerUnit; }
updatedStringBuilder.Length = 0; updatedStringBuilder.Append(tempString); updatedStringBuilder.Append(ellipsis); m_Text = updatedStringBuilder.ToString(); updatedStringBuilder.Length = 0; } }
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;
var settings = GetGenerationSettings(extents); cachedTextGenerator.Populate(m_Text, settings);
Rect inputRect = rectTransform.rect;
// get the text alignment anchor point for the text in local space Vector2 textAnchorPivot = GetTextAnchorPivot(base.alignment); Vector2 refPoint = Vector2.zero; refPoint.x = (textAnchorPivot.x == 1 ? inputRect.xMax : inputRect.xMin); refPoint.y = (textAnchorPivot.y == 0 ? inputRect.yMin : inputRect.yMax);
// Determine fraction of pixel to offset text mesh. Vector2 roundingOffset = PixelAdjustPoint(refPoint) - refPoint;
// Apply the offset to the vertices IList<UIVertex> verts = cachedTextGenerator.verts; float unitsPerPixel = 1 / pixelsPerUnit; //Last 4 verts are always a new line... int vertCount = verts.Count;// - 4;
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; }}