using System;
using System.Collections.Generic;
namespace UnityEngine.UI
{
///
/// Labels are graphics that display text.
///
[AddComponentMenu("UI/Text", 10)]
public class Text : MaskableGraphic, ILayoutElement
{
[SerializeField] private FontData m_FontData = FontData.defaultFontData;
#if UNITY_EDITOR
// needed to track font changes from the inspector
private Font m_LastTrackedFont;
#endif
[TextArea(3, 10)][SerializeField] protected string m_Text = String.Empty;
private TextGenerator m_TextCache;
private TextGenerator m_TextCacheForLayout;
static protected Material s_DefaultText = null;
// We use this flag instead of Unregistering/Registering the callback to avoid allocation.
[NonSerialized] protected bool m_DisableFontTextureRebuiltCallback = false;
protected Text()
{
useLegacyMeshGeneration = false;
}
// text generator,得到字符串在glyph atlas中的UV,构建mesh
///
/// Get or set the material used by this Text.
///
public TextGenerator cachedTextGenerator
{
get { return m_TextCache ?? (m_TextCache = (m_Text.Length != 0 ? new TextGenerator(m_Text.Length) : new TextGenerator())); }
}
public TextGenerator cachedTextGeneratorForLayout
{
get { return m_TextCacheForLayout ?? (m_TextCacheForLayout = new TextGenerator()); }
}
///
/// Text's texture comes from the font.
///
public override Texture mainTexture
{
get
{
if (font != null && font.material != null && font.material.mainTexture != null)
return font.material.mainTexture;
if (m_Material != null)
return m_Material.mainTexture;
return base.mainTexture;
}
}
public void FontTextureChanged()
{
// Only invoke if we are not destroyed.
if (!this)
return;
if (m_DisableFontTextureRebuiltCallback)
return;
cachedTextGenerator.Invalidate();
if (!IsActive())
return;
// this is a bit hacky, but it is currently the
// cleanest solution....
// if we detect the font texture has changed and are in a rebuild loop
// we just regenerate the verts for the new UV's
if (CanvasUpdateRegistry.IsRebuildingGraphics() || CanvasUpdateRegistry.IsRebuildingLayout())
UpdateGeometry();
else
SetAllDirty();
}
public Font font
{
get
{
return m_FontData.font;
}
set
{
if (m_FontData.font == value)
return;
FontUpdateTracker.UntrackText(this);
m_FontData.font = value;
FontUpdateTracker.TrackText(this);
#if UNITY_EDITOR
// needed to track font changes from the inspector
m_LastTrackedFont = value;
#endif
SetAllDirty();
}
}
///
/// Text that's being displayed by the Text.
///
public virtual string text
{
get
{
return m_Text;
}
set
{
if (String.IsNullOrEmpty(value))
{
if (String.IsNullOrEmpty(m_Text))
return;
m_Text = "";
SetVerticesDirty();
}
else if (m_Text != value)
{
m_Text = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
}
///
/// Whether this Text will support rich text.
///
public bool supportRichText
{
get
{
return m_FontData.richText;
}
set
{
if (m_FontData.richText == value)
return;
m_FontData.richText = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
///
/// Wrap mode used by the text.
///
public bool resizeTextForBestFit
{
get
{
return m_FontData.bestFit;
}
set
{
if (m_FontData.bestFit == value)
return;
m_FontData.bestFit = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
public int resizeTextMinSize
{
get
{
return m_FontData.minSize;
}
set
{
if (m_FontData.minSize == value)
return;
m_FontData.minSize = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
public int resizeTextMaxSize
{
get
{
return m_FontData.maxSize;
}
set
{
if (m_FontData.maxSize == value)
return;
m_FontData.maxSize = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
///
/// Alignment anchor used by the text.
///
public TextAnchor alignment
{
get
{
return m_FontData.alignment;
}
set
{
if (m_FontData.alignment == value)
return;
m_FontData.alignment = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
public bool alignByGeometry
{
get
{
return m_FontData.alignByGeometry;
}
set
{
if (m_FontData.alignByGeometry == value)
return;
m_FontData.alignByGeometry = value;
SetVerticesDirty();
}
}
public int fontSize
{
get
{
return m_FontData.fontSize;
}
set
{
// font size改变的时候重建mesh
if (m_FontData.fontSize == value)
return;
m_FontData.fontSize = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
public HorizontalWrapMode horizontalOverflow
{
get
{
return m_FontData.horizontalOverflow;
}
set
{
if (m_FontData.horizontalOverflow == value)
return;
m_FontData.horizontalOverflow = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
public VerticalWrapMode verticalOverflow
{
get
{
return m_FontData.verticalOverflow;
}
set
{
if (m_FontData.verticalOverflow == value)
return;
m_FontData.verticalOverflow = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
public float lineSpacing
{
get
{
return m_FontData.lineSpacing;
}
set
{
if (m_FontData.lineSpacing == value)
return;
m_FontData.lineSpacing = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
///
/// Font style used by the Text's text.
///
public FontStyle fontStyle
{
get
{
return m_FontData.fontStyle;
}
set
{
if (m_FontData.fontStyle == value)
return;
m_FontData.fontStyle = value;
SetVerticesDirty();
SetLayoutDirty();
}
}
public float pixelsPerUnit
{
get
{
var localCanvas = canvas;
if (!localCanvas)
return 1;
// For dynamic fonts, ensure we use one pixel per pixel on the screen.
if (!font || font.dynamic)
return localCanvas.scaleFactor;
// For non-dynamic fonts, calculate pixels per unit based on specified font size relative to font object's own font size.
if (m_FontData.fontSize <= 0 || font.fontSize <= 0)
return 1;
return font.fontSize / (float)m_FontData.fontSize;
}
}
protected override void OnEnable()
{
base.OnEnable();
cachedTextGenerator.Invalidate();
FontUpdateTracker.TrackText(this);
}
protected override void OnDisable()
{
FontUpdateTracker.UntrackText(this);
base.OnDisable();
}
protected override void UpdateGeometry()
{
if (font != null)
{
base.UpdateGeometry();
}
}
#if UNITY_EDITOR
protected override void Reset()
{
AssignDefaultFont();
}
#endif
internal void AssignDefaultFont()
{
font = Resources.GetBuiltinResource("Arial.ttf");
}
// 根据FontData设置TextGenerationSettings
public TextGenerationSettings GetGenerationSettings(Vector2 extents)
{
var settings = new TextGenerationSettings();
settings.generationExtents = extents;
if (font != null && font.dynamic)
{
settings.fontSize = m_FontData.fontSize;
settings.resizeTextMinSize = m_FontData.minSize;
settings.resizeTextMaxSize = m_FontData.maxSize;
}
// Other settings
settings.textAnchor = m_FontData.alignment;
settings.alignByGeometry = m_FontData.alignByGeometry;
settings.scaleFactor = pixelsPerUnit;
settings.color = color; // 这里是顶点颜色
settings.font = font;
settings.pivot = rectTransform.pivot;
settings.richText = m_FontData.richText;
settings.lineSpacing = m_FontData.lineSpacing;
settings.fontStyle = m_FontData.fontStyle;
settings.resizeTextForBestFit = m_FontData.bestFit;
settings.updateBounds = false;
settings.horizontalOverflow = m_FontData.horizontalOverflow;
settings.verticalOverflow = m_FontData.verticalOverflow;
return settings;
}
static public Vector2 GetTextAnchorPivot(TextAnchor anchor)
{
switch (anchor)
{
case TextAnchor.LowerLeft: return new Vector2(0, 0);
case TextAnchor.LowerCenter: return new Vector2(0.5f, 0);
case TextAnchor.LowerRight: return new Vector2(1, 0);
case TextAnchor.MiddleLeft: return new Vector2(0, 0.5f);
case TextAnchor.MiddleCenter: return new Vector2(0.5f, 0.5f);
case TextAnchor.MiddleRight: return new Vector2(1, 0.5f);
case TextAnchor.UpperLeft: return new Vector2(0, 1);
case TextAnchor.UpperCenter: return new Vector2(0.5f, 1);
case TextAnchor.UpperRight: return new Vector2(1, 1);
default: return Vector2.zero;
}
}
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;
// 生成对应的网格数据(顶点、UV)
var settings = GetGenerationSettings(extents);
cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);
// Apply the offset to the vertices
IList verts = cachedTextGenerator.verts;
float unitsPerPixel = 1 / pixelsPerUnit;
//Last 4 verts are always a new line... (\n)
int vertCount = verts.Count - 4;
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;
}
public virtual void CalculateLayoutInputHorizontal() {}
public virtual void CalculateLayoutInputVertical() {}
public virtual float minWidth
{
get { return 0; }
}
public virtual float preferredWidth
{
get
{
var settings = GetGenerationSettings(Vector2.zero);
return cachedTextGeneratorForLayout.GetPreferredWidth(m_Text, settings) / pixelsPerUnit;
}
}
public virtual float flexibleWidth { get { return -1; } }
public virtual float minHeight
{
get { return 0; }
}
public virtual float preferredHeight
{
get
{
var settings = GetGenerationSettings(new Vector2(GetPixelAdjustedRect().size.x, 0.0f));
return cachedTextGeneratorForLayout.GetPreferredHeight(m_Text, settings) / pixelsPerUnit;
}
}
public virtual float flexibleHeight { get { return -1; } }
public virtual int layoutPriority { get { return 0; } }
#if UNITY_EDITOR
public override void OnRebuildRequested()
{
// After a Font asset gets re-imported the managed side gets deleted and recreated,
// that means the delegates are not persisted.
// so we need to properly enforce a consistent state here.
FontUpdateTracker.UntrackText(this);
FontUpdateTracker.TrackText(this);
// Also the textgenerator is no longer valid.
cachedTextGenerator.Invalidate();
base.OnRebuildRequested();
}
// 当编辑器里修改了参数值的时候调这个回调,setDirty,后续重建mesh
// The Text inspector editor can change the font, and we need a way to track changes so that we get the appropriate rebuild callbacks
// We can intercept changes in OnValidate, and keep track of the previous font reference
protected override void OnValidate()
{
if (!IsActive())
{
base.OnValidate();
return;
}
if (m_FontData.font != m_LastTrackedFont)
{
Font newFont = m_FontData.font;
m_FontData.font = m_LastTrackedFont;
FontUpdateTracker.UntrackText(this);
m_FontData.font = newFont;
FontUpdateTracker.TrackText(this);
m_LastTrackedFont = newFont;
}
base.OnValidate();
}
#endif // if UNITY_EDITOR
}
}