using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using FairyGUI.Utils;
namespace FairyGUI
{
///
///
///
public class TextField : DisplayObject, IMeshFactory
{
VertAlignType _verticalAlign;
TextFormat _textFormat;
bool _input;
string _text;
AutoSizeType _autoSize;
bool _wordWrap;
bool _singleLine;
bool _html;
RTLSupport.DirectionType _textDirection;
int _maxWidth;
List _elements;
List _lines;
List _charPositions;
BaseFont _font;
float _textWidth;
float _textHeight;
bool _textChanged;
float _yOffset;
float _fontSizeScale;
float _renderScale;
int _fontVersion;
string _parsedText;
int _ellipsisCharIndex;
RichTextField _richTextField;
const int GUTTER_X = 2;
const int GUTTER_Y = 2;
const float IMAGE_BASELINE = 0.8f;
const int ELLIPSIS_LENGTH = 2;
static float[] STROKE_OFFSET = new float[]
{
-1, 0, 1, 0,
0, -1, 0, 1,
-1, -1, 1, -1,
-1, 1, 1, 1
};
static List sLineChars = new List();
public TextField()
{
_flags |= Flags.TouchDisabled;
_textFormat = new TextFormat();
_fontSizeScale = 1;
_renderScale = UIContentScaler.scaleFactor;
_wordWrap = false;
_text = string.Empty;
_parsedText = string.Empty;
_elements = new List(0);
_lines = new List(1);
CreateGameObject("TextField");
graphics = new NGraphics(gameObject);
graphics.meshFactory = this;
}
internal void EnableRichSupport(RichTextField richTextField)
{
_richTextField = richTextField;
if (richTextField is InputTextField)
{
_input = true;
EnableCharPositionSupport();
}
}
public void EnableCharPositionSupport()
{
if (_charPositions == null)
{
_charPositions = new List();
_textChanged = true;
}
}
///
///
///
public TextFormat textFormat
{
get { return _textFormat; }
set
{
_textFormat = value;
ApplyFormat();
}
}
///
///
///
public void ApplyFormat()
{
string fontName = _textFormat.font;
if (string.IsNullOrEmpty(fontName))
fontName = UIConfig.defaultFont;
BaseFont newFont = FontManager.GetFont(fontName);
if (_font != newFont)
{
_font = newFont;
_fontVersion = _font.version;
graphics.SetShaderAndTexture(_font.shader, _font.mainTexture);
}
if (!string.IsNullOrEmpty(_text))
_textChanged = true;
}
///
///
///
public AlignType align
{
get { return _textFormat.align; }
set
{
if (_textFormat.align != value)
{
_textFormat.align = value;
if (!string.IsNullOrEmpty(_text))
_textChanged = true;
}
}
}
///
///
///
public VertAlignType verticalAlign
{
get
{
return _verticalAlign;
}
set
{
if (_verticalAlign != value)
{
_verticalAlign = value;
if (!_textChanged)
ApplyVertAlign();
}
}
}
///
///
///
public string text
{
get { return _text; }
set
{
if (_text == value && !_html)
return;
_text = value;
_textChanged = true;
_html = false;
}
}
///
///
///
public string htmlText
{
get { return _text; }
set
{
if (_text == value && _html)
return;
_text = value;
_textChanged = true;
_html = true;
}
}
public string parsedText
{
get { return _parsedText; }
}
///
///
///
public AutoSizeType autoSize
{
get { return _autoSize; }
set
{
if (_autoSize != value)
{
_autoSize = value;
_textChanged = true;
}
}
}
///
///
///
public bool wordWrap
{
get { return _wordWrap; }
set
{
if (_wordWrap != value)
{
_wordWrap = value;
_textChanged = true;
}
}
}
///
///
///
public bool singleLine
{
get { return _singleLine; }
set
{
if (_singleLine != value)
{
_singleLine = value;
_textChanged = true;
}
}
}
///
///
///
public float stroke
{
get
{
return _textFormat.outline;
}
set
{
if (_textFormat.outline != value)
{
_textFormat.outline = value;
graphics.SetMeshDirty();
}
}
}
///
///
///
public Color strokeColor
{
get
{
return _textFormat.outlineColor;
}
set
{
if (_textFormat.outlineColor != value)
{
_textFormat.outlineColor = value;
graphics.SetMeshDirty();
}
}
}
///
///
///
public Vector2 shadowOffset
{
get
{
return _textFormat.shadowOffset;
}
set
{
_textFormat.shadowOffset = value;
graphics.SetMeshDirty();
}
}
///
///
///
public float textWidth
{
get
{
if (_textChanged)
BuildLines();
return _textWidth;
}
}
///
///
///
public float textHeight
{
get
{
if (_textChanged)
BuildLines();
return _textHeight;
}
}
///
///
///
public int maxWidth
{
get { return _maxWidth; }
set
{
if (_maxWidth != value)
{
_maxWidth = value;
_textChanged = true;
}
}
}
///
///
///
public List htmlElements
{
get
{
if (_textChanged)
BuildLines();
return _elements;
}
}
///
///
///
public List lines
{
get
{
if (_textChanged)
BuildLines();
return _lines;
}
}
///
///
///
public List charPositions
{
get
{
if (_textChanged)
BuildLines();
graphics.UpdateMesh();
return _charPositions;
}
}
///
///
///
public RichTextField richTextField
{
get { return _richTextField; }
}
///
///
///
public bool Redraw()
{
if (_font == null)
{
_font = FontManager.GetFont(UIConfig.defaultFont);
graphics.SetShaderAndTexture(_font.shader, _font.mainTexture);
_fontVersion = _font.version;
_textChanged = true;
}
if (_font.keepCrisp && _renderScale != UIContentScaler.scaleFactor)
_textChanged = true;
if (_font.version != _fontVersion)
{
_fontVersion = _font.version;
if (_font.mainTexture != graphics.texture)
{
graphics.SetShaderAndTexture(_font.shader, _font.mainTexture);
InvalidateBatchingState();
}
_textChanged = true;
}
if (_textChanged)
BuildLines();
return graphics.UpdateMesh();
}
///
///
///
public bool HasCharacter(char ch)
{
return _font.HasCharacter(ch);
}
///
///
///
///
///
///
///
///
///
public void GetLinesShape(int startLine, float startCharX, int endLine, float endCharX,
bool clipped,
List resultRects)
{
LineInfo line1 = _lines[startLine];
LineInfo line2 = _lines[endLine];
bool leftAlign = _textFormat.align == AlignType.Left;
if (startLine == endLine)
{
Rect r = Rect.MinMaxRect(startCharX, line1.y, endCharX, line1.y + line1.height);
if (clipped)
resultRects.Add(ToolSet.Intersection(ref r, ref _contentRect));
else
resultRects.Add(r);
}
else if (startLine == endLine - 1)
{
Rect r = Rect.MinMaxRect(startCharX, line1.y, leftAlign ? (GUTTER_X + line1.width) : _contentRect.xMax, line1.y + line1.height);
if (clipped)
resultRects.Add(ToolSet.Intersection(ref r, ref _contentRect));
else
resultRects.Add(r);
r = Rect.MinMaxRect(GUTTER_X, line1.y + line1.height, endCharX, line2.y + line2.height);
if (clipped)
resultRects.Add(ToolSet.Intersection(ref r, ref _contentRect));
else
resultRects.Add(r);
}
else
{
Rect r = Rect.MinMaxRect(startCharX, line1.y, leftAlign ? (GUTTER_X + line1.width) : _contentRect.xMax, line1.y + line1.height);
if (clipped)
resultRects.Add(ToolSet.Intersection(ref r, ref _contentRect));
else
resultRects.Add(r);
for (int i = startLine + 1; i < endLine; i++)
{
LineInfo line = _lines[i];
r = Rect.MinMaxRect(GUTTER_X, r.yMax, leftAlign ? (GUTTER_X + line.width) : _contentRect.xMax, line.y + line.height);
if (clipped)
resultRects.Add(ToolSet.Intersection(ref r, ref _contentRect));
else
resultRects.Add(r);
}
r = Rect.MinMaxRect(GUTTER_X, r.yMax, endCharX, line2.y + line2.height);
if (clipped)
resultRects.Add(ToolSet.Intersection(ref r, ref _contentRect));
else
resultRects.Add(r);
}
}
override protected void OnSizeChanged()
{
if ((_flags & Flags.UpdatingSize) == 0)
{
if (_autoSize == AutoSizeType.Shrink || _autoSize == AutoSizeType.Ellipsis || _wordWrap && (_flags & Flags.WidthChanged) != 0)
_textChanged = true;
else if (_autoSize != AutoSizeType.None)
graphics.SetMeshDirty();
if (_verticalAlign != VertAlignType.Top)
ApplyVertAlign();
}
base.OnSizeChanged();
}
public override void EnsureSizeCorrect()
{
if (_textChanged && _autoSize != AutoSizeType.None)
BuildLines();
}
public override void Update(UpdateContext context)
{
if (_richTextField == null) //如果是richTextField,会在update前主动调用了Redraw
Redraw();
base.Update(context);
}
///
/// 准备字体纹理
///
void RequestText()
{
if (!_html)
{
_font.SetFormat(_textFormat, _fontSizeScale);
_font.PrepareCharacters(_parsedText);
if (_autoSize == AutoSizeType.Ellipsis)
_font.PrepareCharacters("…");
}
else
{
int count = _elements.Count;
for (int i = 0; i < count; i++)
{
HtmlElement element = _elements[i];
if (element.type == HtmlElementType.Text)
{
_font.SetFormat(element.format, _fontSizeScale);
_font.PrepareCharacters(element.text);
if (_autoSize == AutoSizeType.Ellipsis)
_font.PrepareCharacters("…");
}
}
}
}
void BuildLines()
{
if (_font == null)
{
_font = FontManager.GetFont(UIConfig.defaultFont);
_fontVersion = _font.version;
graphics.SetShaderAndTexture(_font.shader, _font.mainTexture);
}
_textChanged = false;
graphics.SetMeshDirty();
_renderScale = UIContentScaler.scaleFactor;
_fontSizeScale = 1;
_ellipsisCharIndex = -1;
Cleanup();
if (_text.Length == 0)
{
LineInfo emptyLine = LineInfo.Borrow();
emptyLine.width = 0;
emptyLine.height = _font.GetLineHeight(_textFormat.size);
emptyLine.charIndex = emptyLine.charCount = 0;
emptyLine.y = emptyLine.y2 = GUTTER_Y;
_lines.Add(emptyLine);
_textWidth = _textHeight = 0;
}
else
{
ParseText();
BuildLines2();
if (_autoSize == AutoSizeType.Shrink)
DoShrink();
}
if (_autoSize == AutoSizeType.Both)
{
_flags |= Flags.UpdatingSize;
if (_richTextField != null)
{
if (_input)
{
float w = Mathf.Max(_textFormat.size, _textWidth);
float h = Mathf.Max(_font.GetLineHeight(_textFormat.size) + GUTTER_Y * 2, _textHeight);
_richTextField.SetSize(w, h);
}
else
_richTextField.SetSize(_textWidth, _textHeight);
}
else
SetSize(_textWidth, _textHeight);
InvalidateBatchingState();
_flags &= ~Flags.UpdatingSize;
}
else if (_autoSize == AutoSizeType.Height)
{
_flags |= Flags.UpdatingSize;
if (_richTextField != null)
{
if (_input)
_richTextField.height = Mathf.Max(_font.GetLineHeight(_textFormat.size) + GUTTER_Y * 2, _textHeight);
else
_richTextField.height = _textHeight;
}
else
this.height = _textHeight;
InvalidateBatchingState();
_flags &= ~Flags.UpdatingSize;
}
_yOffset = 0;
ApplyVertAlign();
}
void ParseText()
{
#if RTL_TEXT_SUPPORT
_textDirection = RTLSupport.DetectTextDirection(_text);
#endif
if (_html)
{
HtmlParser.inst.Parse(_text, _textFormat, _elements,
_richTextField != null ? _richTextField.htmlParseOptions : null);
_parsedText = string.Empty;
}
else
_parsedText = _text;
int elementCount = _elements.Count;
if (elementCount == 0)
{
if (_textDirection != RTLSupport.DirectionType.UNKNOW)
_parsedText = RTLSupport.DoMapping(_parsedText);
bool flag = _input || _richTextField != null && _richTextField.emojies != null;
if (!flag)
{
//检查文本中是否有需要转换的字符,如果没有,节省一个new StringBuilder的操作。
int cnt = _parsedText.Length;
for (int i = 0; i < cnt; i++)
{
char ch = _parsedText[i];
if (ch == '\r' || char.IsHighSurrogate(ch))
{
flag = true;
break;
}
}
}
if (flag)
{
StringBuilder buffer = new StringBuilder();
ParseText(buffer, _parsedText, -1);
elementCount = _elements.Count;
_parsedText = buffer.ToString();
}
}
else
{
StringBuilder buffer = new StringBuilder();
int i = 0;
while (i < elementCount)
{
HtmlElement element = _elements[i];
element.charIndex = buffer.Length;
if (element.type == HtmlElementType.Text)
{
if (_textDirection != RTLSupport.DirectionType.UNKNOW)
element.text = RTLSupport.DoMapping(element.text);
i = ParseText(buffer, element.text, i);
elementCount = _elements.Count;
}
else if (element.isEntity)
buffer.Append(' ');
i++;
}
_parsedText = buffer.ToString();
#if RTL_TEXT_SUPPORT
// element.text拼接完后再进行一次判断文本主语序,避免html标签存在把文本变成混合文本 [2018/12/12/ 16:47:42 by aq_1000]
_textDirection = RTLSupport.DetectTextDirection(_parsedText);
#endif
}
}
void BuildLines2()
{
float letterSpacing = _textFormat.letterSpacing * _fontSizeScale;
float lineSpacing = (_textFormat.lineSpacing - 1) * _fontSizeScale;
float rectWidth = _contentRect.width - GUTTER_X * 2;
float rectHeight = _contentRect.height > 0 ? Mathf.Max(_contentRect.height, _font.GetLineHeight(_textFormat.size)) : 0;
float glyphWidth = 0, glyphHeight = 0, baseline = 0;
short wordLen = 0;
bool wordPossible = false;
float posx = 0;
bool checkEdge = _autoSize == AutoSizeType.Ellipsis;
TextFormat format = _textFormat;
_font.SetFormat(format, _fontSizeScale);
bool wrap = _wordWrap && !_singleLine;
if (_maxWidth > 0)
{
wrap = true;
rectWidth = _maxWidth - GUTTER_X * 2;
}
_textWidth = _textHeight = 0;
RequestText();
int elementCount = _elements.Count;
int elementIndex = 0;
HtmlElement element = null;
if (elementCount > 0)
element = _elements[elementIndex];
int textLength = _parsedText.Length;
LineInfo line = LineInfo.Borrow();
_lines.Add(line);
line.y = line.y2 = GUTTER_Y;
sLineChars.Clear();
for (int charIndex = 0; charIndex < textLength; charIndex++)
{
char ch = _parsedText[charIndex];
glyphWidth = glyphHeight = baseline = 0;
while (element != null && element.charIndex == charIndex)
{
if (element.type == HtmlElementType.Text)
{
format = element.format;
_font.SetFormat(format, _fontSizeScale);
}
else
{
IHtmlObject htmlObject = element.htmlObject;
if (_richTextField != null && htmlObject == null)
{
element.space = (int)(rectWidth - line.width - 4);
htmlObject = _richTextField.htmlPageContext.CreateObject(_richTextField, element);
element.htmlObject = htmlObject;
}
if (htmlObject != null)
{
glyphWidth = htmlObject.width + 2;
glyphHeight = htmlObject.height;
baseline = glyphHeight * IMAGE_BASELINE;
}
if (element.isEntity)
ch = '\0'; //indicate it is a place holder
}
elementIndex++;
if (elementIndex < elementCount)
element = _elements[elementIndex];
else
element = null;
}
if (ch == '\0' || ch == '\n')
{
wordPossible = false;
}
else if (_font.GetGlyph(ch == '\t' ? ' ' : ch, out glyphWidth, out glyphHeight, out baseline))
{
if (ch == '\t')
glyphWidth *= 4;
if (wordPossible)
{
if (char.IsWhiteSpace(ch))
{
wordLen = 0;
}
else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z'
|| ch >= '0' && ch <= '9'
|| ch == '.' || ch == '"' || ch == '\''
|| format.specialStyle == TextFormat.SpecialStyle.Subscript
|| format.specialStyle == TextFormat.SpecialStyle.Superscript
|| _textDirection != RTLSupport.DirectionType.UNKNOW && RTLSupport.IsArabicLetter(ch))
{
wordLen++;
}
else
wordPossible = false;
}
else if (char.IsWhiteSpace(ch))
{
wordLen = 0;
wordPossible = true;
}
else if (format.specialStyle == TextFormat.SpecialStyle.Subscript
|| format.specialStyle == TextFormat.SpecialStyle.Superscript)
{
if (sLineChars.Count > 0)
{
wordLen = 2; //避免上标和下标折到下一行
wordPossible = true;
}
}
else
wordPossible = false;
}
else
wordPossible = false;
sLineChars.Add(new LineCharInfo() { width = glyphWidth, height = glyphHeight, baseline = baseline });
if (glyphWidth != 0)
{
if (posx != 0)
posx += letterSpacing;
posx += glyphWidth;
}
if (ch == '\n' && !_singleLine)
{
UpdateLineInfo(line, letterSpacing, sLineChars.Count);
LineInfo newLine = LineInfo.Borrow();
_lines.Add(newLine);
newLine.y = line.y + (line.height + lineSpacing);
if (newLine.y < GUTTER_Y) //lineSpacing maybe negative
newLine.y = GUTTER_Y;
newLine.y2 = newLine.y;
newLine.charIndex = line.charIndex + line.charCount;
if (checkEdge && line.y + line.height < rectHeight)
_ellipsisCharIndex = line.charIndex + Math.Max(0, line.charCount - ELLIPSIS_LENGTH);
sLineChars.Clear();
wordPossible = false;
posx = 0;
line = newLine;
}
else if (posx > rectWidth)
{
if (wrap)
{
int lineCharCount = sLineChars.Count;
int toMoveChars;
if (wordPossible && wordLen < 20 && lineCharCount > 2) //if word had broken, move word to new line
{
toMoveChars = wordLen;
//we caculate the line width WITHOUT the tailing space
UpdateLineInfo(line, letterSpacing, lineCharCount - (toMoveChars + 1));
line.charCount++; //but keep it in this line.
}
else
{
toMoveChars = lineCharCount > 1 ? 1 : 0; //if only one char here, we cant move it to new line
UpdateLineInfo(line, letterSpacing, lineCharCount - toMoveChars);
}
LineInfo newLine = LineInfo.Borrow();
_lines.Add(newLine);
newLine.y = line.y + (line.height + lineSpacing);
if (newLine.y < GUTTER_Y)
newLine.y = GUTTER_Y;
newLine.y2 = newLine.y;
newLine.charIndex = line.charIndex + line.charCount;
posx = 0;
if (toMoveChars != 0)
{
for (int i = line.charCount; i < lineCharCount; i++)
{
LineCharInfo ci = sLineChars[i];
if (posx != 0)
posx += letterSpacing;
posx += ci.width;
}
sLineChars.RemoveRange(0, line.charCount);
}
else
sLineChars.Clear();
if (checkEdge && line.y + line.height < rectHeight)
_ellipsisCharIndex = line.charIndex + Math.Max(0, line.charCount - ELLIPSIS_LENGTH);
wordPossible = false;
line = newLine;
}
else if (checkEdge && _ellipsisCharIndex == -1)
_ellipsisCharIndex = line.charIndex + Math.Max(0, line.charCount - ELLIPSIS_LENGTH);
}
}
UpdateLineInfo(line, letterSpacing, sLineChars.Count);
if (_textWidth > 0)
_textWidth += GUTTER_X * 2;
_textHeight = line.y + line.height + GUTTER_Y;
if (checkEdge && _textWidth <= _contentRect.width && _textHeight <= _contentRect.height + GUTTER_Y)
_ellipsisCharIndex = -1;
_textWidth = Mathf.RoundToInt(_textWidth);
_textHeight = Mathf.RoundToInt(_textHeight);
}
void UpdateLineInfo(LineInfo line, float letterSpacing, int cnt)
{
for (int i = 0; i < cnt; i++)
{
LineCharInfo ci = sLineChars[i];
if (ci.baseline > line.baseline)
{
line.height += (ci.baseline - line.baseline);
line.baseline = ci.baseline;
}
if (ci.height - ci.baseline > line.height - line.baseline)
line.height += (ci.height - ci.baseline - (line.height - line.baseline));
if (ci.width > 0)
{
if (line.width != 0)
line.width += letterSpacing;
line.width += ci.width;
}
}
if (line.height == 0)
{
if (_lines.Count == 1)
line.height = _textFormat.size;
else
line.height = _lines[_lines.Count - 2].height;
}
if (line.width > _textWidth)
_textWidth = line.width;
line.charCount = (short)cnt;
}
void DoShrink()
{
if (_lines.Count > 1 && _textHeight > _contentRect.height)
{
//多行的情况,涉及到自动换行,得用二分法查找最合适的比例,会消耗多一点计算资源
int low = 0;
int high = _textFormat.size;
//先尝试猜测一个比例
_fontSizeScale = Mathf.Sqrt(_contentRect.height / _textHeight);
int cur = Mathf.FloorToInt(_fontSizeScale * _textFormat.size);
while (true)
{
LineInfo.Return(_lines);
BuildLines2();
if (_textWidth > _contentRect.width || _textHeight > _contentRect.height)
high = cur;
else
low = cur;
if (high - low > 1 || high != low && cur == high)
{
cur = low + (high - low) / 2;
_fontSizeScale = (float)cur / _textFormat.size;
}
else
break;
}
}
else if (_textWidth > _contentRect.width)
{
_fontSizeScale = _contentRect.width / _textWidth;
LineInfo.Return(_lines);
BuildLines2();
if (_textWidth > _contentRect.width) //如果还超出,缩小一点再来一次
{
int size = Mathf.FloorToInt(_textFormat.size * _fontSizeScale);
size--;
_fontSizeScale = (float)size / _textFormat.size;
LineInfo.Return(_lines);
BuildLines2();
}
}
}
int ParseText(StringBuilder buffer, string source, int elementIndex)
{
int textLength = source.Length;
int j = 0;
int appendPos = 0;
bool hasEmojies = _richTextField != null && _richTextField.emojies != null;
while (j < textLength)
{
char ch = source[j];
if (ch == '\r')
{
buffer.Append(source, appendPos, j - appendPos);
if (j != textLength - 1 && source[j + 1] == '\n')
j++;
appendPos = j + 1;
buffer.Append('\n');
}
else
{
bool highSurrogate = char.IsHighSurrogate(ch);
if (hasEmojies)
{
uint emojiKey = 0;
Emoji emoji;
if (highSurrogate)
emojiKey = ((uint)source[j + 1] & 0x03FF) + ((((uint)ch & 0x03FF) + 0x40) << 10);
else
emojiKey = ch;
if (_richTextField.emojies.TryGetValue(emojiKey, out emoji))
{
HtmlElement imageElement = HtmlElement.GetElement(HtmlElementType.Image);
imageElement.Set("src", emoji.url);
if (emoji.width != 0)
imageElement.Set("width", emoji.width);
if (emoji.height != 0)
imageElement.Set("height", emoji.height);
if (highSurrogate)
imageElement.text = source.Substring(j, 2);
else
imageElement.text = source.Substring(j, 1);
imageElement.format.align = _textFormat.align;
_elements.Insert(++elementIndex, imageElement);
buffer.Append(source, appendPos, j - appendPos);
appendPos = j;
imageElement.charIndex = buffer.Length;
}
}
if (highSurrogate)
{
buffer.Append(source, appendPos, j - appendPos);
appendPos = j + 2;
j++;//跳过lowSurrogate
buffer.Append(' ');
}
}
j++;
}
if (appendPos < textLength)
buffer.Append(source, appendPos, j - appendPos);
return elementIndex;
}
public void OnPopulateMesh(VertexBuffer vb)
{
if (_textWidth == 0 && _lines.Count == 1)
{
if (_charPositions != null)
{
_charPositions.Clear();
_charPositions.Add(new CharPosition());
}
if (_richTextField != null)
_richTextField.RefreshObjects();
return;
}
float letterSpacing = _textFormat.letterSpacing * _fontSizeScale;
TextFormat format = _textFormat;
_font.SetFormat(format, _fontSizeScale);
_font.UpdateGraphics(graphics);
float rectWidth = _contentRect.width > 0 ? (_contentRect.width - GUTTER_X * 2) : 0;
float rectHeight = _contentRect.height > 0 ? Mathf.Max(_contentRect.height, _font.GetLineHeight(format.size)) : 0;
if (_charPositions != null)
_charPositions.Clear();
List vertList = vb.vertices;
List uvList = vb.uvs;
List uv2List = vb.uvs2;
List colList = vb.colors;
HtmlLink currentLink = null;
float linkStartX = 0;
int linkStartLine = 0;
float posx = 0;
float indent_x;
bool clipping = !_input && (_autoSize == AutoSizeType.None || _autoSize == AutoSizeType.Ellipsis);
bool lineClipped;
AlignType lineAlign;
float glyphWidth, glyphHeight, baseline;
short vertCount;
float underlineStart;
float strikethroughStart;
int minFontSize;
int maxFontSize;
string rtlLine = null;
int elementIndex = 0;
int elementCount = _elements.Count;
HtmlElement element = null;
if (elementCount > 0)
element = _elements[elementIndex];
int lineCount = _lines.Count;
for (int i = 0; i < lineCount; ++i)
{
LineInfo line = _lines[i];
if (line.charCount == 0)
continue;
lineClipped = clipping && i != 0 && line.y + line.height > rectHeight;
lineAlign = format.align;
if (element != null && element.charIndex == line.charIndex)
lineAlign = element.format.align;
else
lineAlign = format.align;
if (_textDirection == RTLSupport.DirectionType.RTL)
{
if (lineAlign == AlignType.Center)
indent_x = (int)((rectWidth + line.width) / 2);
else if (lineAlign == AlignType.Right)
indent_x = rectWidth;
else
indent_x = line.width + GUTTER_X * 2;
if (indent_x > rectWidth)
indent_x = rectWidth;
posx = indent_x - GUTTER_X;
}
else
{
if (lineAlign == AlignType.Center)
indent_x = (int)((rectWidth - line.width) / 2);
else if (lineAlign == AlignType.Right)
indent_x = rectWidth - line.width;
else
indent_x = 0;
if (indent_x < 0)
indent_x = 0;
posx = GUTTER_X + indent_x;
}
int lineCharCount = line.charCount;
underlineStart = posx;
strikethroughStart = posx;
minFontSize = maxFontSize = format.size;
if (_textDirection != RTLSupport.DirectionType.UNKNOW)
{
rtlLine = _parsedText.Substring(line.charIndex, lineCharCount);
if (_textDirection == RTLSupport.DirectionType.RTL)
rtlLine = RTLSupport.ConvertLineR(rtlLine);
else
rtlLine = RTLSupport.ConvertLineL(rtlLine);
lineCharCount = rtlLine.Length;
}
for (int j = 0; j < lineCharCount; j++)
{
int charIndex = line.charIndex + j;
char ch = rtlLine != null ? rtlLine[j] : _parsedText[charIndex];
bool isEllipsis = charIndex == _ellipsisCharIndex;
while (element != null && charIndex == element.charIndex)
{
if (element.type == HtmlElementType.Text)
{
vertCount = 0;
if (format.underline != element.format.underline)
{
if (format.underline)
{
if (!lineClipped)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - underlineStart;
else
lineWidth = underlineStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(underlineStart < posx ? underlineStart : posx, -(line.y + line.baseline), lineWidth,
maxFontSize, 0, vertList, uvList, uv2List, colList);
}
maxFontSize = 0;
}
else
underlineStart = posx;
}
if (format.strikethrough != element.format.strikethrough)
{
if (format.strikethrough)
{
if (!lineClipped)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - strikethroughStart;
else
lineWidth = strikethroughStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(strikethroughStart < posx ? strikethroughStart : posx, -(line.y + line.baseline), lineWidth,
minFontSize, 1, vertList, uvList, uv2List, colList);
}
minFontSize = int.MaxValue;
}
else
strikethroughStart = posx;
}
if (vertCount > 0 && _charPositions != null)
{
CharPosition cp = _charPositions[_charPositions.Count - 1];
cp.vertCount += vertCount;
_charPositions[_charPositions.Count - 1] = cp;
}
format = element.format;
minFontSize = Math.Min(minFontSize, format.size);
maxFontSize = Math.Max(maxFontSize, format.size);
_font.SetFormat(format, _fontSizeScale);
}
else if (element.type == HtmlElementType.Link)
{
currentLink = (HtmlLink)element.htmlObject;
if (currentLink != null)
{
element.position = Vector2.zero;
currentLink.SetPosition(0, 0);
linkStartX = posx;
linkStartLine = i;
}
}
else if (element.type == HtmlElementType.LinkEnd)
{
if (currentLink != null)
{
currentLink.SetArea(linkStartLine, linkStartX, i, posx);
currentLink = null;
}
}
else
{
IHtmlObject htmlObj = element.htmlObject;
if (htmlObj != null)
{
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= htmlObj.width - 2;
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.imgIndex = (short)(elementIndex + 1);
cp.offsetX = posx;
cp.width = (short)htmlObj.width;
_charPositions.Add(cp);
}
if (isEllipsis || lineClipped || clipping && (posx < GUTTER_X || posx > GUTTER_X && posx + htmlObj.width > _contentRect.width - GUTTER_X))
element.status |= 1;
else
element.status &= 254;
element.position = new Vector2(posx + 1, line.y + line.baseline - htmlObj.height * IMAGE_BASELINE);
htmlObj.SetPosition(element.position.x, element.position.y);
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += htmlObj.width + letterSpacing + 2;
}
}
if (element.isEntity)
ch = '\0';
elementIndex++;
if (elementIndex < elementCount)
element = _elements[elementIndex];
else
element = null;
}
if (isEllipsis)
ch = '…';
else if (ch == '\0')
continue;
if (_font.GetGlyph(ch == '\t' ? ' ' : ch, out glyphWidth, out glyphHeight, out baseline))
{
if (ch == '\t')
glyphWidth *= 4;
if (!isEllipsis)
{
if (_textDirection == RTLSupport.DirectionType.RTL)
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (indent_x - GUTTER_X)) && posx < GUTTER_X - 0.5f) //超出区域,剪裁
{
posx -= (letterSpacing + glyphWidth);
continue;
}
posx -= glyphWidth;
}
else
{
if (lineClipped || clipping && (rectWidth < 7 || posx != (GUTTER_X + indent_x)) && posx + glyphWidth > _contentRect.width - GUTTER_X + 0.5f) //超出区域,剪裁
{
posx += letterSpacing + glyphWidth;
continue;
}
}
}
vertCount = (short)_font.DrawGlyph(posx, -(line.y + line.baseline), vertList, uvList, uv2List, colList);
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.vertCount = vertCount;
cp.offsetX = posx;
cp.width = (short)glyphWidth;
_charPositions.Add(cp);
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing + glyphWidth;
}
else //if GetGlyph failed
{
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)i;
cp.charIndex = _charPositions.Count;
cp.offsetX = posx;
_charPositions.Add(cp);
}
if (_textDirection == RTLSupport.DirectionType.RTL)
posx -= letterSpacing;
else
posx += letterSpacing;
}
if (isEllipsis)
lineClipped = true;
}//text loop
if (!lineClipped)
{
vertCount = 0;
if (format.underline)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - underlineStart;
else
lineWidth = underlineStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(underlineStart < posx ? underlineStart : posx, -(line.y + line.baseline), lineWidth,
maxFontSize, 0, vertList, uvList, uv2List, colList);
}
if (format.strikethrough)
{
float lineWidth;
if (_textDirection == RTLSupport.DirectionType.UNKNOW)
lineWidth = (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx) - strikethroughStart;
else
lineWidth = strikethroughStart - (clipping ? Mathf.Clamp(posx, GUTTER_X, GUTTER_X + rectWidth) : posx);
if (lineWidth > 0)
vertCount += (short)_font.DrawLine(strikethroughStart < posx ? strikethroughStart : posx, -(line.y + line.baseline), lineWidth,
minFontSize, 1, vertList, uvList, uv2List, colList);
}
if (vertCount > 0 && _charPositions != null)
{
CharPosition cp = _charPositions[_charPositions.Count - 1];
cp.vertCount += vertCount;
_charPositions[_charPositions.Count - 1] = cp;
}
}
}//line loop
if (element != null && element.type == HtmlElementType.LinkEnd && currentLink != null)
currentLink.SetArea(linkStartLine, linkStartX, lineCount - 1, posx);
if (_charPositions != null)
{
CharPosition cp = new CharPosition();
cp.lineIndex = (short)(lineCount - 1);
cp.charIndex = _charPositions.Count;
cp.offsetX = posx;
_charPositions.Add(cp);
}
int count = vertList.Count;
if (count > 65000)
{
Debug.LogWarning("Text is too large. A mesh may not have more than 65000 vertices.");
vertList.RemoveRange(65000, count - 65000);
colList.RemoveRange(65000, count - 65000);
uvList.RemoveRange(65000, count - 65000);
if (uv2List.Count > 0)
uv2List.RemoveRange(65000, count - 65000);
count = 65000;
}
if (_font.customOutline)
{
bool hasShadow = _textFormat.shadowOffset.x != 0 || _textFormat.shadowOffset.y != 0;
int allocCount = count;
int drawDirs = 0;
if (_textFormat.outline != 0)
{
drawDirs = UIConfig.enhancedTextOutlineEffect ? 8 : 4;
allocCount += count * drawDirs;
}
if (hasShadow)
allocCount += count;
if (allocCount > 65000)
{
Debug.LogWarning("Text is too large. Outline/shadow effect cannot be completed.");
allocCount = count;
}
if (allocCount != count)
{
VertexBuffer vb2 = VertexBuffer.Begin();
List vertList2 = vb2.vertices;
List colList2 = vb2.colors;
Color32 col = _textFormat.outlineColor;
float outline = _textFormat.outline;
if (outline != 0)
{
for (int j = 0; j < drawDirs; j++)
{
for (int i = 0; i < count; i++)
{
Vector3 vert = vertList[i];
vertList2.Add(new Vector3(vert.x + STROKE_OFFSET[j * 2] * outline, vert.y + STROKE_OFFSET[j * 2 + 1] * outline, 0));
colList2.Add(col);
}
vb2.uvs.AddRange(uvList);
if (uv2List.Count > 0)
vb2.uvs2.AddRange(uv2List);
}
}
if (hasShadow)
{
col = _textFormat.shadowColor;
Vector2 offset = _textFormat.shadowOffset;
for (int i = 0; i < count; i++)
{
Vector3 vert = vertList[i];
vertList2.Add(new Vector3(vert.x + offset.x, vert.y - offset.y, 0));
colList2.Add(col);
}
vb2.uvs.AddRange(uvList);
if (uv2List.Count > 0)
vb2.uvs2.AddRange(uv2List);
}
vb.Insert(vb2);
vb2.End();
}
}
vb.AddTriangles();
if (_richTextField != null)
_richTextField.RefreshObjects();
}
void Cleanup()
{
if (_richTextField != null)
_richTextField.CleanupObjects();
HtmlElement.ReturnElements(_elements);
LineInfo.Return(_lines);
_textWidth = 0;
_textHeight = 0;
_parsedText = string.Empty;
_textDirection = RTLSupport.DirectionType.UNKNOW;
if (_charPositions != null)
_charPositions.Clear();
}
void ApplyVertAlign()
{
float oldOffset = _yOffset;
if (_autoSize == AutoSizeType.Both || _autoSize == AutoSizeType.Height
|| _verticalAlign == VertAlignType.Top)
_yOffset = 0;
else
{
float dh;
if (_textHeight == 0 && _lines.Count > 0)
dh = _contentRect.height - _lines[0].height;
else
dh = _contentRect.height - _textHeight;
if (dh < 0)
dh = 0;
if (_verticalAlign == VertAlignType.Middle)
_yOffset = (int)(dh / 2);
else
_yOffset = dh;
}
if (oldOffset != _yOffset)
{
int cnt = _lines.Count;
for (int i = 0; i < cnt; i++)
_lines[i].y = _lines[i].y2 + _yOffset;
graphics.SetMeshDirty();
}
}
///
///
///
public class LineInfo
{
///
/// 行的宽度
///
public float width;
///
/// 行的高度
///
public float height;
///
/// 文字渲染基线
///
public float baseline;
///
/// 行首的字符索引
///
public int charIndex;
///
/// 行包括的字符个数
///
public short charCount;
///
/// 行的y轴位置
///
public float y;
///
/// 行的y轴位置的备份
///
internal float y2;
static Stack pool = new Stack();
///
///
///
///
public static LineInfo Borrow()
{
if (pool.Count > 0)
{
LineInfo ret = pool.Pop();
ret.width = ret.height = ret.baseline = 0;
ret.y = ret.y2 = 0;
ret.charIndex = ret.charCount = 0;
return ret;
}
else
return new LineInfo();
}
///
///
///
///
public static void Return(LineInfo value)
{
pool.Push(value);
}
///
///
///
///
public static void Return(List values)
{
int cnt = values.Count;
for (int i = 0; i < cnt; i++)
pool.Push(values[i]);
values.Clear();
}
}
///
///
///
public struct LineCharInfo
{
public float width;
public float height;
public float baseline;
}
///
///
///
public struct CharPosition
{
///
/// 字符索引
///
public int charIndex;
///
/// 字符所在的行索引
///
public short lineIndex;
///
/// 字符的x偏移
///
public float offsetX;
///
/// 字符占用的顶点数量。
///
public short vertCount;
///
/// 字符的宽度
///
public short width;
///
/// 大于0表示图片索引。
///
public short imgIndex;
}
}
}