2896 lines
93 KiB
C#
2896 lines
93 KiB
C#
//----------------------------------------------
|
|
// NGUI: Next-Gen UI kit
|
|
// Copyright © 2011-2015 Tasharen Entertainment
|
|
//----------------------------------------------
|
|
|
|
#if !UNITY_3_5
|
|
#define DYNAMIC_FONT
|
|
#endif
|
|
|
|
#define FUNCELL_MODIFIED
|
|
|
|
using UnityEngine;
|
|
using System.Text;
|
|
using System.Diagnostics;
|
|
|
|
/// <summary>
|
|
/// Helper class containing functionality related to using dynamic fonts.
|
|
/// </summary>
|
|
|
|
static public class NGUIText
|
|
{
|
|
public enum Alignment
|
|
{
|
|
Automatic,
|
|
Left,
|
|
Center,
|
|
Right,
|
|
Justified,
|
|
}
|
|
|
|
public enum SymbolStyle
|
|
{
|
|
None,
|
|
Normal,
|
|
Colored,
|
|
}
|
|
|
|
#if FUNCELL_MODIFIED
|
|
public enum ChineseRotType
|
|
{
|
|
None,
|
|
Left,
|
|
Right,
|
|
}
|
|
#endif
|
|
|
|
public class GlyphInfo
|
|
{
|
|
public Vector2 v0;
|
|
public Vector2 v1;
|
|
public Vector2 u0;
|
|
public Vector2 u1;
|
|
public Vector2 u2;
|
|
public Vector2 u3;
|
|
public float advance = 0f;
|
|
public int channel = 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// When printing text, a lot of additional data must be passed in. In order to save allocations,
|
|
/// this data is not passed at all, but is rather set in a single place before calling the functions that use it.
|
|
/// </summary>
|
|
|
|
static public string Language = "";
|
|
|
|
static public UIFont bitmapFont;
|
|
#if DYNAMIC_FONT
|
|
static public Font dynamicFont;
|
|
#endif
|
|
static public GlyphInfo glyph = new GlyphInfo();
|
|
|
|
static public int fontSize = 16;
|
|
static public float fontScale = 1f;
|
|
static public float pixelDensity = 1f;
|
|
static public FontStyle fontStyle = FontStyle.Normal;
|
|
static public Alignment alignment = Alignment.Left;
|
|
static public Color tint = Color.white;
|
|
|
|
static public int rectWidth = 1000000;
|
|
static public int rectHeight = 1000000;
|
|
static public int regionWidth = 1000000;
|
|
static public int regionHeight = 1000000;
|
|
static public int maxLines = 0;
|
|
|
|
static public bool gradient = false;
|
|
static public Color gradientBottom = Color.white;
|
|
static public Color gradientTop = Color.white;
|
|
|
|
static public bool encoding = false;
|
|
static public float spacingX = 0f;
|
|
static public float spacingY = 0f;
|
|
static public bool premultiply = false;
|
|
static public SymbolStyle symbolStyle;
|
|
|
|
static public int finalSize = 0;
|
|
static public float finalSpacingX = 0f;
|
|
static public float finalLineHeight = 0f;
|
|
static public float baseline = 0f;
|
|
static public bool useSymbols = false;
|
|
|
|
#if FUNCELL_MODIFIED
|
|
//是否是东方文字
|
|
static public bool isEastern = false;
|
|
static public bool isVie = false;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Recalculate the 'final' values.
|
|
/// </summary>
|
|
|
|
static public void Update() { Update(true); }
|
|
|
|
/// <summary>
|
|
/// Recalculate the 'final' values.
|
|
/// </summary>
|
|
|
|
static public void Update(bool request)
|
|
{
|
|
finalSize = Mathf.RoundToInt(fontSize / pixelDensity);
|
|
finalSpacingX = spacingX * fontScale;
|
|
finalLineHeight = (fontSize + spacingY) * fontScale;
|
|
useSymbols = (bitmapFont != null && bitmapFont.hasSymbols) && encoding && symbolStyle != SymbolStyle.None;
|
|
|
|
#if DYNAMIC_FONT
|
|
Font font = dynamicFont;
|
|
|
|
if (font != null && request)
|
|
{
|
|
font.RequestCharactersInTexture(")_-", finalSize, fontStyle);
|
|
|
|
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
|
if (!font.GetCharacterInfo(')', out mTempChar, finalSize, fontStyle) || mTempChar.vert.height == 0f)
|
|
{
|
|
font.RequestCharactersInTexture("A", finalSize, fontStyle);
|
|
{
|
|
if (!font.GetCharacterInfo('A', out mTempChar, finalSize, fontStyle))
|
|
{
|
|
baseline = 0f;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
float y0 = mTempChar.vert.yMax;
|
|
float y1 = mTempChar.vert.yMin;
|
|
#else
|
|
if (!font.GetCharacterInfo(')', out mTempChar, finalSize, fontStyle) || mTempChar.maxY == 0f)
|
|
{
|
|
font.RequestCharactersInTexture("A", finalSize, fontStyle);
|
|
{
|
|
if (!font.GetCharacterInfo('A', out mTempChar, finalSize, fontStyle))
|
|
{
|
|
baseline = 0f;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
float y0 = mTempChar.maxY;
|
|
float y1 = mTempChar.minY;
|
|
#endif
|
|
baseline = Mathf.Round(y0 + (finalSize - y0 + y1) * 0.5f);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prepare to use the specified text.
|
|
/// </summary>
|
|
|
|
static public void Prepare(string text)
|
|
{
|
|
#if DYNAMIC_FONT
|
|
if (dynamicFont != null)
|
|
dynamicFont.RequestCharactersInTexture(text, finalSize, fontStyle);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the specified symbol.
|
|
/// </summary>
|
|
|
|
static public BMSymbol GetSymbol(string text, int index, int textLength)
|
|
{
|
|
return (bitmapFont != null) ? bitmapFont.MatchSymbol(text, index, textLength) : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the width of the specified glyph. Returns zero if the glyph could not be retrieved.
|
|
/// </summary>
|
|
|
|
static public float GetGlyphWidth(int ch, int prev)
|
|
{
|
|
if (bitmapFont != null)
|
|
{
|
|
bool thinSpace = false;
|
|
|
|
if (ch == '\u2009')
|
|
{
|
|
thinSpace = true;
|
|
ch = ' ';
|
|
}
|
|
|
|
BMGlyph bmg = bitmapFont.bmFont.GetGlyph(ch);
|
|
|
|
if (bmg != null)
|
|
{
|
|
int adv = bmg.advance;
|
|
if (thinSpace) adv >>= 1;
|
|
return fontScale * ((prev != 0) ? adv + bmg.GetKerning(prev) : bmg.advance);
|
|
}
|
|
}
|
|
#if DYNAMIC_FONT
|
|
else if (dynamicFont != null)
|
|
{
|
|
if (dynamicFont.GetCharacterInfo((char)ch, out mTempChar, finalSize, fontStyle))
|
|
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
|
return mTempChar.width * fontScale * pixelDensity;
|
|
#else
|
|
return mTempChar.advance * fontScale * pixelDensity;
|
|
#endif
|
|
}
|
|
#endif
|
|
return 0f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the specified glyph.
|
|
/// </summary>
|
|
|
|
static public GlyphInfo GetGlyph(int ch, int prev)
|
|
{
|
|
if (bitmapFont != null)
|
|
{
|
|
bool thinSpace = false;
|
|
|
|
if (ch == '\u2009')
|
|
{
|
|
thinSpace = true;
|
|
ch = ' ';
|
|
}
|
|
|
|
BMGlyph bmg = bitmapFont.bmFont.GetGlyph(ch);
|
|
|
|
if (bmg != null)
|
|
{
|
|
int kern = (prev != 0) ? bmg.GetKerning(prev) : 0;
|
|
glyph.v0.x = (prev != 0) ? bmg.offsetX + kern : bmg.offsetX;
|
|
glyph.v1.y = -bmg.offsetY;
|
|
|
|
glyph.v1.x = glyph.v0.x + bmg.width;
|
|
glyph.v0.y = glyph.v1.y - bmg.height;
|
|
|
|
glyph.u0.x = bmg.x;
|
|
glyph.u0.y = bmg.y + bmg.height;
|
|
|
|
glyph.u2.x = bmg.x + bmg.width;
|
|
glyph.u2.y = bmg.y;
|
|
|
|
glyph.u1.x = glyph.u0.x;
|
|
glyph.u1.y = glyph.u2.y;
|
|
|
|
glyph.u3.x = glyph.u2.x;
|
|
glyph.u3.y = glyph.u0.y;
|
|
|
|
int adv = bmg.advance;
|
|
if (thinSpace) adv >>= 1;
|
|
glyph.advance = adv + kern;
|
|
glyph.channel = bmg.channel;
|
|
|
|
if (fontScale != 1f)
|
|
{
|
|
glyph.v0 *= fontScale;
|
|
glyph.v1 *= fontScale;
|
|
glyph.advance *= fontScale;
|
|
}
|
|
return glyph;
|
|
}
|
|
}
|
|
#if DYNAMIC_FONT
|
|
else if (dynamicFont != null)
|
|
{
|
|
if (dynamicFont.GetCharacterInfo((char)ch, out mTempChar, finalSize, fontStyle))
|
|
{
|
|
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7
|
|
glyph.v0.x = mTempChar.vert.xMin;
|
|
glyph.v1.x = glyph.v0.x + mTempChar.vert.width;
|
|
|
|
glyph.v0.y = mTempChar.vert.yMax - baseline;
|
|
glyph.v1.y = glyph.v0.y - mTempChar.vert.height;
|
|
|
|
glyph.u0.x = mTempChar.uv.xMin;
|
|
glyph.u0.y = mTempChar.uv.yMin;
|
|
|
|
glyph.u2.x = mTempChar.uv.xMax;
|
|
glyph.u2.y = mTempChar.uv.yMax;
|
|
|
|
if (mTempChar.flipped)
|
|
{
|
|
glyph.u1 = new Vector2(glyph.u2.x, glyph.u0.y);
|
|
glyph.u3 = new Vector2(glyph.u0.x, glyph.u2.y);
|
|
}
|
|
else
|
|
{
|
|
glyph.u1 = new Vector2(glyph.u0.x, glyph.u2.y);
|
|
glyph.u3 = new Vector2(glyph.u2.x, glyph.u0.y);
|
|
}
|
|
|
|
glyph.advance = mTempChar.width;
|
|
glyph.channel = 0;
|
|
#else
|
|
glyph.v0.x = mTempChar.minX;
|
|
glyph.v1.x = mTempChar.maxX;
|
|
|
|
glyph.v0.y = mTempChar.maxY - baseline;
|
|
glyph.v1.y = mTempChar.minY - baseline;
|
|
|
|
glyph.u0 = mTempChar.uvTopLeft;
|
|
glyph.u1 = mTempChar.uvBottomLeft;
|
|
glyph.u2 = mTempChar.uvBottomRight;
|
|
glyph.u3 = mTempChar.uvTopRight;
|
|
|
|
glyph.advance = mTempChar.advance;
|
|
glyph.channel = 0;
|
|
#endif
|
|
glyph.v0.x = Mathf.Round(glyph.v0.x);
|
|
glyph.v0.y = Mathf.Round(glyph.v0.y);
|
|
glyph.v1.x = Mathf.Round(glyph.v1.x);
|
|
glyph.v1.y = Mathf.Round(glyph.v1.y);
|
|
|
|
float pd = fontScale * pixelDensity;
|
|
|
|
if (pd != 1f)
|
|
{
|
|
glyph.v0 *= pd;
|
|
glyph.v1 *= pd;
|
|
glyph.advance *= pd;
|
|
}
|
|
return glyph;
|
|
}
|
|
}
|
|
#endif
|
|
return null;
|
|
}
|
|
|
|
static Color mInvisible = new Color(0f, 0f, 0f, 0f);
|
|
static BetterList<Color> mColors = new BetterList<Color>();
|
|
static float mAlpha = 1f;
|
|
#if DYNAMIC_FONT
|
|
static CharacterInfo mTempChar;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Parse Aa syntax alpha encoded in the string.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public float ParseAlpha(string text, int index)
|
|
{
|
|
int a = (NGUIMath.HexToDecimal(text[index + 1]) << 4) | NGUIMath.HexToDecimal(text[index + 2]);
|
|
return Mathf.Clamp01(a / 255f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a RrGgBb color encoded in the string.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public Color ParseColor(string text, int offset) { return ParseColor24(text, offset); }
|
|
|
|
/// <summary>
|
|
/// Parse a RrGgBb color encoded in the string.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public Color ParseColor24(string text, int offset)
|
|
{
|
|
int r = (NGUIMath.HexToDecimal(text[offset]) << 4) | NGUIMath.HexToDecimal(text[offset + 1]);
|
|
int g = (NGUIMath.HexToDecimal(text[offset + 2]) << 4) | NGUIMath.HexToDecimal(text[offset + 3]);
|
|
int b = (NGUIMath.HexToDecimal(text[offset + 4]) << 4) | NGUIMath.HexToDecimal(text[offset + 5]);
|
|
float f = 1f / 255f;
|
|
return new Color(f * r, f * g, f * b);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a RrGgBbAa color encoded in the string.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public Color ParseColor32(string text, int offset)
|
|
{
|
|
int r = (NGUIMath.HexToDecimal(text[offset]) << 4) | NGUIMath.HexToDecimal(text[offset + 1]);
|
|
int g = (NGUIMath.HexToDecimal(text[offset + 2]) << 4) | NGUIMath.HexToDecimal(text[offset + 3]);
|
|
int b = (NGUIMath.HexToDecimal(text[offset + 4]) << 4) | NGUIMath.HexToDecimal(text[offset + 5]);
|
|
int a = (NGUIMath.HexToDecimal(text[offset + 6]) << 4) | NGUIMath.HexToDecimal(text[offset + 7]);
|
|
float f = 1f / 255f;
|
|
return new Color(f * r, f * g, f * b, f * a);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The reverse of ParseColor -- encodes a color in RrGgBb format.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public string EncodeColor(Color c) { return EncodeColor24(c); }
|
|
|
|
/// <summary>
|
|
/// Convenience function that wraps the specified text block in a color tag.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public string EncodeColor(string text, Color c) { return "[c][" + EncodeColor24(c) + "]" + text + "[-][/c]"; }
|
|
|
|
/// <summary>
|
|
/// The reverse of ParseAlpha -- encodes a color in Aa format.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public string EncodeAlpha(float a)
|
|
{
|
|
int i = Mathf.Clamp(Mathf.RoundToInt(a * 255f), 0, 255);
|
|
return NGUIMath.DecimalToHex8(i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The reverse of ParseColor24 -- encodes a color in RrGgBb format.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public string EncodeColor24(Color c)
|
|
{
|
|
int i = 0xFFFFFF & (NGUIMath.ColorToInt(c) >> 8);
|
|
return NGUIMath.DecimalToHex24(i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The reverse of ParseColor32 -- encodes a color in RrGgBb format.
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public string EncodeColor32(Color c)
|
|
{
|
|
int i = NGUIMath.ColorToInt(c);
|
|
return NGUIMath.DecimalToHex32(i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse an embedded symbol, such as [FFAA00] (set color) or [-] (undo color change). Returns whether the index was adjusted.
|
|
/// </summary>
|
|
|
|
static public bool ParseSymbol(string text, ref int index)
|
|
{
|
|
int n = 1;
|
|
bool bold = false;
|
|
bool italic = false;
|
|
bool underline = false;
|
|
bool strikethrough = false;
|
|
bool ignoreColor = false;
|
|
return ParseSymbol(text, ref index, null, false, ref n, ref bold, ref italic, ref underline, ref strikethrough, ref ignoreColor);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the specified character falls under the 'hex' character category (0-9, A-F).
|
|
/// </summary>
|
|
|
|
[System.Diagnostics.DebuggerHidden]
|
|
[System.Diagnostics.DebuggerStepThrough]
|
|
static public bool IsHex(char ch)
|
|
{
|
|
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse the symbol, if possible. Returns 'true' if the 'index' was adjusted.
|
|
/// Advanced symbol support originally contributed by Rudy Pangestu.
|
|
/// </summary>
|
|
|
|
static public bool ParseSymbol(string text, ref int index, BetterList<Color> colors, bool premultiply,
|
|
ref int sub, ref bool bold, ref bool italic, ref bool underline, ref bool strike, ref bool ignoreColor)
|
|
{
|
|
int length = text.Length;
|
|
|
|
if (index + 3 > length || text[index] != '[') return false;
|
|
|
|
if (text[index + 2] == ']')
|
|
{
|
|
if (text[index + 1] == '-')
|
|
{
|
|
if (colors != null && colors.size > 1)
|
|
colors.RemoveAt(colors.size - 1);
|
|
index += 3;
|
|
return true;
|
|
}
|
|
|
|
string sub3 = text.Substring(index, 3);
|
|
|
|
switch (sub3)
|
|
{
|
|
case "[b]":
|
|
bold = true;
|
|
index += 3;
|
|
return true;
|
|
|
|
case "[i]":
|
|
italic = true;
|
|
index += 3;
|
|
return true;
|
|
|
|
case "[u]":
|
|
underline = true;
|
|
index += 3;
|
|
return true;
|
|
|
|
case "[s]":
|
|
strike = true;
|
|
index += 3;
|
|
return true;
|
|
|
|
case "[c]":
|
|
ignoreColor = true;
|
|
index += 3;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (index + 4 > length) return false;
|
|
|
|
if (text[index + 3] == ']')
|
|
{
|
|
string sub4 = text.Substring(index, 4);
|
|
|
|
switch (sub4)
|
|
{
|
|
case "[/b]":
|
|
bold = false;
|
|
index += 4;
|
|
return true;
|
|
|
|
case "[/i]":
|
|
italic = false;
|
|
index += 4;
|
|
return true;
|
|
|
|
case "[/u]":
|
|
underline = false;
|
|
index += 4;
|
|
return true;
|
|
|
|
case "[/s]":
|
|
strike = false;
|
|
index += 4;
|
|
return true;
|
|
|
|
case "[/c]":
|
|
ignoreColor = false;
|
|
index += 4;
|
|
return true;
|
|
|
|
default:
|
|
{
|
|
char ch0 = text[index + 1];
|
|
char ch1 = text[index + 2];
|
|
|
|
if (IsHex(ch0) && IsHex(ch1))
|
|
{
|
|
int a = (NGUIMath.HexToDecimal(ch0) << 4) | NGUIMath.HexToDecimal(ch1);
|
|
mAlpha = a / 255f;
|
|
index += 4;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index + 5 > length) return false;
|
|
|
|
if (text[index + 4] == ']')
|
|
{
|
|
string sub5 = text.Substring(index, 5);
|
|
|
|
switch (sub5)
|
|
{
|
|
case "[sub]":
|
|
sub = 1;
|
|
index += 5;
|
|
return true;
|
|
|
|
case "[sup]":
|
|
sub = 2;
|
|
index += 5;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (index + 6 > length) return false;
|
|
|
|
if (text[index + 5] == ']')
|
|
{
|
|
string sub6 = text.Substring(index, 6);
|
|
|
|
switch (sub6)
|
|
{
|
|
case "[/sub]":
|
|
sub = 0;
|
|
index += 6;
|
|
return true;
|
|
|
|
case "[/sup]":
|
|
sub = 0;
|
|
index += 6;
|
|
return true;
|
|
|
|
case "[/url]":
|
|
index += 6;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (text[index + 1] == 'u' && text[index + 2] == 'r' && text[index + 3] == 'l' && text[index + 4] == '=')
|
|
{
|
|
int closingBracket = text.IndexOf(']', index + 4);
|
|
|
|
if (closingBracket != -1)
|
|
{
|
|
index = closingBracket + 1;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
index = text.Length;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (index + 8 > length) return false;
|
|
|
|
if (text[index + 7] == ']')
|
|
{
|
|
Color c = ParseColor24(text, index + 1);
|
|
|
|
if (EncodeColor24(c) != text.Substring(index + 1, 6).ToUpper())
|
|
return false;
|
|
|
|
if (colors != null)
|
|
{
|
|
c.a = colors[colors.size - 1].a;
|
|
if (premultiply && c.a != 1f)
|
|
c = Color.Lerp(mInvisible, c, c.a);
|
|
colors.Add(c);
|
|
}
|
|
index += 8;
|
|
return true;
|
|
}
|
|
|
|
if (index + 10 > length) return false;
|
|
|
|
if (text[index + 9] == ']')
|
|
{
|
|
Color c = ParseColor32(text, index + 1);
|
|
if (EncodeColor32(c) != text.Substring(index + 1, 8).ToUpper())
|
|
return false;
|
|
|
|
if (colors != null)
|
|
{
|
|
if (premultiply && c.a != 1f)
|
|
c = Color.Lerp(mInvisible, c, c.a);
|
|
colors.Add(c);
|
|
}
|
|
index += 10;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs through the specified string and removes all color-encoding symbols.
|
|
/// </summary>
|
|
|
|
static public string StripSymbols(string text)
|
|
{
|
|
if (text != null)
|
|
{
|
|
for (int i = 0, imax = text.Length; i < imax;)
|
|
{
|
|
char c = text[i];
|
|
|
|
if (c == '[')
|
|
{
|
|
int sub = 0;
|
|
bool bold = false;
|
|
bool italic = false;
|
|
bool underline = false;
|
|
bool strikethrough = false;
|
|
bool ignoreColor = false;
|
|
int retVal = i;
|
|
|
|
if (ParseSymbol(text, ref retVal, null, false, ref sub, ref bold, ref italic, ref underline, ref strikethrough, ref ignoreColor))
|
|
{
|
|
text = text.Remove(i, retVal - i);
|
|
imax = text.Length;
|
|
continue;
|
|
}
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
|
|
//yuqiang 2018/05/22 use for multi-language
|
|
static public string StripLanSymbol(string text)
|
|
{
|
|
if (string.IsNullOrEmpty(text))
|
|
return string.Empty;
|
|
|
|
if (string.IsNullOrEmpty(Language))
|
|
return text;
|
|
|
|
int offset = 0;
|
|
int startIndex = 0;
|
|
int endIndex = 0;
|
|
|
|
string startLanTag = Language;
|
|
string endLanTag = "/" + Language;
|
|
int lanLen = startLanTag.Length;
|
|
int endLanLen = endLanTag.Length;
|
|
int textLen = text.Length;
|
|
|
|
bool findStart = false;
|
|
bool findEnd = false;
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
for (offset = 0; offset < textLen; ++offset)
|
|
{
|
|
if (offset + lanLen + 1 >= textLen) break;
|
|
|
|
if (text[offset] == '[' && text[offset + lanLen + 1] == ']')
|
|
{
|
|
for (int i = 0; i < lanLen; ++i)
|
|
{
|
|
if (text[offset + i + 1] == startLanTag[i])
|
|
{
|
|
findStart = true;
|
|
}
|
|
else
|
|
{
|
|
findStart = false;
|
|
break;
|
|
}
|
|
}
|
|
if (findStart)
|
|
{
|
|
//skip ']'
|
|
offset += lanLen + 1 + 1;
|
|
startIndex = offset;
|
|
}
|
|
}
|
|
|
|
if (offset + endLanLen + 1 >= textLen) break;
|
|
|
|
if (findStart)
|
|
{
|
|
if (text[offset] == '[' && text[offset + endLanLen + 1] == ']')
|
|
{
|
|
for (int i = 0; i < endLanLen; ++i)
|
|
{
|
|
if (text[offset + i + 1] == endLanTag[i])
|
|
{
|
|
findEnd = true;
|
|
}
|
|
else
|
|
{
|
|
findEnd = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (findEnd)
|
|
{
|
|
endIndex = offset;
|
|
if (endIndex > 0 && endIndex >= startIndex)
|
|
{
|
|
sb.Append(text.Substring(startIndex, endIndex - startIndex));
|
|
}
|
|
startIndex = endIndex;
|
|
findEnd = false;
|
|
//跳到语言tag末尾
|
|
offset += endLanLen + 1 + 1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sb.Length > 0)
|
|
return sb.ToString();
|
|
|
|
return text;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Align the vertices to be right or center-aligned given the line width specified by NGUIText.lineWidth.
|
|
/// </summary>
|
|
|
|
static public void Align(BetterList<Vector3> verts, int indexOffset, float printedWidth, int elements = 4)
|
|
{
|
|
switch (alignment)
|
|
{
|
|
case Alignment.Right:
|
|
{
|
|
float padding = rectWidth - printedWidth;
|
|
if (padding < 0f) return;
|
|
#if UNITY_FLASH
|
|
for (int i = indexOffset; i < verts.size; ++i)
|
|
verts.buffer[i] = verts.buffer[i] + new Vector3(padding, 0f);
|
|
#else
|
|
for (int i = indexOffset; i < verts.size; ++i)
|
|
verts.buffer[i].x += padding;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case Alignment.Center:
|
|
{
|
|
float padding = (rectWidth - printedWidth) * 0.5f;
|
|
if (padding < 0f) return;
|
|
|
|
// Keep it pixel-perfect
|
|
int diff = Mathf.RoundToInt(rectWidth - printedWidth);
|
|
int intWidth = Mathf.RoundToInt(rectWidth);
|
|
|
|
bool oddDiff = (diff & 1) == 1;
|
|
bool oddWidth = (intWidth & 1) == 1;
|
|
if ((oddDiff && !oddWidth) || (!oddDiff && oddWidth))
|
|
padding += 0.5f * fontScale;
|
|
#if UNITY_FLASH
|
|
for (int i = indexOffset; i < verts.size; ++i)
|
|
verts.buffer[i] = verts.buffer[i] + new Vector3(padding, 0f);
|
|
#else
|
|
for (int i = indexOffset; i < verts.size; ++i)
|
|
verts.buffer[i].x += padding;
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
case Alignment.Justified:
|
|
{
|
|
// Printed text needs to reach at least 65% of the width in order to be justified
|
|
if (printedWidth < rectWidth * 0.65f) return;
|
|
|
|
// There must be some padding involved
|
|
float padding = (rectWidth - printedWidth) * 0.5f;
|
|
if (padding < 1f) return;
|
|
|
|
// There must be at least two characters
|
|
int chars = (verts.size - indexOffset) / elements;
|
|
if (chars < 1) return;
|
|
|
|
float progressPerChar = 1f / (chars - 1);
|
|
float scale = rectWidth / printedWidth;
|
|
|
|
for (int i = indexOffset + elements, charIndex = 1; i < verts.size; ++charIndex)
|
|
{
|
|
float x0 = verts.buffer[i].x;
|
|
float x1 = verts.buffer[i + elements / 2].x;
|
|
float w = x1 - x0;
|
|
float x0a = x0 * scale;
|
|
float x1a = x0a + w;
|
|
float x1b = x1 * scale;
|
|
float x0b = x1b - w;
|
|
float progress = charIndex * progressPerChar;
|
|
|
|
x1 = Mathf.Lerp(x1a, x1b, progress);
|
|
x0 = Mathf.Lerp(x0a, x0b, progress);
|
|
x0 = Mathf.Round(x0);
|
|
x1 = Mathf.Round(x1);
|
|
|
|
if (elements == 4)
|
|
{
|
|
#if UNITY_FLASH
|
|
verts.buffer[i] = verts.buffer[i] + new Vector3(x0, 0f);
|
|
verts.buffer[i+1] = verts.buffer[i+1] + new Vector3(x0, 0f);
|
|
verts.buffer[i+2] = verts.buffer[i+2] + new Vector3(x1, 0f);
|
|
verts.buffer[i+3] = verts.buffer[i+3] + new Vector3(x1, 0f);
|
|
i += elements;
|
|
#else
|
|
verts.buffer[i++].x = x0;
|
|
verts.buffer[i++].x = x0;
|
|
verts.buffer[i++].x = x1;
|
|
verts.buffer[i++].x = x1;
|
|
#endif
|
|
}
|
|
else if (elements == 2)
|
|
{
|
|
#if UNITY_FLASH
|
|
verts.buffer[i] = verts.buffer[i] + new Vector3(x0, 0f);
|
|
verts.buffer[i + 1] = verts.buffer[i + 1] + new Vector3(x1, 0f);
|
|
#else
|
|
verts.buffer[i++].x = x0;
|
|
verts.buffer[i++].x = x1;
|
|
#endif
|
|
}
|
|
else if (elements == 1)
|
|
{
|
|
#if UNITY_FLASH
|
|
verts.buffer[i] = verts.buffer[i] + new Vector3(x0, 0f);
|
|
#else
|
|
verts.buffer[i++].x = x0;
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the index of the closest character within the provided list of values.
|
|
/// Meant to be used with the arrays created by PrintExactCharacterPositions().
|
|
/// </summary>
|
|
|
|
static public int GetExactCharacterIndex(BetterList<Vector3> verts, BetterList<int> indices, Vector2 pos)
|
|
{
|
|
for (int i = 0; i < indices.size; ++i)
|
|
{
|
|
int i0 = (i << 1);
|
|
int i1 = i0 + 1;
|
|
|
|
float x0 = verts[i0].x;
|
|
if (pos.x < x0) continue;
|
|
|
|
float x1 = verts[i1].x;
|
|
if (pos.x > x1) continue;
|
|
|
|
float y0 = verts[i0].y;
|
|
if (pos.y < y0) continue;
|
|
|
|
float y1 = verts[i1].y;
|
|
if (pos.y > y1) continue;
|
|
|
|
return indices[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the index of the closest vertex within the provided list of values.
|
|
/// This function first sorts by Y, and only then by X.
|
|
/// Meant to be used with the arrays created by PrintApproximateCharacterPositions().
|
|
/// </summary>
|
|
|
|
static public int GetApproximateCharacterIndex(BetterList<Vector3> verts, BetterList<int> indices, Vector2 pos)
|
|
{
|
|
// First sort by Y, and only then by X
|
|
float bestX = float.MaxValue;
|
|
float bestY = float.MaxValue;
|
|
int bestIndex = 0;
|
|
|
|
for (int i = 0; i < verts.size; ++i)
|
|
{
|
|
float diffY = Mathf.Abs(pos.y - verts[i].y);
|
|
if (diffY > bestY) continue;
|
|
|
|
float diffX = Mathf.Abs(pos.x - verts[i].x);
|
|
|
|
if (diffY < bestY)
|
|
{
|
|
bestY = diffY;
|
|
bestX = diffX;
|
|
bestIndex = i;
|
|
}
|
|
else if (diffX < bestX)
|
|
{
|
|
bestX = diffX;
|
|
bestIndex = i;
|
|
}
|
|
}
|
|
return indices[bestIndex];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the specified character is a space.
|
|
/// </summary>
|
|
|
|
[DebuggerHidden]
|
|
[DebuggerStepThrough]
|
|
static bool IsSpace(int ch) { return (ch == ' ' || ch == 0x200a || ch == 0x200b || ch == '\u2009'); }
|
|
|
|
/// <summary>
|
|
/// Convenience function that ends the line by either appending a new line character or replacing a space with one.
|
|
/// </summary>
|
|
|
|
[DebuggerHidden]
|
|
[DebuggerStepThrough]
|
|
static public void EndLine(ref StringBuilder s)
|
|
{
|
|
int i = s.Length - 1;
|
|
if (i > 0 && IsSpace(s[i])) s[i] = '\n';
|
|
else s.Append('\n');
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convenience function that ends the line by replacing a space with a newline character.
|
|
/// </summary>
|
|
|
|
[DebuggerHidden]
|
|
[DebuggerStepThrough]
|
|
static void ReplaceSpaceWithNewline(ref StringBuilder s)
|
|
{
|
|
int i = s.Length - 1;
|
|
if (i > 0 && IsSpace(s[i])) s[i] = '\n';
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the printed size of the specified string. The returned value is in pixels.
|
|
/// </summary>
|
|
|
|
static public Vector2 CalculatePrintedSize(string text)
|
|
{
|
|
Vector2 v = Vector2.zero;
|
|
|
|
if (!string.IsNullOrEmpty(text))
|
|
{
|
|
// When calculating printed size, get rid of all symbols first since they are invisible anyway
|
|
if (encoding) text = StripSymbols(text);
|
|
|
|
// Ensure we have characters to work with
|
|
Prepare(text);
|
|
|
|
float x = 0f, y = 0f, maxX = 0f;
|
|
int textLength = text.Length, ch = 0, prev = 0;
|
|
|
|
for (int i = 0; i < textLength; ++i)
|
|
{
|
|
ch = text[i];
|
|
|
|
// Start a new line
|
|
if (ch == '\n')
|
|
{
|
|
if (x > maxX) maxX = x;
|
|
x = 0f;
|
|
y += finalLineHeight;
|
|
continue;
|
|
}
|
|
|
|
// Skip invalid characters
|
|
if (ch < ' ') continue;
|
|
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, i, textLength) : null;
|
|
|
|
if (symbol == null)
|
|
{
|
|
float w = GetGlyphWidth(ch, prev);
|
|
|
|
if (w != 0f)
|
|
{
|
|
w += finalSpacingX;
|
|
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x > maxX) maxX = x - finalSpacingX;
|
|
x = w;
|
|
y += finalLineHeight;
|
|
}
|
|
else x += w;
|
|
|
|
prev = ch;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float w = finalSpacingX + symbol.advance * fontScale;
|
|
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x > maxX) maxX = x - finalSpacingX;
|
|
x = w;
|
|
y += finalLineHeight;
|
|
}
|
|
else x += w;
|
|
|
|
i += symbol.sequence.Length - 1;
|
|
prev = 0;
|
|
}
|
|
}
|
|
|
|
v.x = ((x > maxX) ? x - finalSpacingX : maxX);
|
|
v.y = (y + finalLineHeight);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static BetterList<float> mSizes = new BetterList<float>();
|
|
|
|
/// <summary>
|
|
/// Calculate the character index offset required to print the end of the specified text.
|
|
/// </summary>
|
|
|
|
static public int CalculateOffsetToFit(string text)
|
|
{
|
|
if (string.IsNullOrEmpty(text) || regionWidth < 1) return 0;
|
|
|
|
Prepare(text);
|
|
|
|
int textLength = text.Length, ch = 0, prev = 0;
|
|
|
|
for (int i = 0, imax = text.Length; i < imax; ++i)
|
|
{
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, i, textLength) : null;
|
|
|
|
if (symbol == null)
|
|
{
|
|
ch = text[i];
|
|
float w = GetGlyphWidth(ch, prev);
|
|
if (w != 0f) mSizes.Add(finalSpacingX + w);
|
|
prev = ch;
|
|
}
|
|
else
|
|
{
|
|
mSizes.Add(finalSpacingX + symbol.advance * fontScale);
|
|
for (int b = 0, bmax = symbol.sequence.Length - 1; b < bmax; ++b) mSizes.Add(0);
|
|
i += symbol.sequence.Length - 1;
|
|
prev = 0;
|
|
}
|
|
}
|
|
|
|
float remainingWidth = regionWidth;
|
|
int currentCharacterIndex = mSizes.size;
|
|
|
|
while (currentCharacterIndex > 0 && remainingWidth > 0)
|
|
remainingWidth -= mSizes[--currentCharacterIndex];
|
|
|
|
mSizes.Clear();
|
|
|
|
if (remainingWidth < 0) ++currentCharacterIndex;
|
|
return currentCharacterIndex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the end of line that would fit into a field of given width.
|
|
/// </summary>
|
|
|
|
static public string GetEndOfLineThatFits(string text)
|
|
{
|
|
int textLength = text.Length;
|
|
int offset = CalculateOffsetToFit(text);
|
|
return text.Substring(offset, textLength - offset);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Text wrapping functionality. The 'width' and 'height' should be in pixels.
|
|
/// </summary>
|
|
|
|
static public bool WrapText(string text, out string finalText, bool wrapLineColors = false)
|
|
{
|
|
return WrapText(text, out finalText, false, wrapLineColors);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Text wrapping functionality. The 'width' and 'height' should be in pixels.
|
|
/// </summary>
|
|
|
|
static public bool WrapText(string text, out string finalText, bool keepCharCount, bool wrapLineColors, bool useEllipsis = false)
|
|
{
|
|
if (regionWidth < 1 || regionHeight < 1 || finalLineHeight < 1f)
|
|
{
|
|
finalText = "";
|
|
return false;
|
|
}
|
|
|
|
float height = (maxLines > 0) ? Mathf.Min(regionHeight, finalLineHeight * maxLines) : regionHeight;
|
|
int maxLineCount = (maxLines > 0) ? maxLines : 1000000;
|
|
maxLineCount = Mathf.FloorToInt(Mathf.Min(maxLineCount, height / finalLineHeight) + 0.01f);
|
|
|
|
if (maxLineCount == 0)
|
|
{
|
|
finalText = "";
|
|
return false;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(text)) text = " ";
|
|
Prepare(text);
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
int textLength = text.Length;
|
|
float remainingWidth = regionWidth;
|
|
int start = 0, offset = 0, lineCount = 1, prev = 0;
|
|
bool lineIsEmpty = true;
|
|
bool fits = true;
|
|
#if FUNCELL_MODIFIED
|
|
bool isVie = NGUIText.isVie;
|
|
bool eastern = NGUIText.isEastern;
|
|
#else
|
|
bool eastern = false;
|
|
#endif
|
|
|
|
Color c = tint;
|
|
int subscriptMode = 0; // 0 = normal, 1 = subscript, 2 = superscript
|
|
bool bold = false;
|
|
bool italic = false;
|
|
bool underline = false;
|
|
bool strikethrough = false;
|
|
bool ignoreColor = false;
|
|
|
|
if (!useSymbols) wrapLineColors = false;
|
|
if (wrapLineColors)
|
|
{
|
|
mColors.Add(c);
|
|
sb.Append("[");
|
|
sb.Append(NGUIText.EncodeColor(c));
|
|
sb.Append("]");
|
|
}
|
|
|
|
// Run through all characters
|
|
for (; offset < textLength; ++offset)
|
|
{
|
|
#region //normal
|
|
char ch = text[offset];
|
|
if (ch > 12287) eastern = true;
|
|
#if FUNCELL_MODIFIED
|
|
if (ch >= '\x0E00' && ch <= '\x0E7F') eastern = true;
|
|
if (isVie)
|
|
{
|
|
//如果是越南文
|
|
eastern = false;
|
|
}
|
|
#endif
|
|
|
|
// New line character -- start a new line
|
|
if (ch == '\n')
|
|
{
|
|
if (lineCount == maxLineCount) break;
|
|
remainingWidth = regionWidth;
|
|
|
|
// Add the previous word to the final string
|
|
if (start < offset) sb.Append(text.Substring(start, offset - start + 1));
|
|
else sb.Append(ch);
|
|
|
|
if (wrapLineColors)
|
|
{
|
|
for (int i = 0; i < mColors.size; ++i)
|
|
sb.Insert(sb.Length - 1, "[-]");
|
|
|
|
for (int i = 0; i < mColors.size; ++i)
|
|
{
|
|
sb.Append("[");
|
|
sb.Append(NGUIText.EncodeColor(mColors[i]));
|
|
sb.Append("]");
|
|
}
|
|
}
|
|
|
|
lineIsEmpty = true;
|
|
++lineCount;
|
|
start = offset + 1;
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
|
|
// When encoded symbols such as [RrGgBb] or [-] are encountered, skip past them
|
|
if (encoding)
|
|
{
|
|
if (!wrapLineColors)
|
|
{
|
|
if (ParseSymbol(text, ref offset))
|
|
{
|
|
--offset;
|
|
continue;
|
|
}
|
|
}
|
|
else if (ParseSymbol(text, ref offset, mColors, premultiply, ref subscriptMode, ref bold,
|
|
ref italic, ref underline, ref strikethrough, ref ignoreColor))
|
|
{
|
|
if (ignoreColor)
|
|
{
|
|
c = mColors[mColors.size - 1];
|
|
c.a *= mAlpha * tint.a;
|
|
}
|
|
else
|
|
{
|
|
c = tint * mColors[mColors.size - 1];
|
|
c.a *= mAlpha;
|
|
}
|
|
|
|
for (int b = 0, bmax = mColors.size - 2; b < bmax; ++b)
|
|
c.a *= mColors[b].a;
|
|
|
|
--offset;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, offset, textLength) : null;
|
|
|
|
// Calculate how wide this symbol or character is going to be
|
|
float glyphWidth;
|
|
|
|
if (symbol == null)
|
|
{
|
|
// Find the glyph for this character
|
|
float w = GetGlyphWidth(ch, prev);
|
|
if (w == 0f && !IsSpace(ch)) continue;
|
|
glyphWidth = finalSpacingX + w;
|
|
}
|
|
else glyphWidth = finalSpacingX + symbol.advance * fontScale;
|
|
|
|
// Reduce the width
|
|
remainingWidth -= glyphWidth;
|
|
|
|
// If this marks the end of a word, add it to the final string.
|
|
if (IsSpace(ch) && !eastern && start < offset)
|
|
{
|
|
int end = offset - start + 1;
|
|
|
|
// Last word on the last line should not include an invisible character
|
|
if (lineCount == maxLineCount && remainingWidth <= 0f && offset < textLength)
|
|
{
|
|
char cho = text[offset];
|
|
if (cho < ' ' || IsSpace(cho)) --end;
|
|
}
|
|
|
|
sb.Append(text.Substring(start, end));
|
|
lineIsEmpty = false;
|
|
start = offset + 1;
|
|
prev = ch;
|
|
}
|
|
|
|
// Doesn't fit?
|
|
if (Mathf.RoundToInt(remainingWidth) < 0)
|
|
{
|
|
// Can't start a new line
|
|
if (lineIsEmpty || lineCount == maxLineCount)
|
|
{
|
|
// Adds "..." at the end of text that doesn't fit. Contributed by Jason Nollan.
|
|
if (useEllipsis && lineCount == maxLineCount && offset > 1)
|
|
{
|
|
float ellipsisWidth = GetGlyphWidth('.', '.') * 3f;
|
|
|
|
if (ellipsisWidth < regionWidth)
|
|
{
|
|
remainingWidth += glyphWidth;
|
|
int tempOffset = offset;
|
|
int removeCount = 0;
|
|
|
|
while (tempOffset > 1 && remainingWidth < ellipsisWidth)
|
|
{
|
|
--tempOffset;
|
|
char prevCh = text[tempOffset - 1];
|
|
char characterToRemove = text[tempOffset];
|
|
bool isCaseWhereSpaceShouldBeInStringBuilderButIsnt = (remainingWidth == 0 && IsSpace(characterToRemove));
|
|
remainingWidth += GetGlyphWidth(characterToRemove, prevCh);
|
|
if (tempOffset < start && !isCaseWhereSpaceShouldBeInStringBuilderButIsnt)
|
|
++removeCount;
|
|
}
|
|
|
|
if (remainingWidth >= ellipsisWidth)
|
|
{
|
|
if (removeCount > 0)
|
|
sb.Length = Mathf.Max(0, sb.Length - removeCount);
|
|
|
|
sb.Append(text.Substring(start, Mathf.Max(0, tempOffset - start)));
|
|
while (sb.Length > 0 && IsSpace(sb[sb.Length - 1])) --sb.Length;
|
|
sb.Append("...");
|
|
|
|
++lineCount;
|
|
start = offset = tempOffset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is the first word on the line -- add it up to the character that fits
|
|
sb.Append(text.Substring(start, Mathf.Max(0, offset - start)));
|
|
bool space = IsSpace(ch);
|
|
if (!space && !eastern) fits = false;
|
|
|
|
if (wrapLineColors && mColors.size > 0) sb.Append("[-]");
|
|
|
|
if (lineCount++ == maxLineCount)
|
|
{
|
|
start = offset;
|
|
break;
|
|
}
|
|
|
|
if (keepCharCount) ReplaceSpaceWithNewline(ref sb);
|
|
else EndLine(ref sb);
|
|
|
|
if (wrapLineColors)
|
|
{
|
|
for (int i = 0; i < mColors.size; ++i)
|
|
sb.Insert(sb.Length - 1, "[-]");
|
|
|
|
for (int i = 0; i < mColors.size; ++i)
|
|
{
|
|
sb.Append("[");
|
|
sb.Append(NGUIText.EncodeColor(mColors[i]));
|
|
sb.Append("]");
|
|
}
|
|
}
|
|
|
|
// Start a brand-new line
|
|
lineIsEmpty = true;
|
|
|
|
if (space)
|
|
{
|
|
start = offset + 1;
|
|
remainingWidth = regionWidth;
|
|
}
|
|
else
|
|
{
|
|
start = offset;
|
|
remainingWidth = regionWidth - glyphWidth;
|
|
}
|
|
prev = 0;
|
|
}
|
|
else
|
|
{
|
|
// Revert the position to the beginning of the word and reset the line
|
|
lineIsEmpty = true;
|
|
remainingWidth = regionWidth;
|
|
offset = start - 1;
|
|
prev = 0;
|
|
|
|
if (lineCount++ == maxLineCount) break;
|
|
if (keepCharCount) ReplaceSpaceWithNewline(ref sb);
|
|
else EndLine(ref sb);
|
|
|
|
if (wrapLineColors)
|
|
{
|
|
for (int i = 0; i < mColors.size; ++i)
|
|
sb.Insert(sb.Length - 1, "[-]");
|
|
|
|
for (int i = 0; i < mColors.size; ++i)
|
|
{
|
|
sb.Append("[");
|
|
sb.Append(NGUIText.EncodeColor(mColors[i]));
|
|
sb.Append("]");
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else prev = ch;
|
|
|
|
// Advance the offset past the symbol
|
|
if (symbol != null)
|
|
{
|
|
offset += symbol.length - 1;
|
|
prev = 0;
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
if (start < offset) sb.Append(text.Substring(start, offset - start));
|
|
if (wrapLineColors && mColors.size > 0) sb.Append("[-]");
|
|
finalText = sb.ToString();
|
|
|
|
mColors.Clear();
|
|
return fits && ((offset == textLength) || (lineCount <= Mathf.Min(maxLines, maxLineCount)));
|
|
}
|
|
|
|
static Color32 s_c0, s_c1;
|
|
|
|
#if FUNCELL_MODIFIED
|
|
static private void AddVerts(bool isChinese, ChineseRotType chineseRotType, BetterList<Vector3> verts, Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4)
|
|
{
|
|
if (isChinese && chineseRotType != ChineseRotType.None)
|
|
{
|
|
if (chineseRotType == ChineseRotType.Left)
|
|
{
|
|
verts.Add(v2);
|
|
verts.Add(v3);
|
|
verts.Add(v4);
|
|
verts.Add(v1);
|
|
}
|
|
else
|
|
{
|
|
verts.Add(v4);
|
|
verts.Add(v1);
|
|
verts.Add(v2);
|
|
verts.Add(v3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
verts.Add(v1);
|
|
verts.Add(v2);
|
|
verts.Add(v3);
|
|
verts.Add(v4);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Print the specified text into the buffers.
|
|
/// </summary>
|
|
|
|
static public void Print(string text, BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols, ChineseRotType chineseRotType)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
|
|
int indexOffset = verts.size;
|
|
Prepare(text);
|
|
|
|
// Start with the white tint
|
|
mColors.Add(Color.white);
|
|
mAlpha = 1f;
|
|
|
|
int ch = 0, prev = 0;
|
|
float x = 0f, y = 0f, maxX = 0f;
|
|
float sizeF = finalSize;
|
|
|
|
Color gb = tint * gradientBottom;
|
|
Color gt = tint * gradientTop;
|
|
Color32 uc = tint;
|
|
int textLength = text.Length;
|
|
|
|
Rect uvRect = new Rect();
|
|
float invX = 0f, invY = 0f;
|
|
float sizePD = sizeF * pixelDensity;
|
|
|
|
// Advanced symbol support contributed by Rudy Pangestu.
|
|
bool subscript = false;
|
|
int subscriptMode = 0; // 0 = normal, 1 = subscript, 2 = superscript
|
|
bool bold = false;
|
|
bool italic = false;
|
|
bool underline = false;
|
|
bool strikethrough = false;
|
|
bool ignoreColor = false;
|
|
const float sizeShrinkage = 0.75f;
|
|
|
|
float v0x;
|
|
float v1x;
|
|
float v1y;
|
|
float v0y;
|
|
float prevX = 0;
|
|
|
|
if (bitmapFont != null)
|
|
{
|
|
uvRect = bitmapFont.uvRect;
|
|
invX = uvRect.width / bitmapFont.texWidth;
|
|
invY = uvRect.height / bitmapFont.texHeight;
|
|
}
|
|
var isChinese = false;
|
|
if(chineseRotType != ChineseRotType.None)
|
|
{
|
|
for (int i = 0; i < textLength; ++i)
|
|
{
|
|
ch = text[i];
|
|
if (ch >= 0x4e00 && ch <= 0x9fbb)
|
|
{
|
|
isChinese = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(isChinese && chineseRotType == ChineseRotType.Right)
|
|
{
|
|
var newText = new StringBuilder(textLength);
|
|
for(int i = textLength - 1; i >= 0; --i)
|
|
{
|
|
newText.Append(text[i]);
|
|
}
|
|
text = newText.ToString();
|
|
}
|
|
|
|
for (int i = 0; i < textLength; ++i)
|
|
{
|
|
ch = text[i];
|
|
prevX = x;
|
|
|
|
// New line character -- skip to the next line
|
|
if (ch == '\n')
|
|
{
|
|
if (x > maxX) maxX = x;
|
|
|
|
if (alignment != Alignment.Left)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
|
|
// Invalid character -- skip it
|
|
if (ch < ' ')
|
|
{
|
|
prev = ch;
|
|
continue;
|
|
}
|
|
|
|
// Color changing symbol
|
|
if (encoding && ParseSymbol(text, ref i, mColors, premultiply, ref subscriptMode, ref bold,
|
|
ref italic, ref underline, ref strikethrough, ref ignoreColor))
|
|
{
|
|
Color fc;
|
|
|
|
if (ignoreColor)
|
|
{
|
|
fc = mColors[mColors.size - 1];
|
|
fc.a *= mAlpha * tint.a;
|
|
}
|
|
else
|
|
{
|
|
fc = tint * mColors[mColors.size - 1];
|
|
fc.a *= mAlpha;
|
|
}
|
|
uc = fc;
|
|
|
|
for (int b = 0, bmax = mColors.size - 2; b < bmax; ++b)
|
|
fc.a *= mColors[b].a;
|
|
|
|
if (gradient)
|
|
{
|
|
gb = gradientBottom * fc;
|
|
gt = gradientTop * fc;
|
|
}
|
|
--i;
|
|
continue;
|
|
}
|
|
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, i, textLength) : null;
|
|
|
|
if (symbol != null)
|
|
{
|
|
v0x = x + symbol.offsetX * fontScale;
|
|
v1x = v0x + symbol.width * fontScale;
|
|
v1y = -(y + symbol.offsetY * fontScale);
|
|
v0y = v1y - symbol.height * fontScale;
|
|
|
|
// Doesn't fit? Move down to the next line
|
|
if (Mathf.RoundToInt(x + symbol.advance * fontScale) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
v0x -= x;
|
|
v1x -= x;
|
|
v0y -= finalLineHeight;
|
|
v1y -= finalLineHeight;
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prevX = 0;
|
|
}
|
|
|
|
AddVerts(isChinese, chineseRotType, verts, new Vector3(v0x, v0y), new Vector3(v0x, v1y), new Vector3(v1x, v1y), new Vector3(v1x, v0y));
|
|
|
|
x += finalSpacingX + symbol.advance * fontScale;
|
|
i += symbol.length - 1;
|
|
prev = 0;
|
|
|
|
if (uvs != null)
|
|
{
|
|
Rect uv = symbol.uvRect;
|
|
|
|
float u0x = uv.xMin;
|
|
float u0y = uv.yMin;
|
|
float u1x = uv.xMax;
|
|
float u1y = uv.yMax;
|
|
|
|
uvs.Add(new Vector2(u0x, u0y));
|
|
uvs.Add(new Vector2(u0x, u1y));
|
|
uvs.Add(new Vector2(u1x, u1y));
|
|
uvs.Add(new Vector2(u1x, u0y));
|
|
}
|
|
|
|
if (cols != null)
|
|
{
|
|
if (symbolStyle == SymbolStyle.Colored)
|
|
{
|
|
for (int b = 0; b < 4; ++b) cols.Add(uc);
|
|
}
|
|
else
|
|
{
|
|
Color32 col = Color.white;
|
|
col.a = uc.a;
|
|
for (int b = 0; b < 4; ++b) cols.Add(col);
|
|
}
|
|
}
|
|
}
|
|
else // No symbol present
|
|
{
|
|
GlyphInfo glyph = GetGlyph(ch, prev);
|
|
if (glyph == null) continue;
|
|
prev = ch;
|
|
|
|
if (subscriptMode != 0)
|
|
{
|
|
glyph.v0.x *= sizeShrinkage;
|
|
glyph.v0.y *= sizeShrinkage;
|
|
glyph.v1.x *= sizeShrinkage;
|
|
glyph.v1.y *= sizeShrinkage;
|
|
|
|
if (subscriptMode == 1)
|
|
{
|
|
glyph.v0.y -= fontScale * fontSize * 0.4f;
|
|
glyph.v1.y -= fontScale * fontSize * 0.4f;
|
|
}
|
|
else
|
|
{
|
|
glyph.v0.y += fontScale * fontSize * 0.05f;
|
|
glyph.v1.y += fontScale * fontSize * 0.05f;
|
|
}
|
|
}
|
|
|
|
v0x = glyph.v0.x + x;
|
|
v0y = glyph.v0.y - y;
|
|
v1x = glyph.v1.x + x;
|
|
v1y = glyph.v1.y - y;
|
|
|
|
float w = glyph.advance;
|
|
if (finalSpacingX < 0f) w += finalSpacingX;
|
|
|
|
// Doesn't fit? Move down to the next line
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
v0x -= x;
|
|
v1x -= x;
|
|
v0y -= finalLineHeight;
|
|
v1y -= finalLineHeight;
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prevX = 0;
|
|
}
|
|
|
|
if (IsSpace(ch))
|
|
{
|
|
if (underline)
|
|
{
|
|
ch = '_';
|
|
}
|
|
else if (strikethrough)
|
|
{
|
|
ch = '-';
|
|
}
|
|
}
|
|
|
|
// Advance the position
|
|
x += (subscriptMode == 0) ? finalSpacingX + glyph.advance :
|
|
(finalSpacingX + glyph.advance) * sizeShrinkage;
|
|
|
|
// No need to continue if this is a space character
|
|
if (IsSpace(ch)) continue;
|
|
|
|
// Texture coordinates
|
|
if (uvs != null)
|
|
{
|
|
if (bitmapFont != null)
|
|
{
|
|
glyph.u0.x = uvRect.xMin + invX * glyph.u0.x;
|
|
glyph.u2.x = uvRect.xMin + invX * glyph.u2.x;
|
|
glyph.u0.y = uvRect.yMax - invY * glyph.u0.y;
|
|
glyph.u2.y = uvRect.yMax - invY * glyph.u2.y;
|
|
|
|
glyph.u1.x = glyph.u0.x;
|
|
glyph.u1.y = glyph.u2.y;
|
|
|
|
glyph.u3.x = glyph.u2.x;
|
|
glyph.u3.y = glyph.u0.y;
|
|
}
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
uvs.Add(glyph.u0);
|
|
uvs.Add(glyph.u1);
|
|
uvs.Add(glyph.u2);
|
|
uvs.Add(glyph.u3);
|
|
}
|
|
}
|
|
|
|
// Vertex colors
|
|
if (cols != null)
|
|
{
|
|
if (glyph.channel == 0 || glyph.channel == 15)
|
|
{
|
|
if (gradient)
|
|
{
|
|
float min = sizePD + glyph.v0.y / fontScale;
|
|
float max = sizePD + glyph.v1.y / fontScale;
|
|
|
|
min /= sizePD;
|
|
max /= sizePD;
|
|
|
|
s_c0 = Color.Lerp(gb, gt, min);
|
|
s_c1 = Color.Lerp(gb, gt, max);
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
cols.Add(s_c0);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
|
|
cols.Add(uc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Packed fonts come as alpha masks in each of the RGBA channels.
|
|
// In order to use it we need to use a special shader.
|
|
//
|
|
// Limitations:
|
|
// - Effects (drop shadow, outline) will not work.
|
|
// - Should not be a part of the atlas (eastern fonts rarely are anyway).
|
|
// - Lower color precision
|
|
|
|
Color col = uc;
|
|
|
|
col *= 0.49f;
|
|
|
|
switch (glyph.channel)
|
|
{
|
|
case 1: col.b += 0.51f; break;
|
|
case 2: col.g += 0.51f; break;
|
|
case 4: col.r += 0.51f; break;
|
|
case 8: col.a += 0.51f; break;
|
|
}
|
|
|
|
Color32 c = col;
|
|
for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
|
|
cols.Add(c);
|
|
}
|
|
}
|
|
|
|
// Bold and italic contributed by Rudy Pangestu.
|
|
if (!bold)
|
|
{
|
|
if (!italic)
|
|
{
|
|
AddVerts(isChinese, chineseRotType, verts, new Vector3(v0x, v0y), new Vector3(v0x, v1y), new Vector3(v1x, v1y), new Vector3(v1x, v0y));
|
|
}
|
|
else // Italic
|
|
{
|
|
float slant = fontSize * 0.1f * ((v1y - v0y) / fontSize);
|
|
AddVerts(isChinese, chineseRotType, verts, new Vector3(v0x - slant, v0y), new Vector3(v0x + slant, v1y), new Vector3(v1x + slant, v1y), new Vector3(v1x - slant, v0y));
|
|
}
|
|
}
|
|
else // Bold
|
|
{
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
float a = mBoldOffset[j * 2];
|
|
float b = mBoldOffset[j * 2 + 1];
|
|
|
|
float slant = (italic ? fontSize * 0.1f * ((v1y - v0y) / fontSize) : 0f);
|
|
AddVerts(isChinese, chineseRotType, verts, new Vector3(v0x + a - slant, v0y + b), new Vector3(v0x + a + slant, v1y + b), new Vector3(v1x + a + slant, v1y + b), new Vector3(v1x + a - slant, v0y + b));
|
|
}
|
|
}
|
|
|
|
// Underline and strike-through contributed by Rudy Pangestu.
|
|
if (underline || strikethrough)
|
|
{
|
|
GlyphInfo dash = GetGlyph(strikethrough ? '-' : '_', prev);
|
|
if (dash == null) continue;
|
|
|
|
if (uvs != null)
|
|
{
|
|
if (bitmapFont != null)
|
|
{
|
|
dash.u0.x = uvRect.xMin + invX * dash.u0.x;
|
|
dash.u2.x = uvRect.xMin + invX * dash.u2.x;
|
|
dash.u0.y = uvRect.yMax - invY * dash.u0.y;
|
|
dash.u2.y = uvRect.yMax - invY * dash.u2.y;
|
|
}
|
|
|
|
float cx = (dash.u0.x + dash.u2.x) * 0.5f;
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
uvs.Add(new Vector2(cx, dash.u0.y));
|
|
uvs.Add(new Vector2(cx, dash.u2.y));
|
|
uvs.Add(new Vector2(cx, dash.u2.y));
|
|
uvs.Add(new Vector2(cx, dash.u0.y));
|
|
}
|
|
}
|
|
|
|
if (subscript && strikethrough)
|
|
{
|
|
v0y = (-y + dash.v0.y) * sizeShrinkage;
|
|
v1y = (-y + dash.v1.y) * sizeShrinkage;
|
|
}
|
|
else
|
|
{
|
|
v0y = (-y + dash.v0.y);
|
|
v1y = (-y + dash.v1.y);
|
|
}
|
|
|
|
if (bold)
|
|
{
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
float a = mBoldOffset[j * 2];
|
|
float b = mBoldOffset[j * 2 + 1];
|
|
|
|
AddVerts(isChinese, chineseRotType, verts, new Vector3(prevX + a, v0y + b), new Vector3(prevX + a, v1y + b), new Vector3(x + a, v1y + b), new Vector3(x + a, v0y + b));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddVerts(isChinese, chineseRotType, verts, new Vector3(prevX, v0y), new Vector3(prevX, v1y), new Vector3(x, v1y), new Vector3(x, v0y));
|
|
}
|
|
|
|
if (gradient)
|
|
{
|
|
float min = sizePD + dash.v0.y / fontScale;
|
|
float max = sizePD + dash.v1.y / fontScale;
|
|
|
|
min /= sizePD;
|
|
max /= sizePD;
|
|
|
|
s_c0 = Color.Lerp(gb, gt, min);
|
|
s_c1 = Color.Lerp(gb, gt, max);
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
cols.Add(s_c0);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
|
|
cols.Add(uc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
mColors.Clear();
|
|
}
|
|
#else
|
|
/// <summary>
|
|
/// Print the specified text into the buffers.
|
|
/// </summary>
|
|
|
|
static public void Print(string text, BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
|
|
int indexOffset = verts.size;
|
|
Prepare(text);
|
|
|
|
// Start with the white tint
|
|
mColors.Add(Color.white);
|
|
mAlpha = 1f;
|
|
|
|
int ch = 0, prev = 0;
|
|
float x = 0f, y = 0f, maxX = 0f;
|
|
float sizeF = finalSize;
|
|
|
|
Color gb = tint * gradientBottom;
|
|
Color gt = tint * gradientTop;
|
|
Color32 uc = tint;
|
|
int textLength = text.Length;
|
|
|
|
Rect uvRect = new Rect();
|
|
float invX = 0f, invY = 0f;
|
|
float sizePD = sizeF * pixelDensity;
|
|
|
|
// Advanced symbol support contributed by Rudy Pangestu.
|
|
bool subscript = false;
|
|
int subscriptMode = 0; // 0 = normal, 1 = subscript, 2 = superscript
|
|
bool bold = false;
|
|
bool italic = false;
|
|
bool underline = false;
|
|
bool strikethrough = false;
|
|
bool ignoreColor = false;
|
|
const float sizeShrinkage = 0.75f;
|
|
|
|
float v0x;
|
|
float v1x;
|
|
float v1y;
|
|
float v0y;
|
|
float prevX = 0;
|
|
|
|
if (bitmapFont != null)
|
|
{
|
|
uvRect = bitmapFont.uvRect;
|
|
invX = uvRect.width / bitmapFont.texWidth;
|
|
invY = uvRect.height / bitmapFont.texHeight;
|
|
}
|
|
|
|
for (int i = 0; i < textLength; ++i)
|
|
{
|
|
ch = text[i];
|
|
|
|
prevX = x;
|
|
|
|
// New line character -- skip to the next line
|
|
if (ch == '\n')
|
|
{
|
|
if (x > maxX) maxX = x;
|
|
|
|
if (alignment != Alignment.Left)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
|
|
// Invalid character -- skip it
|
|
if (ch < ' ')
|
|
{
|
|
prev = ch;
|
|
continue;
|
|
}
|
|
|
|
// Color changing symbol
|
|
if (encoding && ParseSymbol(text, ref i, mColors, premultiply, ref subscriptMode, ref bold,
|
|
ref italic, ref underline, ref strikethrough, ref ignoreColor))
|
|
{
|
|
Color fc;
|
|
|
|
if (ignoreColor)
|
|
{
|
|
fc = mColors[mColors.size - 1];
|
|
fc.a *= mAlpha * tint.a;
|
|
}
|
|
else
|
|
{
|
|
fc = tint * mColors[mColors.size - 1];
|
|
fc.a *= mAlpha;
|
|
}
|
|
uc = fc;
|
|
|
|
for (int b = 0, bmax = mColors.size - 2; b < bmax; ++b)
|
|
fc.a *= mColors[b].a;
|
|
|
|
if (gradient)
|
|
{
|
|
gb = gradientBottom * fc;
|
|
gt = gradientTop * fc;
|
|
}
|
|
--i;
|
|
continue;
|
|
}
|
|
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, i, textLength) : null;
|
|
|
|
if (symbol != null)
|
|
{
|
|
v0x = x + symbol.offsetX * fontScale;
|
|
v1x = v0x + symbol.width * fontScale;
|
|
v1y = -(y + symbol.offsetY * fontScale);
|
|
v0y = v1y - symbol.height * fontScale;
|
|
|
|
// Doesn't fit? Move down to the next line
|
|
if (Mathf.RoundToInt(x + symbol.advance * fontScale) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
v0x -= x;
|
|
v1x -= x;
|
|
v0y -= finalLineHeight;
|
|
v1y -= finalLineHeight;
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prevX = 0;
|
|
}
|
|
|
|
verts.Add(new Vector3(v0x, v0y));
|
|
verts.Add(new Vector3(v0x, v1y));
|
|
verts.Add(new Vector3(v1x, v1y));
|
|
verts.Add(new Vector3(v1x, v0y));
|
|
|
|
x += finalSpacingX + symbol.advance * fontScale;
|
|
i += symbol.length - 1;
|
|
prev = 0;
|
|
|
|
if (uvs != null)
|
|
{
|
|
Rect uv = symbol.uvRect;
|
|
|
|
float u0x = uv.xMin;
|
|
float u0y = uv.yMin;
|
|
float u1x = uv.xMax;
|
|
float u1y = uv.yMax;
|
|
|
|
uvs.Add(new Vector2(u0x, u0y));
|
|
uvs.Add(new Vector2(u0x, u1y));
|
|
uvs.Add(new Vector2(u1x, u1y));
|
|
uvs.Add(new Vector2(u1x, u0y));
|
|
}
|
|
|
|
if (cols != null)
|
|
{
|
|
if (symbolStyle == SymbolStyle.Colored)
|
|
{
|
|
for (int b = 0; b < 4; ++b) cols.Add(uc);
|
|
}
|
|
else
|
|
{
|
|
Color32 col = Color.white;
|
|
col.a = uc.a;
|
|
for (int b = 0; b < 4; ++b) cols.Add(col);
|
|
}
|
|
}
|
|
}
|
|
else // No symbol present
|
|
{
|
|
GlyphInfo glyph = GetGlyph(ch, prev);
|
|
if (glyph == null) continue;
|
|
prev = ch;
|
|
|
|
if (subscriptMode != 0)
|
|
{
|
|
glyph.v0.x *= sizeShrinkage;
|
|
glyph.v0.y *= sizeShrinkage;
|
|
glyph.v1.x *= sizeShrinkage;
|
|
glyph.v1.y *= sizeShrinkage;
|
|
|
|
if (subscriptMode == 1)
|
|
{
|
|
glyph.v0.y -= fontScale * fontSize * 0.4f;
|
|
glyph.v1.y -= fontScale * fontSize * 0.4f;
|
|
}
|
|
else
|
|
{
|
|
glyph.v0.y += fontScale * fontSize * 0.05f;
|
|
glyph.v1.y += fontScale * fontSize * 0.05f;
|
|
}
|
|
}
|
|
|
|
v0x = glyph.v0.x + x;
|
|
v0y = glyph.v0.y - y;
|
|
v1x = glyph.v1.x + x;
|
|
v1y = glyph.v1.y - y;
|
|
|
|
float w = glyph.advance;
|
|
if (finalSpacingX < 0f) w += finalSpacingX;
|
|
|
|
// Doesn't fit? Move down to the next line
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
v0x -= x;
|
|
v1x -= x;
|
|
v0y -= finalLineHeight;
|
|
v1y -= finalLineHeight;
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prevX = 0;
|
|
}
|
|
|
|
if (IsSpace(ch))
|
|
{
|
|
if (underline)
|
|
{
|
|
ch = '_';
|
|
}
|
|
else if (strikethrough)
|
|
{
|
|
ch = '-';
|
|
}
|
|
}
|
|
|
|
// Advance the position
|
|
x += (subscriptMode == 0) ? finalSpacingX + glyph.advance :
|
|
(finalSpacingX + glyph.advance) * sizeShrinkage;
|
|
|
|
// No need to continue if this is a space character
|
|
if (IsSpace(ch)) continue;
|
|
|
|
// Texture coordinates
|
|
if (uvs != null)
|
|
{
|
|
if (bitmapFont != null)
|
|
{
|
|
glyph.u0.x = uvRect.xMin + invX * glyph.u0.x;
|
|
glyph.u2.x = uvRect.xMin + invX * glyph.u2.x;
|
|
glyph.u0.y = uvRect.yMax - invY * glyph.u0.y;
|
|
glyph.u2.y = uvRect.yMax - invY * glyph.u2.y;
|
|
|
|
glyph.u1.x = glyph.u0.x;
|
|
glyph.u1.y = glyph.u2.y;
|
|
|
|
glyph.u3.x = glyph.u2.x;
|
|
glyph.u3.y = glyph.u0.y;
|
|
}
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
uvs.Add(glyph.u0);
|
|
uvs.Add(glyph.u1);
|
|
uvs.Add(glyph.u2);
|
|
uvs.Add(glyph.u3);
|
|
}
|
|
}
|
|
|
|
// Vertex colors
|
|
if (cols != null)
|
|
{
|
|
if (glyph.channel == 0 || glyph.channel == 15)
|
|
{
|
|
if (gradient)
|
|
{
|
|
float min = sizePD + glyph.v0.y / fontScale;
|
|
float max = sizePD + glyph.v1.y / fontScale;
|
|
|
|
min /= sizePD;
|
|
max /= sizePD;
|
|
|
|
s_c0 = Color.Lerp(gb, gt, min);
|
|
s_c1 = Color.Lerp(gb, gt, max);
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
cols.Add(s_c0);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
|
|
cols.Add(uc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Packed fonts come as alpha masks in each of the RGBA channels.
|
|
// In order to use it we need to use a special shader.
|
|
//
|
|
// Limitations:
|
|
// - Effects (drop shadow, outline) will not work.
|
|
// - Should not be a part of the atlas (eastern fonts rarely are anyway).
|
|
// - Lower color precision
|
|
|
|
Color col = uc;
|
|
|
|
col *= 0.49f;
|
|
|
|
switch (glyph.channel)
|
|
{
|
|
case 1: col.b += 0.51f; break;
|
|
case 2: col.g += 0.51f; break;
|
|
case 4: col.r += 0.51f; break;
|
|
case 8: col.a += 0.51f; break;
|
|
}
|
|
|
|
Color32 c = col;
|
|
for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
|
|
cols.Add(c);
|
|
}
|
|
}
|
|
|
|
// Bold and italic contributed by Rudy Pangestu.
|
|
if (!bold)
|
|
{
|
|
if (!italic)
|
|
{
|
|
verts.Add(new Vector3(v0x, v0y));
|
|
verts.Add(new Vector3(v0x, v1y));
|
|
verts.Add(new Vector3(v1x, v1y));
|
|
verts.Add(new Vector3(v1x, v0y));
|
|
}
|
|
else // Italic
|
|
{
|
|
float slant = fontSize * 0.1f * ((v1y - v0y) / fontSize);
|
|
verts.Add(new Vector3(v0x - slant, v0y));
|
|
verts.Add(new Vector3(v0x + slant, v1y));
|
|
verts.Add(new Vector3(v1x + slant, v1y));
|
|
verts.Add(new Vector3(v1x - slant, v0y));
|
|
}
|
|
}
|
|
else // Bold
|
|
{
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
float a = mBoldOffset[j * 2];
|
|
float b = mBoldOffset[j * 2 + 1];
|
|
|
|
float slant = (italic ? fontSize * 0.1f * ((v1y - v0y) / fontSize) : 0f);
|
|
verts.Add(new Vector3(v0x + a - slant, v0y + b));
|
|
verts.Add(new Vector3(v0x + a + slant, v1y + b));
|
|
verts.Add(new Vector3(v1x + a + slant, v1y + b));
|
|
verts.Add(new Vector3(v1x + a - slant, v0y + b));
|
|
}
|
|
}
|
|
|
|
// Underline and strike-through contributed by Rudy Pangestu.
|
|
if (underline || strikethrough)
|
|
{
|
|
GlyphInfo dash = GetGlyph(strikethrough ? '-' : '_', prev);
|
|
if (dash == null) continue;
|
|
|
|
if (uvs != null)
|
|
{
|
|
if (bitmapFont != null)
|
|
{
|
|
dash.u0.x = uvRect.xMin + invX * dash.u0.x;
|
|
dash.u2.x = uvRect.xMin + invX * dash.u2.x;
|
|
dash.u0.y = uvRect.yMax - invY * dash.u0.y;
|
|
dash.u2.y = uvRect.yMax - invY * dash.u2.y;
|
|
}
|
|
|
|
float cx = (dash.u0.x + dash.u2.x) * 0.5f;
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
uvs.Add(new Vector2(cx, dash.u0.y));
|
|
uvs.Add(new Vector2(cx, dash.u2.y));
|
|
uvs.Add(new Vector2(cx, dash.u2.y));
|
|
uvs.Add(new Vector2(cx, dash.u0.y));
|
|
}
|
|
}
|
|
|
|
if (subscript && strikethrough)
|
|
{
|
|
v0y = (-y + dash.v0.y) * sizeShrinkage;
|
|
v1y = (-y + dash.v1.y) * sizeShrinkage;
|
|
}
|
|
else
|
|
{
|
|
v0y = (-y + dash.v0.y);
|
|
v1y = (-y + dash.v1.y);
|
|
}
|
|
|
|
if (bold)
|
|
{
|
|
for (int j = 0; j < 4; ++j)
|
|
{
|
|
float a = mBoldOffset[j * 2];
|
|
float b = mBoldOffset[j * 2 + 1];
|
|
|
|
verts.Add(new Vector3(prevX + a, v0y + b));
|
|
verts.Add(new Vector3(prevX + a, v1y + b));
|
|
verts.Add(new Vector3(x + a, v1y + b));
|
|
verts.Add(new Vector3(x + a, v0y + b));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
verts.Add(new Vector3(prevX, v0y));
|
|
verts.Add(new Vector3(prevX, v1y));
|
|
verts.Add(new Vector3(x, v1y));
|
|
verts.Add(new Vector3(x, v0y));
|
|
}
|
|
|
|
if (gradient)
|
|
{
|
|
float min = sizePD + dash.v0.y / fontScale;
|
|
float max = sizePD + dash.v1.y / fontScale;
|
|
|
|
min /= sizePD;
|
|
max /= sizePD;
|
|
|
|
s_c0 = Color.Lerp(gb, gt, min);
|
|
s_c1 = Color.Lerp(gb, gt, max);
|
|
|
|
for (int j = 0, jmax = (bold ? 4 : 1); j < jmax; ++j)
|
|
{
|
|
cols.Add(s_c0);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c1);
|
|
cols.Add(s_c0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int j = 0, jmax = (bold ? 16 : 4); j < jmax; ++j)
|
|
cols.Add(uc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX);
|
|
indexOffset = verts.size;
|
|
}
|
|
mColors.Clear();
|
|
}
|
|
#endif
|
|
|
|
static float[] mBoldOffset = new float[]
|
|
{
|
|
-0.25f, 0f, 0.25f, 0f,
|
|
0f, -0.25f, 0f, 0.25f
|
|
};
|
|
|
|
/// <summary>
|
|
/// Print character positions and indices into the specified buffer. Meant to be used with the "find closest vertex" calculations.
|
|
/// </summary>
|
|
|
|
static public void PrintApproximateCharacterPositions(string text, BetterList<Vector3> verts, BetterList<int> indices)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) text = " ";
|
|
|
|
Prepare(text);
|
|
|
|
float x = 0f, y = 0f, maxX = 0f, halfSize = fontSize * fontScale * 0.5f;
|
|
int textLength = text.Length, indexOffset = verts.size, ch = 0, prev = 0;
|
|
|
|
for (int i = 0; i < textLength; ++i)
|
|
{
|
|
ch = text[i];
|
|
|
|
verts.Add(new Vector3(x, -y - halfSize));
|
|
indices.Add(i);
|
|
|
|
if (ch == '\n')
|
|
{
|
|
if (x > maxX) maxX = x;
|
|
|
|
if (alignment != Alignment.Left)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX, 1);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
else if (ch < ' ')
|
|
{
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
|
|
if (encoding && ParseSymbol(text, ref i))
|
|
{
|
|
--i;
|
|
continue;
|
|
}
|
|
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, i, textLength) : null;
|
|
|
|
if (symbol == null)
|
|
{
|
|
float w = GetGlyphWidth(ch, prev);
|
|
|
|
if (w != 0f)
|
|
{
|
|
w += finalSpacingX;
|
|
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX, 1);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = w;
|
|
y += finalLineHeight;
|
|
}
|
|
else x += w;
|
|
|
|
verts.Add(new Vector3(x, -y - halfSize));
|
|
indices.Add(i + 1);
|
|
prev = ch;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float w = symbol.advance * fontScale + finalSpacingX;
|
|
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX, 1);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = w;
|
|
y += finalLineHeight;
|
|
}
|
|
else x += w;
|
|
|
|
verts.Add(new Vector3(x, -y - halfSize));
|
|
indices.Add(i + 1);
|
|
i += symbol.sequence.Length - 1;
|
|
prev = 0;
|
|
}
|
|
}
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
Align(verts, indexOffset, x - finalSpacingX, 1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Print character positions and indices into the specified buffer.
|
|
/// This function's data is meant to be used for precise character selection, such as clicking on a link.
|
|
/// There are 2 vertices for every index: Bottom Left + Top Right.
|
|
/// </summary>
|
|
|
|
static public void PrintExactCharacterPositions(string text, BetterList<Vector3> verts, BetterList<int> indices)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) text = " ";
|
|
|
|
Prepare(text);
|
|
|
|
float fullSize = fontSize * fontScale;
|
|
float x = 0f, y = 0f, maxX = 0f;
|
|
int textLength = text.Length, indexOffset = verts.size, ch = 0, prev = 0;
|
|
|
|
for (int i = 0; i < textLength; ++i)
|
|
{
|
|
ch = text[i];
|
|
|
|
if (ch == '\n')
|
|
{
|
|
if (x > maxX) maxX = x;
|
|
|
|
if (alignment != Alignment.Left)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX, 2);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
else if (ch < ' ')
|
|
{
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
|
|
if (encoding && ParseSymbol(text, ref i))
|
|
{
|
|
--i;
|
|
continue;
|
|
}
|
|
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, i, textLength) : null;
|
|
|
|
if (symbol == null)
|
|
{
|
|
float gw = GetGlyphWidth(ch, prev);
|
|
|
|
if (gw != 0f)
|
|
{
|
|
float w = gw + finalSpacingX;
|
|
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX, 2);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = 0f;
|
|
y += finalLineHeight;
|
|
prev = 0;
|
|
--i;
|
|
continue;
|
|
}
|
|
|
|
indices.Add(i);
|
|
verts.Add(new Vector3(x, -y - fullSize));
|
|
verts.Add(new Vector3(x + w, -y));
|
|
prev = ch;
|
|
x += w;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float w = symbol.advance * fontScale + finalSpacingX;
|
|
|
|
if (Mathf.RoundToInt(x + w) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
{
|
|
Align(verts, indexOffset, x - finalSpacingX, 2);
|
|
indexOffset = verts.size;
|
|
}
|
|
|
|
x = 0f;
|
|
y += finalLineHeight;
|
|
prev = 0;
|
|
--i;
|
|
continue;
|
|
}
|
|
|
|
indices.Add(i);
|
|
verts.Add(new Vector3(x, -y - fullSize));
|
|
verts.Add(new Vector3(x + w, -y));
|
|
i += symbol.sequence.Length - 1;
|
|
x += w;
|
|
prev = 0;
|
|
}
|
|
}
|
|
|
|
if (alignment != Alignment.Left && indexOffset < verts.size)
|
|
Align(verts, indexOffset, x - finalSpacingX, 2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Print the caret and selection vertices. Note that it's expected that 'text' has been stripped clean of symbols.
|
|
/// </summary>
|
|
|
|
static public void PrintCaretAndSelection(string text, int start, int end, BetterList<Vector3> caret, BetterList<Vector3> highlight)
|
|
{
|
|
if (string.IsNullOrEmpty(text)) text = " ";
|
|
|
|
Prepare(text);
|
|
|
|
int caretPos = end;
|
|
|
|
if (start > end)
|
|
{
|
|
end = start;
|
|
start = caretPos;
|
|
}
|
|
|
|
float x = 0f, y = 0f, maxX = 0f, fs = fontSize * fontScale;
|
|
int caretOffset = (caret != null) ? caret.size : 0;
|
|
int highlightOffset = (highlight != null) ? highlight.size : 0;
|
|
int textLength = text.Length, index = 0, ch = 0, prev = 0;
|
|
bool highlighting = false, caretSet = false;
|
|
|
|
Vector2 last0 = Vector2.zero;
|
|
Vector2 last1 = Vector2.zero;
|
|
|
|
for (; index < textLength; ++index)
|
|
{
|
|
// Print the caret
|
|
if (caret != null && !caretSet && caretPos <= index)
|
|
{
|
|
caretSet = true;
|
|
caret.Add(new Vector3(x - 1f, -y - fs));
|
|
caret.Add(new Vector3(x - 1f, -y));
|
|
caret.Add(new Vector3(x + 1f, -y));
|
|
caret.Add(new Vector3(x + 1f, -y - fs));
|
|
}
|
|
|
|
ch = text[index];
|
|
|
|
if (ch == '\n')
|
|
{
|
|
// Used for alignment purposes
|
|
if (x > maxX) maxX = x;
|
|
|
|
// Align the caret
|
|
if (caret != null && caretSet)
|
|
{
|
|
if (alignment != Alignment.Left) Align(caret, caretOffset, x - finalSpacingX);
|
|
caret = null;
|
|
}
|
|
|
|
if (highlight != null)
|
|
{
|
|
if (highlighting)
|
|
{
|
|
// Close the selection on this line
|
|
highlighting = false;
|
|
highlight.Add(last1);
|
|
highlight.Add(last0);
|
|
}
|
|
else if (start <= index && end > index)
|
|
{
|
|
// This must be an empty line. Add a narrow vertical highlight.
|
|
highlight.Add(new Vector3(x, -y - fs));
|
|
highlight.Add(new Vector3(x, -y));
|
|
highlight.Add(new Vector3(x + 2f, -y));
|
|
highlight.Add(new Vector3(x + 2f, -y - fs));
|
|
}
|
|
|
|
// Align the highlight
|
|
if (alignment != Alignment.Left && highlightOffset < highlight.size)
|
|
{
|
|
Align(highlight, highlightOffset, x - finalSpacingX);
|
|
highlightOffset = highlight.size;
|
|
}
|
|
}
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
else if (ch < ' ')
|
|
{
|
|
prev = 0;
|
|
continue;
|
|
}
|
|
|
|
if (encoding && ParseSymbol(text, ref index))
|
|
{
|
|
--index;
|
|
continue;
|
|
}
|
|
|
|
// See if there is a symbol matching this text
|
|
BMSymbol symbol = useSymbols ? GetSymbol(text, index, textLength) : null;
|
|
float w = (symbol != null) ? symbol.advance * fontScale : GetGlyphWidth(ch, prev);
|
|
|
|
if (w != 0f)
|
|
{
|
|
float v0x = x;
|
|
float v1x = x + w;
|
|
float v0y = -y - fs;
|
|
float v1y = -y;
|
|
|
|
if (Mathf.RoundToInt(v1x + finalSpacingX) > regionWidth)
|
|
{
|
|
if (x == 0f) return;
|
|
|
|
// Used for alignment purposes
|
|
if (x > maxX) maxX = x;
|
|
|
|
// Align the caret
|
|
if (caret != null && caretSet)
|
|
{
|
|
if (alignment != Alignment.Left) Align(caret, caretOffset, x - finalSpacingX);
|
|
caret = null;
|
|
}
|
|
|
|
if (highlight != null)
|
|
{
|
|
if (highlighting)
|
|
{
|
|
// Close the selection on this line
|
|
highlighting = false;
|
|
highlight.Add(last1);
|
|
highlight.Add(last0);
|
|
}
|
|
else if (start <= index && end > index)
|
|
{
|
|
// This must be an empty line. Add a narrow vertical highlight.
|
|
highlight.Add(new Vector3(x, -y - fs));
|
|
highlight.Add(new Vector3(x, -y));
|
|
highlight.Add(new Vector3(x + 2f, -y));
|
|
highlight.Add(new Vector3(x + 2f, -y - fs));
|
|
}
|
|
|
|
// Align the highlight
|
|
if (alignment != Alignment.Left && highlightOffset < highlight.size)
|
|
{
|
|
Align(highlight, highlightOffset, x - finalSpacingX);
|
|
highlightOffset = highlight.size;
|
|
}
|
|
}
|
|
|
|
v0x -= x;
|
|
v1x -= x;
|
|
v0y -= finalLineHeight;
|
|
v1y -= finalLineHeight;
|
|
|
|
x = 0;
|
|
y += finalLineHeight;
|
|
}
|
|
|
|
x += w + finalSpacingX;
|
|
|
|
// Print the highlight
|
|
if (highlight != null)
|
|
{
|
|
if (start > index || end <= index)
|
|
{
|
|
if (highlighting)
|
|
{
|
|
// Finish the highlight
|
|
highlighting = false;
|
|
highlight.Add(last1);
|
|
highlight.Add(last0);
|
|
}
|
|
}
|
|
else if (!highlighting)
|
|
{
|
|
// Start the highlight
|
|
highlighting = true;
|
|
highlight.Add(new Vector3(v0x, v0y));
|
|
highlight.Add(new Vector3(v0x, v1y));
|
|
}
|
|
}
|
|
|
|
// Save what the character ended with
|
|
last0 = new Vector2(v1x, v0y);
|
|
last1 = new Vector2(v1x, v1y);
|
|
prev = ch;
|
|
}
|
|
}
|
|
|
|
// Ensure we always have a caret
|
|
if (caret != null)
|
|
{
|
|
if (!caretSet)
|
|
{
|
|
caret.Add(new Vector3(x - 1f, -y - fs));
|
|
caret.Add(new Vector3(x - 1f, -y));
|
|
caret.Add(new Vector3(x + 1f, -y));
|
|
caret.Add(new Vector3(x + 1f, -y - fs));
|
|
}
|
|
|
|
if (alignment != Alignment.Left)
|
|
Align(caret, caretOffset, x - finalSpacingX);
|
|
}
|
|
|
|
// Close the selection
|
|
if (highlight != null)
|
|
{
|
|
if (highlighting)
|
|
{
|
|
// Finish the highlight
|
|
highlight.Add(last1);
|
|
highlight.Add(last0);
|
|
}
|
|
else if (start < index && end == index)
|
|
{
|
|
// Happens when highlight ends on an empty line. Highlight it with a thin line.
|
|
highlight.Add(new Vector3(x, -y - fs));
|
|
highlight.Add(new Vector3(x, -y));
|
|
highlight.Add(new Vector3(x + 2f, -y));
|
|
highlight.Add(new Vector3(x + 2f, -y - fs));
|
|
}
|
|
|
|
// Align the highlight
|
|
if (alignment != Alignment.Left && highlightOffset < highlight.size)
|
|
Align(highlight, highlightOffset, x - finalSpacingX);
|
|
}
|
|
}
|
|
}
|