[Unity3D学习]NGUI UILabel 图文混排扩展

转载:http://blog.gamerisker.com/archives/530.html
之前发过一个图文混排的文章 《[Unity3D学习]NGUI UILabel 图文混排》
一直感觉那样比较消耗。又由于公司项目也需要做图文混排,就想了另一种图文混排思路,从性能来说比之前的思路要好很多,不过也有弊端,现在这种解决方案,做动态表情不太适应。不过我想的是,手机游戏对聊天功能其实已经比较弱化了,所以个人认为聊天不需要做得太复杂,一切从简就好。

说说这个版本的图文混排的思路吧:

创建了两个UILabel 一个是动态字体、一个是图集字体,一个用来显示文本内容,另一个就用来显示表情,再通过修改表情Label的顶点坐标来修改每个表情的位置,达到图文混排的效果。

这个思路我觉得还是合理的,不过有些地方我不满意,我在NGUI UILabel 的OnFill方法(1519行)中添加了一个回调,来修改最终的渲染坐标,我个人不喜欢去修改原作者的代码。不愿意去破坏原来的结构,这是我不满意的地方。

其实我最开始的想法是将文本和表情放在同一个mesh上,那样就能自己独立写一个CustomLabel。这样的性能应该是最好的。

不过总的来说,现在的这个方案,是用最少的代码实现了动态字体的图文混排。(这只是给出了实现的基本原理,其中还有很多的不足,需要自己去打磨!仔细看代码,相信你会对NGUI有一个很深程度的理解的。)

说了那么多,先看看 Demo :  http://game.gamerisker.com/symbol

看完Demo就来看代码吧

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// 
/// 主要用来串联一下两个类 实现DEMO功能
/// Author : YangDan
/// Site : blog.gamerisker.com
/// 
public class Main : MonoBehaviour
{
    public SymbolInput input;

    public SymbolLabel label;

    public List list = new List();

	// Use this for initialization
	void Start ()
    {
        foreach (UISprite item in list)
        {
            UIEventListener.Get(item.gameObject).onClick = OnClick;
        }
	}

    private void OnClick(GameObject go)
    {
        input.value += go.name;
    }
	
	// Update is called once per frame
	void Update () {

        if (Input.GetKeyDown(KeyCode.Return)||Input.GetKeyDown(KeyCode.KeypadEnter))
        {
            label.text = input.value;
            input.value = "";
        }
	}

}

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;

/// 
/// 支持动态字体的Label组件
/// 
public class SymbolLabel : MonoBehaviour
{
    /// 
    /// 表情转移字符定义
    /// 
    private List m_Symbols = new List { "{00}", "{01}", "{02}", "{03}", "{04}", "{05}"
                                            , "{06}", "{07}", "{08}", "{09}", "{10}"};
    
    private string m_Text;
    private string m_realText;

    public UIFont uifont;
    public Font font;
    public int fontSize = 26;
    public int symbolSize = 26;
    public int spacingY = 0;
    public int width = 100;
    public int depth = 0;
    public int maxLine = 0;
    public UILabel.Overflow overflowMethod = UILabel.Overflow.ResizeHeight;

    private UILabel m_TextLabel;
    private UILabel m_SymbolLabel;

    private MatchCollection m_matchs;
    private MatchCollection m_spaceMatchs;
    private List m_realMatchs;

    private int m_DrawStart = 0;

    void Awake()
    {
        m_realMatchs = new List();

        m_TextLabel = NGUITools.AddChild(gameObject);
        m_TextLabel.name = "textLabel";
        m_TextLabel.trueTypeFont = font;
        m_TextLabel.spacingY = spacingY;
        m_TextLabel.fontSize = fontSize;
        m_TextLabel.overflowMethod = overflowMethod;
        m_TextLabel.alignment = NGUIText.Alignment.Left;
        m_TextLabel.pivot = UIWidget.Pivot.TopLeft;
        m_TextLabel.width = width;
        m_TextLabel.depth = depth;
        m_TextLabel.transform.localPosition = Vector3.zero;
        if (overflowMethod == UILabel.Overflow.ClampContent)
        {
            m_TextLabel.height = fontSize;
            m_TextLabel.maxLineCount = maxLine;
        }

        m_SymbolLabel = NGUITools.AddChild(gameObject);
        m_SymbolLabel.name = "symbolLabel";
        m_SymbolLabel.bitmapFont = uifont;
        m_SymbolLabel.fontSize = symbolSize;
        m_SymbolLabel.overflowMethod = UILabel.Overflow.ShrinkContent;
        m_SymbolLabel.alignment = NGUIText.Alignment.Left;
        m_SymbolLabel.pivot = UIWidget.Pivot.TopLeft;
        m_SymbolLabel.depth = depth + 1;
        m_SymbolLabel.transform.localPosition = Vector3.zero;
        m_SymbolLabel.SetSymbolOffset(SymbolOffset);
    }

    public int height
    {
        get
        {
            return m_TextLabel.height;
        }
    }

    public UILabel labelText
    {
        get
        {
            return m_TextLabel;
        }
    }

    public UILabel labelSymbol
    {
        get
        {
            return m_SymbolLabel;
        }
    }

    public string text 
    {
        get { return m_Text; }

        set
        {
            if(string.IsNullOrEmpty(value))
            {
                m_Text = "";
                m_TextLabel.text = null;
                m_SymbolLabel.text = null;
                m_realMatchs.Clear();
                return;
            }
            
            m_realMatchs.Clear();

            m_Text = value;

            string mProcessedText = m_TextLabel.processedText;

            if (overflowMethod == UILabel.Overflow.ResizeHeight) mProcessedText = m_Text;
            else NGUIText.WrapText(m_Text, out mProcessedText);

            StringBuilder sString = new StringBuilder();
            string t = value;
            const string pattern = "\\{\\w\\w\\}";

            m_realText = NGUIText.StripSymbols(mProcessedText);
            m_matchs = Regex.Matches(m_realText, pattern);

            const string pat = " ";
            m_spaceMatchs = Regex.Matches(mProcessedText, pat);

            if (m_matchs.Count > 0)
            {
                Match item;
                for (int i = 0; i < m_matchs.Count; i++)
                {
                    item = m_matchs[i];

                    if (m_Symbols.IndexOf(item.Value) > -1)
                    {
                        m_realMatchs.Add(item);
                        sString.Append(item.Value);
                    }
                }
            }

            m_TextLabel.text = t;
            m_SymbolLabel.text = sString.ToString();

            m_SymbolLabel.width = m_TextLabel.width;
            m_SymbolLabel.height = m_TextLabel.height;

            m_SymbolLabel.MarkAsChanged();
        }
    }

    /// 
    /// 修改顶点坐标 适配表情位置
    /// 1 — 2
    /// |  / |
    /// 0 — 3
    /// 
    private void SymbolOffset()
    {
        BetterList textVerts = m_TextLabel.geometry.verts;
        BetterList symbolVerts = m_SymbolLabel.geometry.verts;
        Vector3 spacing = new Vector3(0,0);

        if (textVerts.size > 0 && symbolVerts.size > 0)
        {
            Match item;
            float tw, sw, x = 0;
            int end, start;

            for (int i = 0; i < m_realMatchs.Count; i++)
            {
                item = m_realMatchs[i];

                //获取表情转移字符顶点开始、结束索引
                start = GetIndex(item.Index) * 4;
                end = start + (item.Length - 1) * 4 + 3;

                //表情都顶点索引
                int p = i * 4;  

                //表情宽度
                sw = Mathf.Abs(symbolVerts.buffer[p].x - symbolVerts.buffer[p + 3].x);

                //如果不换行,计算文本表情转移符都宽带 否则换行不需要计算 添加1个单位距离 跟在后面
                if (textVerts.buffer[start].y == textVerts.buffer[end].y)
                {
                    //文本表情转义符宽度
                    tw = Mathf.Abs(textVerts.buffer[start].x - textVerts.buffer[end].x);

                    //计算居中坐标
                    x = (tw - sw) / 2;
                }
                else x = 1;              

                //居中显示表情
                spacing.x = x;

                //计算偏移
                Vector2 po = m_TextLabel.pivotOffset;
                float fx = Mathf.Lerp(0f, -NGUIText.rectWidth, po.x);
                float fy = Mathf.Lerp(NGUIText.rectHeight, 0f, po.y) + Mathf.Lerp((m_TextLabel.printedSize.y - NGUIText.rectHeight), 0f, po.y);
                fx = Mathf.Round(fx);
                fy = Mathf.Round(fy);

                //计算出位移向量   
                Vector3 v = textVerts.buffer[start] - symbolVerts.buffer[p];   

                //第一个顶点
                symbolVerts.buffer[p] = textVerts.buffer[start] + spacing;
                symbolVerts.buffer[p].x -= fx;
                symbolVerts.buffer[p++].y -= fy;

                //第二个顶点
                symbolVerts.buffer[p] = symbolVerts.buffer[p] + v + spacing;
                symbolVerts.buffer[p].x -= fx;
                symbolVerts.buffer[p++].y -= fy;

                //第三个顶点
                symbolVerts.buffer[p] = symbolVerts.buffer[p] + v + spacing;
                symbolVerts.buffer[p].x -= fx;
                symbolVerts.buffer[p++].y -= fy;

                //第四个顶点
                symbolVerts.buffer[p] = symbolVerts.buffer[p] + v + spacing;
                symbolVerts.buffer[p].x -= fx;
                symbolVerts.buffer[p++].y -= fy;

                for (int j = 0; j < item.Length; j++)
                {
                    //本来是希望将顶点坐标抹除、但是由于会出现坐标不对都情况、所以放弃了该方法,将顶点都颜色清除掉。
                    //textVerts.buffer[start++] = Vector3.zero;
                    //textVerts.buffer[start++] = Vector3.zero;
                    //textVerts.buffer[start++] = Vector3.zero;
                    //textVerts.buffer[start++] = Vector3.zero;

                    if (m_TextLabel.geometry.cols.size >= (start + 4))
                    {
                        m_TextLabel.geometry.cols[start++] = Color.clear;
                        m_TextLabel.geometry.cols[start++] = Color.clear;
                        m_TextLabel.geometry.cols[start++] = Color.clear;
                        m_TextLabel.geometry.cols[start++] = Color.clear;
                    }
                }
            }
        }
    }

    /// 
    /// 获取表情转移字符'{'顶点索引,并且需要排除空格符的部分,因为空格符UILabel是不会生成顶点的 所以需要减去空格符都数量,才能正确获得表情索引
    /// 
    /// 
    private int GetIndex(int itemIndex)
    {
        Match item;

        int count = 0;
        for (int i = 0; i < m_spaceMatchs.Count; i++)
        {
            item = m_spaceMatchs[i];
            if (item.Index < itemIndex)
            {
                count++;
            }
        }

        return itemIndex - count;
    }

}

//----------------------------------------------
//            NGUI: Next-Gen UI kit
// Copyright © 2011-2014 Tasharen Entertainment
//----------------------------------------------

#if !UNITY_EDITOR && (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_BLACKBERRY)
#define MOBILE
#endif

using UnityEngine;
using System.Collections.Generic;
using System.Text;

/// 
/// Input field makes it possible to enter custom information within the UI.
/// 使用这个类时,请去UIInputEditor.cs文件中将 [CustomEditor(typeof(UIInput), true)]改为[CustomEditor(typeof(UIInput), false)]
/// 如果不修改,你将无法设置newLabel这个属性。
/// 

//[AddComponentMenu("NGUI/UI/Input Field")]
public class SymbolInput : UIInput
{
	/// 
	/// Text label used to display the input's value.
	/// 

	public SymbolLabel newLabel;

	/// 
	/// Input field's value.
	/// 

    //[SerializeField][HideInInspector] protected string mValue;

    //protected string mDefaultText = "";
    //protected Color mDefaultColor = Color.white;
    //protected float mPosition = 0f;
    //protected bool mDoInit = true;
    //protected UIWidget.Pivot mPivot = UIWidget.Pivot.TopLeft;

    //static protected int mDrawStart = 0;

//#if MOBILE
//    static protected TouchScreenKeyboard mKeyboard;
//#else
//    protected int mSelectionStart = 0;
//    protected int mSelectionEnd = 0;
//    protected UITexture mHighlight = null;
//    protected UITexture mCaret = null;
//    protected Texture2D mBlankTex = null;
//    protected float mNextBlink = 0f;
//    protected float mLastAlpha = 0f;

//    static protected string mLastIME = "";
//#endif

	/// 
	/// Default text used by the input's label.
	/// 

	public new string defaultText
	{
		get
		{
			return mDefaultText;
		}
		set
		{
			if (mDoInit) Init();
			mDefaultText = value;
			UpdateLabel();
		}
	}

	[System.Obsolete("Use UIInput.value instead")]
	public new string text { get { return this.value; } set { this.value = value; } }

	/// 
	/// Input field's current text value.
	/// 

	public new string value
	{
		get
		{
#if UNITY_EDITOR
			if (!Application.isPlaying) return "";
#endif
			if (mDoInit) Init();
			return mValue;
		}
		set
		{
#if UNITY_EDITOR
			if (!Application.isPlaying) return;
#endif
			if (mDoInit) Init();
			mDrawStart = 0;

#if MOBILE && !UNITY_3_5
			// BB10's implementation has a bug in Unity
			if (Application.platform == RuntimePlatform.BB10Player)
				value = value.Replace("\\b", "\b");
#endif
			// Validate all input
			value = Validate(value);
#if MOBILE
			if (isSelected && mKeyboard != null && mCached != value)
			{
				mKeyboard.text = value;
				mCached = value;
			}

			if (mValue != value)
			{
				mValue = value;
				if (!isSelected) SaveToPlayerPrefs(value);
				UpdateLabel();
				ExecuteOnChange();
			}
#else
			if (mValue != value)
			{
				mValue = value;

				if (isSelected)
				{
					if (string.IsNullOrEmpty(value))
					{
						mSelectionStart = 0;
						mSelectionEnd = 0;
					}
					else
					{
						mSelectionStart = value.Length;
						mSelectionEnd = mSelectionStart;
					}
				}
				else SaveToPlayerPrefs(value);

				UpdateLabel();
				ExecuteOnChange();
			}
#endif
		}
	}

	[System.Obsolete("Use UIInput.isSelected instead")]
	public new bool selected { get { return isSelected; } set { isSelected = value; } }

	/// 
	/// Whether the input is currently selected.
	/// 

	public new bool isSelected
	{
		get
		{
			return selection == this;
		}
		set
		{
			if (!value) { if (isSelected) UICamera.selectedObject = null; }
			else UICamera.selectedObject = gameObject;
		}
	}

#if MOBILE
	/// 
	/// Current position of the cursor.
	/// 

	public int cursorPosition { get { return value.Length; } set {} }

	/// 
	/// Index of the character where selection begins.
	/// 

	public int selectionStart { get { return value.Length; } set {} }

	/// 
	/// Index of the character where selection ends.
	/// 

	public int selectionEnd { get { return value.Length; } set {} }
#else
	/// 
	/// Current position of the cursor.
	/// 

	public new int cursorPosition
	{
		get
		{
			return isSelected ? mSelectionEnd : value.Length;
		}
		set
		{
			if (isSelected)
			{
				mSelectionEnd = value;
				UpdateLabel();
			}
		}
	}

	/// 
	/// Index of the character where selection begins.
	/// 

	public new int selectionStart
	{
		get
		{
			return isSelected ? mSelectionStart : value.Length;
		}
		set
		{
			if (isSelected)
			{
				mSelectionStart = value;
				UpdateLabel();
			}
		}
	}

	/// 
	/// Index of the character where selection ends.
	/// 

	public new int selectionEnd
	{
		get
		{
			return isSelected ? mSelectionEnd : value.Length;
		}
		set
		{
			if (isSelected)
			{
				mSelectionEnd = value;
				UpdateLabel();
			}
		}
	}
#endif

	/// 
	/// Validate the specified text, returning the validated version.
	/// 

	public new string Validate (string val)
	{
		if (string.IsNullOrEmpty(val)) return "";

		StringBuilder sb = new StringBuilder(val.Length);

		for (int i = 0; i < val.Length; ++i)
		{
			char c = val[i];
			if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
			else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
			if (c != 0) sb.Append(c);
		}

		if (characterLimit > 0 && sb.Length > characterLimit)
			return sb.ToString(0, characterLimit);
		return sb.ToString();
	}

	/// 
	/// Automatically set the value by loading it from player prefs if possible.
	/// 

	void Start ()
	{
		if (string.IsNullOrEmpty(mValue))
		{
			if (!string.IsNullOrEmpty(savedAs) && PlayerPrefs.HasKey(savedAs))
				value = PlayerPrefs.GetString(savedAs);
		}
		else value = mValue.Replace("\\n", "\n");
	}

	/// 
	/// Labels used for input shouldn't support rich text.
	/// 

	protected new void Init ()
	{
		if (mDoInit && newLabel != null)
		{
			mDoInit = false;
			mDefaultText = newLabel.text;
            mDefaultColor = newLabel.labelText.color;
            //newLabel.labelText.supportEncoding = false;

            if (newLabel.labelText.alignment == NGUIText.Alignment.Justified)
			{
                newLabel.labelText.alignment = NGUIText.Alignment.Left;
				Debug.LogWarning("Input fields using labels with justified alignment are not supported at this time", this);
			}

            mPivot = newLabel.labelText.pivot;
            mPosition = newLabel.labelText.cachedTransform.localPosition.x;
			UpdateLabel();
		}
	}

	/// 
	/// Save the specified value to player prefs.
	/// 

	protected new void SaveToPlayerPrefs (string val)
	{
		if (!string.IsNullOrEmpty(savedAs))
		{
			if (string.IsNullOrEmpty(val)) PlayerPrefs.DeleteKey(savedAs);
			else PlayerPrefs.SetString(savedAs, val);
		}
	}

	/// 
	/// Selection event, sent by the EventSystem.
	/// 

	override protected void OnSelect (bool isSelected)
	{
		if (isSelected) OnSelectEvent();
		else OnDeselectEvent();
	}

	/// 
	/// Notification of the input field gaining selection.
	/// 

	protected new void OnSelectEvent ()
	{
		selection = this;

		if (mDoInit) Init();
        
		if (newLabel != null && NGUITools.GetActive(this))
		{
            newLabel.labelText.color = activeTextColor;
#if MOBILE
			if (Application.platform == RuntimePlatform.IPhonePlayer ||
				Application.platform == RuntimePlatform.Android
#if UNITY_WP8
				|| Application.platform == RuntimePlatform.WP8Player
#endif
#if UNITY_BLACKBERRY
				|| Application.platform == RuntimePlatform.BB10Player
#endif
			)
			{
				mKeyboard = (inputType == InputType.Password) ?
					TouchScreenKeyboard.Open(mValue, TouchScreenKeyboardType.Default, false, false, true) :
					TouchScreenKeyboard.Open(mValue, (TouchScreenKeyboardType)((int)keyboardType), inputType == InputType.AutoCorrect, newLabel.labelText.multiLine, false, false, defaultText);
			}
			else
#endif
            {
				Vector2 pos = (UICamera.current != null && UICamera.current.cachedCamera != null) ?
                    UICamera.current.cachedCamera.WorldToScreenPoint(newLabel.labelText.worldCorners[0]) :
                    newLabel.labelText.worldCorners[0];
				pos.y = Screen.height - pos.y;
				Input.imeCompositionMode = IMECompositionMode.On;
				Input.compositionCursorPos = pos;
#if !MOBILE
				mSelectionStart = 0;
				mSelectionEnd = string.IsNullOrEmpty(mValue) ? 0 : mValue.Length;
#endif
				mDrawStart = 0;
			}
			UpdateLabel();
		}
	}

	/// 
	/// Notification of the input field losing selection.
	/// 

	protected new void OnDeselectEvent ()
	{
		if (mDoInit) Init();

		if (newLabel != null && NGUITools.GetActive(this))
		{
			mValue = value;
#if MOBILE
			if (mKeyboard != null)
			{
				mKeyboard.active = false;
				mKeyboard = null;
			}
#endif
			if (string.IsNullOrEmpty(mValue))
			{
				newLabel.text = mDefaultText;
                newLabel.labelText.color = mDefaultColor;
			}
			else newLabel.text = mValue;

			Input.imeCompositionMode = IMECompositionMode.Auto;
			RestoreLabelPivot();
		}
		
		selection = null;
		UpdateLabel();
	}

	/// 
	/// Update the text based on input.
	/// 

#if MOBILE
	string mCached = "";

	void Update()
	{
		if (mKeyboard != null && isSelected)
		{
			string text = mKeyboard.text;

			if (mCached != text)
			{
				mCached = text;
				value = text;
			}

			if (mKeyboard.done)
			{
#if !UNITY_3_5
				if (!mKeyboard.wasCanceled)
#endif
					Submit();
				mKeyboard = null;
				isSelected = false;
				mCached = "";
			}
		}
	}
#else
	void Update ()
	{
#if UNITY_EDITOR
		if (!Application.isPlaying) return;
#endif
		if (isSelected)
		{
			if (mDoInit) Init();

			if (selectOnTab != null && Input.GetKeyDown(KeyCode.Tab))
			{
				UICamera.selectedObject = selectOnTab;
				return;
			}

			string ime = Input.compositionString;

			// There seems to be an inconsistency between IME on Windows, and IME on OSX.
			// On Windows, Input.inputString is always empty while IME is active. On the OSX it is not.
			if (string.IsNullOrEmpty(ime) && !string.IsNullOrEmpty(Input.inputString))
			{
				// Process input ignoring non-printable characters as they are not consistent.
				// Windows has them, OSX may not. They get handled inside OnGUI() instead.
				string s = Input.inputString;

				for (int i = 0; i < s.Length; ++i)
				{
					char ch = s[i];
					if (ch < ' ') continue;

					// OSX inserts these characters for arrow keys
					if (ch == '\uF700') continue;
					if (ch == '\uF701') continue;
					if (ch == '\uF702') continue;
					if (ch == '\uF703') continue;

					Insert(ch.ToString());
				}
			}

			// Append IME composition
			if (mLastIME != ime)
			{
				mSelectionEnd = string.IsNullOrEmpty(ime) ? mSelectionStart : mValue.Length + ime.Length;
				mLastIME = ime;
				UpdateLabel();
				ExecuteOnChange();
			}

			// Blink the caret
			if (mCaret != null && mNextBlink < RealTime.time)
			{
				mNextBlink = RealTime.time + 0.5f;
				mCaret.enabled = !mCaret.enabled;
			}

			// If the label's final alpha changes, we need to update the drawn geometry,
			// or the highlight widgets (which have their geometry set manually) won't update.
            if (isSelected && mLastAlpha != newLabel.labelText.finalAlpha)
				UpdateLabel();
		}
	}

	/// 
	/// Unfortunately Unity 4.3 and earlier doesn't offer a way to properly process events outside of OnGUI.
	/// 

	void OnGUI ()
	{
		if (isSelected && Event.current.rawType == EventType.KeyDown)
			ProcessEvent(Event.current);
	}

	/// 
	/// Handle the specified event.
	/// 

	bool ProcessEvent (Event ev)
	{
		if (newLabel == null) return false;

		RuntimePlatform rp = Application.platform;

		bool isMac = (
			rp == RuntimePlatform.OSXEditor ||
			rp == RuntimePlatform.OSXPlayer ||
			rp == RuntimePlatform.OSXWebPlayer);

		bool ctrl = isMac ?
			((ev.modifiers & EventModifiers.Command) != 0) :
			((ev.modifiers & EventModifiers.Control) != 0);

		bool shift = ((ev.modifiers & EventModifiers.Shift) != 0);

		switch (ev.keyCode)
		{
			case KeyCode.Backspace:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
					if (mSelectionStart == mSelectionEnd)
					{
						if (mSelectionStart < 1) return true;
						--mSelectionEnd;
					}
					Insert("");
				}
				return true;
			}

			case KeyCode.Delete:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
					if (mSelectionStart == mSelectionEnd)
					{
						if (mSelectionStart >= mValue.Length) return true;
						++mSelectionEnd;
					}
					Insert("");
				}
				return true;
			}

			case KeyCode.LeftArrow:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
					mSelectionEnd = Mathf.Max(mSelectionEnd - 1, 0);
					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			case KeyCode.RightArrow:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
					mSelectionEnd = Mathf.Min(mSelectionEnd + 1, mValue.Length);
					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			case KeyCode.PageUp:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
					mSelectionEnd = 0;
					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			case KeyCode.PageDown:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
					mSelectionEnd = mValue.Length;
					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			case KeyCode.Home:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
                    if (newLabel.labelText.multiLine)
					{
                        mSelectionEnd = newLabel.labelText.GetCharacterIndex(mSelectionEnd, KeyCode.Home);
					}
					else mSelectionEnd = 0;

					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			case KeyCode.End:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
                    if (newLabel.labelText.multiLine)
					{
                        mSelectionEnd = newLabel.labelText.GetCharacterIndex(mSelectionEnd, KeyCode.End);
					}
					else mSelectionEnd = mValue.Length;

					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			case KeyCode.UpArrow:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
                    mSelectionEnd = newLabel.labelText.GetCharacterIndex(mSelectionEnd, KeyCode.UpArrow);
					if (mSelectionEnd != 0) mSelectionEnd += mDrawStart;
					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			case KeyCode.DownArrow:
			{
				ev.Use();

				if (!string.IsNullOrEmpty(mValue))
				{
                    mSelectionEnd = newLabel.labelText.GetCharacterIndex(mSelectionEnd, KeyCode.DownArrow);
                    if (mSelectionEnd != newLabel.labelText.processedText.Length) mSelectionEnd += mDrawStart;
					else mSelectionEnd = mValue.Length;
					if (!shift) mSelectionStart = mSelectionEnd;
					UpdateLabel();
				}
				return true;
			}

			// Copy
			case KeyCode.C:
			{
				if (ctrl)
				{
					ev.Use();
					NGUITools.clipboard = GetSelection();
				}
				return true;
			}

			// Paste
			case KeyCode.V:
			{
				if (ctrl)
				{
					ev.Use();
					Insert(NGUITools.clipboard);
				}
				return true;
			}

			// Cut
			case KeyCode.X:
			{
				if (ctrl)
				{
					ev.Use();
					NGUITools.clipboard = GetSelection();
					Insert("");
				}
				return true;
			}

			// Submit
			case KeyCode.Return:
			case KeyCode.KeypadEnter:
			{
				ev.Use();

                if (newLabel.labelText.multiLine && !ctrl && newLabel.labelText.overflowMethod != UILabel.Overflow.ClampContent)
				{
					Insert("\n");
				}
				else
				{
					UICamera.currentScheme = UICamera.ControlScheme.Controller;
					UICamera.currentKey = ev.keyCode;
					Submit();
					UICamera.currentKey = KeyCode.None;
				}
				return true;
			}
		}
		return false;
	}

	/// 
	/// Insert the specified text string into the current input value, respecting selection and validation.
	/// 

	override protected void Insert (string text)
	{
		string left = GetLeftText();
		string right = GetRightText();
		int rl = right.Length;

		StringBuilder sb = new StringBuilder(left.Length + right.Length + text.Length);
		sb.Append(left);

		// Append the new text
		for (int i = 0, imax = text.Length; i < imax; ++i)
		{
			// Can't go past the character limit
			if (characterLimit > 0 && sb.Length + rl >= characterLimit) break;

			// If we have an input validator, validate the input first
			char c = text[i];
			if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
			else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);

			// Append the character if it hasn't been invalidated
			if (c != 0) sb.Append(c);
		}

		// Advance the selection
		mSelectionStart = sb.Length;
		mSelectionEnd = mSelectionStart;

		// Append the text that follows it, ensuring that it's also validated after the inserted value
		for (int i = 0, imax = right.Length; i < imax; ++i)
		{
			char c = right[i];
			if (onValidate != null) c = onValidate(sb.ToString(), sb.Length, c);
			else if (validation != Validation.None) c = Validate(sb.ToString(), sb.Length, c);
			if (c != 0) sb.Append(c);
		}

		mValue = sb.ToString();
		UpdateLabel();
		ExecuteOnChange();
	}

	/// 
	/// Get the text to the left of the selection.
	/// 

	protected new string GetLeftText ()
	{
		int min = Mathf.Min(mSelectionStart, mSelectionEnd);
		return (string.IsNullOrEmpty(mValue) || min < 0) ? "" : mValue.Substring(0, min);
	}

	/// 
	/// Get the text to the right of the selection.
	/// 

	protected new string GetRightText ()
	{
		int max = Mathf.Max(mSelectionStart, mSelectionEnd);
		return (string.IsNullOrEmpty(mValue) || max >= mValue.Length) ? "" : mValue.Substring(max);
	}

	/// 
	/// Get currently selected text.
	/// 

	protected new string GetSelection ()
	{
		if (string.IsNullOrEmpty(mValue) || mSelectionStart == mSelectionEnd)
		{
			return "";
		}
		else
		{
			int min = Mathf.Min(mSelectionStart, mSelectionEnd);
			int max = Mathf.Max(mSelectionStart, mSelectionEnd);
			return mValue.Substring(min, max - min);
		}
	}

	/// 
	/// Helper function that retrieves the index of the character under the mouse.
	/// 

	protected new int GetCharUnderMouse ()
	{
        Vector3[] corners = newLabel.labelText.worldCorners;
		Ray ray = UICamera.currentRay;
		Plane p = new Plane(corners[0], corners[1], corners[2]);
		float dist;
        return p.Raycast(ray, out dist) ? mDrawStart + newLabel.labelText.GetCharacterIndexAtPosition(ray.GetPoint(dist)) : 0;
	}

	/// 
	/// Move the caret on press.
	/// 

	protected override void OnPress (bool isPressed)
	{
		if (isPressed && isSelected && newLabel != null && UICamera.currentScheme == UICamera.ControlScheme.Mouse)
		{
			mSelectionEnd = GetCharUnderMouse();
			if (!Input.GetKey(KeyCode.LeftShift) &&
				!Input.GetKey(KeyCode.RightShift)) mSelectionStart = mSelectionEnd;
			UpdateLabel();
		}
	}

	/// 
	/// Drag selection.
	/// 

	protected override void OnDrag (Vector2 delta)
	{
		if (newLabel != null && UICamera.currentScheme == UICamera.ControlScheme.Mouse)
		{
			mSelectionEnd = GetCharUnderMouse();
			UpdateLabel();
		}
	}

	/// 
	/// Ensure we've released the dynamically created resources.
	/// 

	void OnDisable () { Cleanup(); }

	/// 
	/// Cleanup.
	/// 
    
	protected override void Cleanup ()
	{
		if (mHighlight) mHighlight.enabled = false;
		if (mCaret) mCaret.enabled = false;

		if (mBlankTex)
		{
			NGUITools.Destroy(mBlankTex);
			mBlankTex = null;
		}
	}
#endif // !MOBILE

	/// 
	/// Submit the input field's text.
	/// 

	public new void Submit ()
	{
		if (NGUITools.GetActive(this))
		{
			current = this;
			mValue = value;
			EventDelegate.Execute(onSubmit);
			SaveToPlayerPrefs(mValue);
			current = null;
		}
	}

	/// 
	/// Update the visual text label.
	/// 

	public new void UpdateLabel ()
	{
		if (newLabel != null)
		{
			if (mDoInit) Init();
			bool selected = isSelected;
			string fullText = value;
			bool isEmpty = string.IsNullOrEmpty(fullText) && string.IsNullOrEmpty(Input.compositionString);
            newLabel.labelText.color = (isEmpty && !selected) ? mDefaultColor : activeTextColor;
			string processed;

			if (isEmpty)
			{
				processed = selected ? "" : mDefaultText;
				RestoreLabelPivot();
			}
			else
			{
				if (inputType == InputType.Password)
				{
					processed = "";
					for (int i = 0, imax = fullText.Length; i < imax; ++i) processed += "*";
				}
				else processed = fullText;

				// Start with text leading up to the selection
				int selPos = selected ? Mathf.Min(processed.Length, cursorPosition) : 0;
				string left = processed.Substring(0, selPos);

				// Append the composition string and the cursor character
				if (selected) left += Input.compositionString;

				// Append the text from the selection onwards
				processed = left + processed.Substring(selPos, processed.Length - selPos);

				// Clamped content needs to be adjusted further
                if (selected && newLabel.labelText.overflowMethod == UILabel.Overflow.ClampContent)
				{
					// Determine what will actually fit into the given line
                    int offset = newLabel.labelText.CalculateOffsetToFit(processed);

					if (offset == 0)
					{
						mDrawStart = 0;
						RestoreLabelPivot();
					}
					else if (selPos < mDrawStart)
					{
						mDrawStart = selPos;
						SetPivotToLeft();
					}
					else if (offset < mDrawStart)
					{
						mDrawStart = offset;
						SetPivotToLeft();
					}
					else
					{
                        offset = newLabel.labelText.CalculateOffsetToFit(processed.Substring(0, selPos));

						if (offset > mDrawStart)
						{
							mDrawStart = offset;
							SetPivotToRight();
						}
					}

					// If necessary, trim the front
                    // 处理末尾 开始 出现{ } 转义符
                    if (mDrawStart != 0)
                    {
                        processed = processed.Substring(mDrawStart, processed.Length - mDrawStart);
                        int back = 0;
                        int front = 0;
                        const int t = 3;
                        front = processed.IndexOf('}', 0, t);

                        if (front > -1)
                        {
                            processed = processed.Substring(front + 1);
                        }

                        back = processed.LastIndexOf('{', processed.Length - 1, 3);

                        if (back > -1)
                        {
                            int l = processed.Length;
                            processed = processed.Substring(0, back);
                        }

                    }
				}
				else
				{
					mDrawStart = 0;
					RestoreLabelPivot();
				}
			}

            newLabel.text = processed;

#if !MOBILE
			if (selected)
			{
				int start = mSelectionStart - mDrawStart;
				int end = mSelectionEnd - mDrawStart;

				// Blank texture used by selection and caret
				if (mBlankTex == null)
				{
					mBlankTex = new Texture2D(2, 2, TextureFormat.ARGB32, false);
					for (int y = 0; y < 2; ++y)
						for (int x = 0; x < 2; ++x)
							mBlankTex.SetPixel(x, y, Color.white);
					mBlankTex.Apply();
				}

				// Create the selection highlight
				if (start != end)
				{
					if (mHighlight == null)
					{
						mHighlight = NGUITools.AddWidget(newLabel.labelText.cachedGameObject);
						mHighlight.name = "Input Highlight";
						mHighlight.mainTexture = mBlankTex;
						mHighlight.fillGeometry = false;
                        mHighlight.pivot = newLabel.labelText.pivot;
                        mHighlight.SetAnchor(newLabel.labelText.cachedTransform);
					}
					else
					{
                        mHighlight.pivot = newLabel.labelText.pivot;
						mHighlight.mainTexture = mBlankTex;
						mHighlight.MarkAsChanged();
						mHighlight.enabled = true;
					}
				}

				// Create the caret
				if (mCaret == null)
				{
                    mCaret = NGUITools.AddWidget(newLabel.labelText.cachedGameObject);
					mCaret.name = "Input Caret";
					mCaret.mainTexture = mBlankTex;
					mCaret.fillGeometry = false;
                    mCaret.pivot = newLabel.labelText.pivot;
                    mCaret.SetAnchor(newLabel.labelText.cachedTransform);
				}
				else
				{
                    mCaret.pivot = newLabel.labelText.pivot;
					mCaret.mainTexture = mBlankTex;
					mCaret.MarkAsChanged();
					mCaret.enabled = true;
				}

				if (start != end)
				{
                    newLabel.labelText.PrintOverlay(start, end, mCaret.geometry, mHighlight.geometry, caretColor, selectionColor);
					mHighlight.enabled = mHighlight.geometry.hasVertices;
				}
				else
				{
                    newLabel.labelText.PrintOverlay(start, end, mCaret.geometry, null, caretColor, selectionColor);
					if (mHighlight != null) mHighlight.enabled = false;
				}

				// Reset the blinking time
				mNextBlink = RealTime.time + 0.5f;
                mLastAlpha = newLabel.labelText.finalAlpha;
			}
			else Cleanup();
#endif
		}
	}

	/// 
	/// Set the label's pivot to the left.
	/// 

	protected new void SetPivotToLeft ()
	{
		Vector2 po = NGUIMath.GetPivotOffset(mPivot);
		po.x = 0f;
        newLabel.labelText.pivot = NGUIMath.GetPivot(po);
	}

	/// 
	/// Set the label's pivot to the right.
	/// 

	protected new void SetPivotToRight ()
	{
		Vector2 po = NGUIMath.GetPivotOffset(mPivot);
		po.x = 1f;
        newLabel.labelText.pivot = NGUIMath.GetPivot(po);
	}

	/// 
	/// Restore the input label's pivot point.
	/// 

	protected new void RestoreLabelPivot ()
	{
        if (newLabel != null && newLabel.labelText.pivot != mPivot)
            newLabel.labelText.pivot = mPivot;
	}

	/// 
	/// Validate the specified input.
	/// 

	protected new char Validate (string text, int pos, char ch)
	{
		// Validation is disabled
		if (validation == Validation.None || !enabled) return ch;

		if (validation == Validation.Integer)
		{
			// Integer number validation
			if (ch >= '0' && ch <= '9') return ch;
			if (ch == '-' && pos == 0 && !text.Contains("-")) return ch;
		}
		else if (validation == Validation.Float)
		{
			// Floating-point number
			if (ch >= '0' && ch <= '9') return ch;
			if (ch == '-' && pos == 0 && !text.Contains("-")) return ch;
			if (ch == '.' && !text.Contains(".")) return ch;
		}
		else if (validation == Validation.Alphanumeric)
		{
			// All alphanumeric characters
			if (ch >= 'A' && ch <= 'Z') return ch;
			if (ch >= 'a' && ch <= 'z') return ch;
			if (ch >= '0' && ch <= '9') return ch;
		}
		else if (validation == Validation.Username)
		{
			// Lowercase and numbers
			if (ch >= 'A' && ch <= 'Z') return (char)(ch - 'A' + 'a');
			if (ch >= 'a' && ch <= 'z') return ch;
			if (ch >= '0' && ch <= '9') return ch;
		}
		else if (validation == Validation.Name)
		{
			char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
			char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';

			if (ch >= 'a' && ch <= 'z')
			{
				// Space followed by a letter -- make sure it's capitalized
				if (lastChar == ' ') return (char)(ch - 'a' + 'A');
				return ch;
			}
			else if (ch >= 'A' && ch <= 'Z')
			{
				// Uppercase letters are only allowed after spaces (and apostrophes)
				if (lastChar != ' ' && lastChar != '\'') return (char)(ch - 'A' + 'a');
				return ch;
			}
			else if (ch == '\'')
			{
				// Don't allow more than one apostrophe
				if (lastChar != ' ' && lastChar != '\'' && nextChar != '\'' && !text.Contains("'")) return ch;
			}
			else if (ch == ' ')
			{
				// Don't allow more than one space in a row
				if (lastChar != ' ' && lastChar != '\'' && nextChar != ' ' && nextChar != '\'') return ch;
			}
		}
		return (char)0;
	}

	/// 
	/// Execute the OnChange callback.
	/// 

	protected new void ExecuteOnChange ()
	{
		if (EventDelegate.IsValid(onChange))
		{
			current = this;
			EventDelegate.Execute(onChange);
			current = null;
		}
	}
}

//----------------------------------------------
//            NGUI: Next-Gen UI kit
// Copyright © 2011-2014 Tasharen Entertainment
//----------------------------------------------

#if !UNITY_3_5 && !UNITY_FLASH
#define DYNAMIC_FONT
#endif

using UnityEngine;
using System.Collections.Generic;
using System;
using Alignment = NGUIText.Alignment;

[ExecuteInEditMode]
[AddComponentMenu("NGUI/UI/NGUI Label")]
public class UILabel : UIWidget
{
	public enum Effect
	{
		None,
		Shadow,
		Outline,
	}

	public enum Overflow
	{
		ShrinkContent,
		ClampContent,
		ResizeFreely,
		ResizeHeight,
	}

	public enum Crispness
	{
		Never,
		OnDesktop,
		Always,
	}

	/// 
	/// Whether the label will keep its content crisp even when shrunk.
	/// You may want to turn this off on mobile devices.
	/// 

	public Crispness keepCrispWhenShrunk = Crispness.OnDesktop;

	[HideInInspector][SerializeField] Font mTrueTypeFont;
	[HideInInspector][SerializeField] UIFont mFont;
#if !UNITY_3_5
	[MultilineAttribute(6)]
#endif
	[HideInInspector][SerializeField] string mText = "";
	[HideInInspector][SerializeField] int mFontSize = 16;
	[HideInInspector][SerializeField] FontStyle mFontStyle = FontStyle.Normal;
	[HideInInspector][SerializeField] Alignment mAlignment = Alignment.Automatic;
	[HideInInspector][SerializeField] bool mEncoding = true;
	[HideInInspector][SerializeField] int mMaxLineCount = 0; // 0 denotes unlimited
	[HideInInspector][SerializeField] Effect mEffectStyle = Effect.None;
	[HideInInspector][SerializeField] Color mEffectColor = Color.black;
	[HideInInspector][SerializeField] NGUIText.SymbolStyle mSymbols = NGUIText.SymbolStyle.Normal;
	[HideInInspector][SerializeField] Vector2 mEffectDistance = Vector2.one;
	[HideInInspector][SerializeField] Overflow mOverflow = Overflow.ShrinkContent;
	[HideInInspector][SerializeField] Material mMaterial;
	[HideInInspector][SerializeField] bool mApplyGradient = false;
	[HideInInspector][SerializeField] Color mGradientTop = Color.white;
	[HideInInspector][SerializeField] Color mGradientBottom = new Color(0.7f, 0.7f, 0.7f);
	[HideInInspector][SerializeField] int mSpacingX = 0;
	[HideInInspector][SerializeField] int mSpacingY = 0;

	// Obsolete values
	[HideInInspector][SerializeField] bool mShrinkToFit = false;
	[HideInInspector][SerializeField] int mMaxLineWidth = 0;
	[HideInInspector][SerializeField] int mMaxLineHeight = 0;
	[HideInInspector][SerializeField] float mLineWidth = 0;
	[HideInInspector][SerializeField] bool mMultiline = true;

#if DYNAMIC_FONT
	[System.NonSerialized]
	Font mActiveTTF = null;
	float mDensity = 1f;
#endif
	bool mShouldBeProcessed = true;
	string mProcessedText = null;
	bool mPremultiply = false;
	Vector2 mCalculatedSize = Vector2.zero;
	float mScale = 1f;
	int mPrintedSize = 0;
	int mLastWidth = 0;
	int mLastHeight = 0;

	/// 
	/// Function used to determine if something has changed (and thus the geometry must be rebuilt)
	/// 

	bool shouldBeProcessed
	{
		get
		{
			return mShouldBeProcessed;
		}
		set
		{
			if (value)
			{
				mChanged = true;
				mShouldBeProcessed = true;
			}
			else
			{
				mShouldBeProcessed = false;
			}
		}
	}

	/// 
	/// Whether the rectangle is anchored horizontally.
	/// 

	public override bool isAnchoredHorizontally { get { return base.isAnchoredHorizontally || mOverflow == Overflow.ResizeFreely; } }

	/// 
	/// Whether the rectangle is anchored vertically.
	/// 

	public override bool isAnchoredVertically
	{
		get
		{
			return base.isAnchoredVertically ||
				mOverflow == Overflow.ResizeFreely ||
				mOverflow == Overflow.ResizeHeight;
		}
	}

	/// 
	/// Retrieve the material used by the font.
	/// 

	public override Material material
	{
		get
		{
			if (mMaterial != null) return mMaterial;
			if (mFont != null) return mFont.material;
			if (mTrueTypeFont != null) return mTrueTypeFont.material;
			return null;
		}
		set
		{
			if (mMaterial != value)
			{
				MarkAsChanged();
				mMaterial = value;
				MarkAsChanged();
			}
		}
	}

	[Obsolete("Use UILabel.bitmapFont instead")]
	public UIFont font { get { return bitmapFont; } set { bitmapFont = value; } }

	/// 
	/// Set the font used by this label.
	/// 

	public UIFont bitmapFont
	{
		get
		{
			return mFont;
		}
		set
		{
			if (mFont != value)
			{
				RemoveFromPanel();
				mFont = value;
				mTrueTypeFont = null;
				MarkAsChanged();
			}
		}
	}

	/// 
	/// Set the font used by this label.
	/// 

	public Font trueTypeFont
	{
		get
		{
			if (mTrueTypeFont != null) return mTrueTypeFont;
			return (mFont != null ? mFont.dynamicFont : null);
		}
		set
		{
			if (mTrueTypeFont != value)
			{
#if DYNAMIC_FONT
				SetActiveFont(null);
				RemoveFromPanel();
				mTrueTypeFont = value;
				shouldBeProcessed = true;
				mFont = null;
				SetActiveFont(value);
				ProcessAndRequest();
				if (mActiveTTF != null)
					base.MarkAsChanged();
#else
				mTrueTypeFont = value;
				mFont = null;
#endif
			}
		}
	}

	/// 
	/// Ambiguous helper function.
	/// 

	public UnityEngine.Object ambigiousFont
	{
		get
		{
			return (mFont != null) ? (UnityEngine.Object)mFont : (UnityEngine.Object)mTrueTypeFont;
		}
		set
		{
			UIFont bf = value as UIFont;
			if (bf != null) bitmapFont = bf;
			else trueTypeFont = value as Font;
		}
	}

	/// 
	/// Text that's being displayed by the label.
	/// 

	public string text
	{
		get
		{
			return mText;
		}
		set
		{
			if (mText == value) return;

			if (string.IsNullOrEmpty(value))
			{
				if (!string.IsNullOrEmpty(mText))
				{
					mText = "";
					MarkAsChanged();
					ProcessAndRequest();
				}
			}
			else if (mText != value)
			{
				mText = value;
				MarkAsChanged();
				ProcessAndRequest();
			}

			if (autoResizeBoxCollider) ResizeCollider();
		}
	}

	/// 
	/// Default font size.
	/// 

	public int defaultFontSize { get { return (trueTypeFont != null) ? mFontSize : (mFont != null ? mFont.defaultSize : 16); } }

	/// 
	/// Active font size used by the label.
	/// 

	public int fontSize
	{
		get
		{
			return mFontSize;
		}
		set
		{
			value = Mathf.Clamp(value, 0, 256);

			if (mFontSize != value)
			{
				mFontSize = value;
				shouldBeProcessed = true;
				ProcessAndRequest();
			}
		}
	}

	/// 
	/// Dynamic font style used by the label.
	/// 

	public FontStyle fontStyle
	{
		get
		{
			return mFontStyle;
		}
		set
		{
			if (mFontStyle != value)
			{
				mFontStyle = value;
				shouldBeProcessed = true;
				ProcessAndRequest();
			}
		}
	}

	/// 
	/// Text alignment option.
	/// 

	public Alignment alignment
	{
		get
		{
			return mAlignment;
		}
		set
		{
			if (mAlignment != value)
			{
				mAlignment = value;
				shouldBeProcessed = true;
				ProcessAndRequest();
			}
		}
	}

	/// 
	/// Whether a gradient will be applied.
	/// 

	public bool applyGradient
	{
		get
		{
			return mApplyGradient;
		}
		set
		{
			if (mApplyGradient != value)
			{
				mApplyGradient = value;
				MarkAsChanged();
			}
		}
	}

	/// 
	/// Top gradient color.
	/// 

	public Color gradientTop
	{
		get
		{
			return mGradientTop;
		}
		set
		{
			if (mGradientTop != value)
			{
				mGradientTop = value;
				if (mApplyGradient) MarkAsChanged();
			}
		}
	}

	/// 
	/// Bottom gradient color.
	/// 

	public Color gradientBottom
	{
		get
		{
			return mGradientBottom;
		}
		set
		{
			if (mGradientBottom != value)
			{
				mGradientBottom = value;
				if (mApplyGradient) MarkAsChanged();
			}
		}
	}

	/// 
	/// Additional horizontal spacing between characters when printing text.
	/// 

	public int spacingX
	{
		get
		{
			return mSpacingX;
		}
		set
		{
			if (mSpacingX != value)
			{
				mSpacingX = value;
				MarkAsChanged();
			}
		}
	}

	/// 
	/// Additional vertical spacing between lines when printing text.
	/// 

	public int spacingY
	{
		get
		{
			return mSpacingY;
		}
		set
		{
			if (mSpacingY != value)
			{
				mSpacingY = value;
				MarkAsChanged();
			}
		}
	}

#if DYNAMIC_FONT
	/// 
	/// Whether the label will use the printed size instead of font size when printing the label.
	/// It's a dynamic font feature that will ensure that the text is crisp when shrunk.
	/// 

	bool keepCrisp
	{
		get
		{
			if (trueTypeFont != null && keepCrispWhenShrunk != Crispness.Never)
			{
#if UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_BLACKBERRY
				return (keepCrispWhenShrunk == Crispness.Always);
#else
				return true;
#endif
			}
			return false;
		}
	}
#endif

	/// 
	/// Whether this label will support color encoding in the format of [RRGGBB] and new line in the form of a "\\n" string.
	/// 

	public bool supportEncoding
	{
		get
		{
			return mEncoding;
		}
		set
		{
			if (mEncoding != value)
			{
				mEncoding = value;
				shouldBeProcessed = true;
			}
		}
	}

	/// 
	/// Style used for symbols.
	/// 

	public NGUIText.SymbolStyle symbolStyle
	{
		get
		{
			return mSymbols;
		}
		set
		{
			if (mSymbols != value)
			{
				mSymbols = value;
				shouldBeProcessed = true;
			}
		}
	}

	/// 
	/// Overflow method controls the label's behaviour when its content doesn't fit the bounds.
	/// 

	public Overflow overflowMethod
	{
		get
		{
			return mOverflow;
		}
		set
		{
			if (mOverflow != value)
			{
				mOverflow = value;
				shouldBeProcessed = true;
			}
		}
	}

	/// 
	/// Maximum width of the label in pixels.
	/// 

	[System.Obsolete("Use 'width' instead")]
	public int lineWidth
	{
		get { return width; }
		set { width = value; }
	}

	/// 
	/// Maximum height of the label in pixels.
	/// 

	[System.Obsolete("Use 'height' instead")]
	public int lineHeight
	{
		get { return height; }
		set { height = value; }
	}

	/// 
	/// Whether the label supports multiple lines.
	/// 
	
	public bool multiLine
	{
		get
		{
			return mMaxLineCount != 1;
		}
		set
		{
			if ((mMaxLineCount != 1) != value)
			{
				mMaxLineCount = (value ? 0 : 1);
				shouldBeProcessed = true;
			}
		}
	}

	/// 
	/// Process the label's text before returning its corners.
	/// 

	public override Vector3[] localCorners
	{
		get
		{
			if (shouldBeProcessed) ProcessText();
			return base.localCorners;
		}
	}

	/// 
	/// Process the label's text before returning its corners.
	/// 

	public override Vector3[] worldCorners
	{
		get
		{
			if (shouldBeProcessed) ProcessText();
			return base.worldCorners;
		}
	}

	/// 
	/// Process the label's text before returning its drawing dimensions.
	/// 

	public override Vector4 drawingDimensions
	{
		get
		{
			if (shouldBeProcessed) ProcessText();
			return base.drawingDimensions;
		}
	}

	/// 
	/// The max number of lines to be displayed for the label
	/// 

	public int maxLineCount
	{
		get
		{
			return mMaxLineCount;
		}
		set
		{
			if (mMaxLineCount != value)
			{
				mMaxLineCount = Mathf.Max(value, 0);
				shouldBeProcessed = true;
				if (overflowMethod == Overflow.ShrinkContent) MakePixelPerfect();
			}
		}
	}

	/// 
	/// What effect is used by the label.
	/// 

	public Effect effectStyle
	{
		get
		{
			return mEffectStyle;
		}
		set
		{
			if (mEffectStyle != value)
			{
				mEffectStyle = value;
				shouldBeProcessed = true;
			}
		}
	}

	/// 
	/// Color used by the effect, if it's enabled.
	/// 

	public Color effectColor
	{
		get
		{
			return mEffectColor;
		}
		set
		{
			if (mEffectColor != value)
			{
				mEffectColor = value;
				if (mEffectStyle != Effect.None) shouldBeProcessed = true;
			}
		}
	}

	/// 
	/// Effect distance in pixels.
	/// 

	public Vector2 effectDistance
	{
		get
		{
			return mEffectDistance;
		}
		set
		{
			if (mEffectDistance != value)
			{
				mEffectDistance = value;
				shouldBeProcessed = true;
			}
		}
	}

	/// 
	/// Whether the label will automatically shrink its size in order to fit the maximum line width.
	/// 

	[System.Obsolete("Use 'overflowMethod == UILabel.Overflow.ShrinkContent' instead")]
	public bool shrinkToFit
	{
		get
		{
			return mOverflow == Overflow.ShrinkContent;
		}
		set
		{
			if (value)
			{
				overflowMethod = Overflow.ShrinkContent;
			}
		}
	}

	/// 
	/// Returns the processed version of 'text', with new line characters, line wrapping, etc.
	/// 

	public string processedText
	{
		get
		{
			if (mLastWidth != mWidth || mLastHeight != mHeight)
			{
				mLastWidth = mWidth;
				mLastHeight = mHeight;
				mShouldBeProcessed = true;
			}

			// Process the text if necessary
			if (shouldBeProcessed) ProcessText();
			return mProcessedText;
		}
	}

	/// 
	/// Actual printed size of the text, in pixels.
	/// 

	public Vector2 printedSize
	{
		get
		{
			if (shouldBeProcessed) ProcessText();
			return mCalculatedSize;
		}
	}

	/// 
	/// Local size of the widget, in pixels.
	/// 

	public override Vector2 localSize
	{
		get
		{
			if (shouldBeProcessed) ProcessText();
			return base.localSize;
		}
	}

	/// 
	/// Whether the label has a valid font.
	/// 

#if DYNAMIC_FONT
	bool isValid { get { return mFont != null || mTrueTypeFont != null; } }
#else
	bool isValid { get { return mFont != null; } }
#endif

#if DYNAMIC_FONT
	static BetterList mList = new BetterList();
	static Dictionary mFontUsage = new Dictionary();

	/// 
	/// Register the font texture change listener.
	/// 

	protected override void OnInit ()
	{
		base.OnInit();
		mList.Add(this);
		SetActiveFont(trueTypeFont);
	}

	/// 
	/// Remove the font texture change listener.
	/// 

	protected override void OnDisable ()
	{
		SetActiveFont(null);
		mList.Remove(this);
		base.OnDisable();
	}

	/// 
	/// Set the active font, correctly setting and clearing callbacks.
	/// 

	protected void SetActiveFont (Font fnt)
	{
		if (mActiveTTF != fnt)
		{
			if (mActiveTTF != null)
			{
				int usage;

				if (mFontUsage.TryGetValue(mActiveTTF, out usage))
				{
					usage = Mathf.Max(0, --usage);

					if (usage == 0)
					{
						mActiveTTF.textureRebuildCallback = null;
						mFontUsage.Remove(mActiveTTF);
					}
					else mFontUsage[mActiveTTF] = usage;
				}
				else mActiveTTF.textureRebuildCallback = null;
			}

			mActiveTTF = fnt;

			if (mActiveTTF != null)
			{
				int usage = 0;

				// Font hasn't been used yet? Register a change delegate callback
				if (!mFontUsage.TryGetValue(mActiveTTF, out usage))
					mActiveTTF.textureRebuildCallback = OnFontTextureChanged;

				mFontUsage[mActiveTTF] = ++usage;
			}
		}
	}

	/// 
	/// Notification called when the Unity's font's texture gets rebuilt.
	/// Unity's font has a nice tendency to simply discard other characters when the texture's dimensions change.
	/// By requesting them inside the notification callback, we immediately force them back in.
	/// Originally I was subscribing each label to the font individually, but as it turned out
	/// mono's delegate system causes an insane amount of memory allocations when += or -= to a delegate.
	/// So... queue yet another work-around.
	/// 

	static void OnFontTextureChanged ()
	{
		for (int i = 0; i < mList.size; ++i)
		{
			UILabel lbl = mList[i];

			if (lbl != null)
			{
				Font fnt = lbl.trueTypeFont;

				if (fnt != null)
				{
					fnt.RequestCharactersInTexture(lbl.mText, lbl.mPrintedSize, lbl.mFontStyle);
					lbl.MarkAsChanged();
				}
			}
		}
	}
#endif

	/// 
	/// Get the sides of the rectangle relative to the specified transform.
	/// The order is left, top, right, bottom.
	/// 

	public override Vector3[] GetSides (Transform relativeTo)
	{
		if (shouldBeProcessed) ProcessText();
		return base.GetSides(relativeTo);
	}

	/// 
	/// Upgrading labels is a bit different.
	/// 

	protected override void UpgradeFrom265 ()
	{
		ProcessText(true, true);

		if (mShrinkToFit)
		{
			overflowMethod = Overflow.ShrinkContent;
			mMaxLineCount = 0;
		}

		if (mMaxLineWidth != 0)
		{
			width = mMaxLineWidth;
			overflowMethod = mMaxLineCount > 0 ? Overflow.ResizeHeight : Overflow.ShrinkContent;
		}
		else overflowMethod = Overflow.ResizeFreely;

		if (mMaxLineHeight != 0)
			height = mMaxLineHeight;

		if (mFont != null)
		{
			int min = mFont.defaultSize;
			if (height < min) height = min;
		}

		mMaxLineWidth = 0;
		mMaxLineHeight = 0;
		mShrinkToFit = false;

		NGUITools.UpdateWidgetCollider(gameObject, true);
	}

	/// 
	/// If the label is anchored it should not auto-resize.
	/// 

	protected override void OnAnchor ()
	{
		if (mOverflow == Overflow.ResizeFreely)
		{
			if (isFullyAnchored)
				mOverflow = Overflow.ShrinkContent;
		}
		else if (mOverflow == Overflow.ResizeHeight)
		{
			if (topAnchor.target != null && bottomAnchor.target != null)
				mOverflow = Overflow.ShrinkContent;
		}
		base.OnAnchor();
	}

	/// 
	/// Request the needed characters in the texture.
	/// 

	void ProcessAndRequest ()
	{
#if UNITY_EDITOR
		if (!Application.isPlaying && !NGUITools.GetActive(this)) return;
		if (!mAllowProcessing) return;
#endif
		if (ambigiousFont != null) ProcessText();
	}

#if UNITY_EDITOR
	// Used to ensure that we don't process font more than once inside OnValidate function below
	bool mAllowProcessing = true;
	bool mUsingTTF = true;

	/// 
	/// Validate the properties.
	/// 

	protected override void OnValidate ()
	{
		base.OnValidate();

		if (NGUITools.GetActive(this))
		{
			Font ttf = mTrueTypeFont;
			UIFont fnt = mFont;

			// If the true type font was not used before, but now it is, clear the font reference
			if (!mUsingTTF && ttf != null) fnt = null;
			else if (mUsingTTF && fnt != null) ttf = null;

			mFont = null;
			mTrueTypeFont = null;
			mAllowProcessing = false;

#if DYNAMIC_FONT
			SetActiveFont(null);
#endif
			if (fnt != null)
			{
				bitmapFont = fnt;
				mUsingTTF = false;
			}
			else if (ttf != null)
			{
				trueTypeFont = ttf;
				mUsingTTF = true;
			}

			shouldBeProcessed = true;
			mAllowProcessing = true;
			ProcessAndRequest();
			if (autoResizeBoxCollider) ResizeCollider();
		}
	}
#endif

	/// 
	/// Determine start-up values.
	/// 

	protected override void OnStart ()
	{
		base.OnStart();

		// Legacy support
		if (mLineWidth > 0f)
		{
			mMaxLineWidth = Mathf.RoundToInt(mLineWidth);
			mLineWidth = 0f;
		}

		if (!mMultiline)
		{
			mMaxLineCount = 1;
			mMultiline = true;
		}

		// Whether this is a premultiplied alpha shader
		mPremultiply = (material != null && material.shader != null && material.shader.name.Contains("Premultiplied"));

#if DYNAMIC_FONT
		// Request the text within the font
		ProcessAndRequest();
#endif
	}

	/// 
	/// UILabel needs additional processing when something changes.
	/// 

	public override void MarkAsChanged ()
	{
		shouldBeProcessed = true;
		base.MarkAsChanged();
	}

	/// 
	/// Process the raw text, called when something changes.
	/// 

	public void ProcessText () { ProcessText(false, true); }

	/// 
	/// Process the raw text, called when something changes.
	/// 

	void ProcessText (bool legacyMode, bool full)
	{
		if (!isValid) return;

		mChanged = true;
		shouldBeProcessed = false;

		NGUIText.rectWidth  = legacyMode ? (mMaxLineWidth  != 0 ? mMaxLineWidth  : 1000000) : width;
		NGUIText.rectHeight = legacyMode ? (mMaxLineHeight != 0 ? mMaxLineHeight : 1000000) : height;

		mPrintedSize = Mathf.Abs(legacyMode ? Mathf.RoundToInt(cachedTransform.localScale.x) : defaultFontSize);
		mScale = 1f;

		if (NGUIText.rectWidth < 1 || NGUIText.rectHeight < 0)
		{
			mProcessedText = "";
			return;
		}

#if DYNAMIC_FONT
		bool isDynamic = (trueTypeFont != null);

		if (isDynamic && keepCrisp)
		{
			UIRoot rt = root;
			if (rt != null) mDensity = (rt != null) ? rt.pixelSizeAdjustment : 1f;
		}
		else mDensity = 1f;
#endif
		if (full) UpdateNGUIText();

		if (mOverflow == Overflow.ResizeFreely) NGUIText.rectWidth = 1000000;
		if (mOverflow == Overflow.ResizeFreely || mOverflow == Overflow.ResizeHeight)
			NGUIText.rectHeight = 1000000;

		if (mPrintedSize > 0)
		{
#if DYNAMIC_FONT
			bool adjustSize = keepCrisp;
#endif
			for (int ps = mPrintedSize; ps > 0; --ps)
			{
#if DYNAMIC_FONT
				// Adjust either the size, or the scale
				if (adjustSize)
				{
					mPrintedSize = ps;
					NGUIText.fontSize = mPrintedSize;
				}
				else
#endif
				{
					mScale = (float)ps / mPrintedSize;
#if DYNAMIC_FONT
					NGUIText.fontScale = isDynamic ? mScale : ((float)mFontSize / mFont.defaultSize) * mScale;
#else
					NGUIText.fontScale = ((float)mFontSize / mFont.defaultSize) * mScale;
#endif
				}

				NGUIText.Update(false);

				// Wrap the text
				bool fits = NGUIText.WrapText(mText, out mProcessedText, true);

				if (mOverflow == Overflow.ShrinkContent && !fits)
				{
					if (--ps > 1) continue;
					else break;
				}
				else if (mOverflow == Overflow.ResizeFreely)
				{
					mCalculatedSize = NGUIText.CalculatePrintedSize(mProcessedText);

					mWidth = Mathf.Max(minWidth, Mathf.RoundToInt(mCalculatedSize.x));
					mHeight = Mathf.Max(minHeight, Mathf.RoundToInt(mCalculatedSize.y));

					if ((mWidth & 1) == 1) ++mWidth;
					if ((mHeight & 1) == 1) ++mHeight;
				}
				else if (mOverflow == Overflow.ResizeHeight)
				{
					mCalculatedSize = NGUIText.CalculatePrintedSize(mProcessedText);
					mHeight = Mathf.Max(minHeight, Mathf.RoundToInt(mCalculatedSize.y));
					if ((mHeight & 1) == 1) ++mHeight;
				}
				else
				{
					mCalculatedSize = NGUIText.CalculatePrintedSize(mProcessedText);
				}

				// Upgrade to the new system
				if (legacyMode)
				{
					width = Mathf.RoundToInt(mCalculatedSize.x);
					height = Mathf.RoundToInt(mCalculatedSize.y);
					cachedTransform.localScale = Vector3.one;
				}
				break;
			}
		}
		else
		{
			cachedTransform.localScale = Vector3.one;
			mProcessedText = "";
			mScale = 1f;
		}
		
		if (full)
		{
			NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
			NGUIText.dynamicFont = null;
#endif
		}
	}

	/// 
	/// Text is pixel-perfect when its scale matches the size.
	/// 

	public override void MakePixelPerfect ()
	{
		if (ambigiousFont != null)
		{
			Vector3 pos = cachedTransform.localPosition;
			pos.x = Mathf.RoundToInt(pos.x);
			pos.y = Mathf.RoundToInt(pos.y);
			pos.z = Mathf.RoundToInt(pos.z);

			cachedTransform.localPosition = pos;
			cachedTransform.localScale = Vector3.one;

			if (mOverflow == Overflow.ResizeFreely)
			{
				AssumeNaturalSize();
			}
			else
			{
				int w = width;
				int h = height;

				Overflow over = mOverflow;
				if (over != Overflow.ResizeHeight) mWidth = 100000;
				mHeight = 100000;

				mOverflow = Overflow.ShrinkContent;
				ProcessText(false, true);
				mOverflow = over;

				int minX = Mathf.RoundToInt(mCalculatedSize.x);
				int minY = Mathf.RoundToInt(mCalculatedSize.y);

				minX = Mathf.Max(minX, base.minWidth);
				minY = Mathf.Max(minY, base.minHeight);

				mWidth = Mathf.Max(w, minX);
				mHeight = Mathf.Max(h, minY);

				MarkAsChanged();
			}
		}
		else base.MakePixelPerfect();
	}

	/// 
	/// Make the label assume its natural size.
	/// 

	public void AssumeNaturalSize ()
	{
		if (ambigiousFont != null)
		{
			mWidth = 100000;
			mHeight = 100000;
			ProcessText(false, true);
			mWidth = Mathf.RoundToInt(mCalculatedSize.x);
			mHeight = Mathf.RoundToInt(mCalculatedSize.y);
			if ((mWidth & 1) == 1) ++mWidth;
			if ((mHeight & 1) == 1) ++mHeight;
			MarkAsChanged();
		}
	}

	[System.Obsolete("Use UILabel.GetCharacterAtPosition instead")]
	public int GetCharacterIndex (Vector3 worldPos) { return GetCharacterIndexAtPosition(worldPos); }

	[System.Obsolete("Use UILabel.GetCharacterAtPosition instead")]
	public int GetCharacterIndex (Vector2 localPos) { return GetCharacterIndexAtPosition(localPos); }

	static BetterList mTempVerts = new BetterList();
	static BetterList mTempIndices = new BetterList();

	/// 
	/// Return the index of the character at the specified world position.
	/// 

	public int GetCharacterIndexAtPosition (Vector3 worldPos)
	{
		Vector2 localPos = cachedTransform.InverseTransformPoint(worldPos);
		return GetCharacterIndexAtPosition(localPos);
	}

	/// 
	/// Return the index of the character at the specified local position.
	/// 

	public int GetCharacterIndexAtPosition (Vector2 localPos)
	{
		if (isValid)
		{
			string text = processedText;
			if (string.IsNullOrEmpty(text)) return 0;

			UpdateNGUIText();

			NGUIText.PrintCharacterPositions(text, mTempVerts, mTempIndices);

			if (mTempVerts.size > 0)
			{
				ApplyOffset(mTempVerts, 0);
				int retVal = NGUIText.GetClosestCharacter(mTempVerts, localPos);
				retVal = mTempIndices[retVal];

				mTempVerts.Clear();
				mTempIndices.Clear();

				NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
				NGUIText.dynamicFont = null;
#endif
				return retVal;
			}

			NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
			NGUIText.dynamicFont = null;
#endif
		}
		return 0;
	}

	/// 
	/// Retrieve the word directly below the specified world-space position.
	/// 

	public string GetWordAtPosition (Vector3 worldPos) { return GetWordAtCharacterIndex(GetCharacterIndexAtPosition(worldPos)); }

	/// 
	/// Retrieve the word directly below the specified relative-to-label position.
	/// 

	public string GetWordAtPosition (Vector2 localPos) { return GetWordAtCharacterIndex(GetCharacterIndexAtPosition(localPos)); }

	/// 
	/// Retrieve the word right under the specified character index.
	/// 

	public string GetWordAtCharacterIndex (int characterIndex)
	{
		if (characterIndex != -1 && characterIndex < mText.Length)
		{
			int linkStart = mText.LastIndexOf(' ', characterIndex) + 1;
			int linkEnd = mText.IndexOf(' ', characterIndex);
			if (linkEnd == -1) linkEnd = mText.Length;

			if (linkStart != linkEnd)
			{
				int len = linkEnd - linkStart;

				if (len > 0)
				{
					string word = mText.Substring(linkStart, len);
					return NGUIText.StripSymbols(word);
				}
			}
		}
		return null;
	}

	/// 
	/// Retrieve the URL directly below the specified world-space position.
	/// 

	public string GetUrlAtPosition (Vector3 worldPos) { return GetUrlAtCharacterIndex(GetCharacterIndexAtPosition(worldPos)); }

	/// 
	/// Retrieve the URL directly below the specified relative-to-label position.
	/// 

	public string GetUrlAtPosition (Vector2 localPos) { return GetUrlAtCharacterIndex(GetCharacterIndexAtPosition(localPos)); }

	/// 
	/// Retrieve the URL right under the specified character index.
	/// 

	public string GetUrlAtCharacterIndex (int characterIndex)
	{
		if (characterIndex != -1 && characterIndex < mText.Length)
		{
			int linkStart = mText.LastIndexOf("[url=", characterIndex);

			if (linkStart != -1)
			{
				linkStart += 5;
				int linkEnd = mText.IndexOf("]", linkStart);
				if (linkEnd != -1) return mText.Substring(linkStart, linkEnd - linkStart);
			}
		}
		return null;
	}

	/// 
	/// Get the index of the character on the line directly above or below the current index.
	/// 

	public int GetCharacterIndex (int currentIndex, KeyCode key)
	{
		if (isValid)
		{
			string text = processedText;
			if (string.IsNullOrEmpty(text)) return 0;

			int def = defaultFontSize;
			UpdateNGUIText();

			NGUIText.PrintCharacterPositions(text, mTempVerts, mTempIndices);

			if (mTempVerts.size > 0)
			{
				ApplyOffset(mTempVerts, 0);

				for (int i = 0; i < mTempIndices.size; ++i)
				{
					if (mTempIndices[i] == currentIndex)
					{
						// Determine position on the line above or below this character
						Vector2 localPos = mTempVerts[i];

						if (key == KeyCode.UpArrow) localPos.y += def + spacingY;
						else if (key == KeyCode.DownArrow) localPos.y -= def + spacingY;
						else if (key == KeyCode.Home) localPos.x -= 1000f;
						else if (key == KeyCode.End) localPos.x += 1000f;

						// Find the closest character to this position
						int retVal = NGUIText.GetClosestCharacter(mTempVerts, localPos);
						retVal = mTempIndices[retVal];
						if (retVal == currentIndex) break;

						mTempVerts.Clear();
						mTempIndices.Clear();
						return retVal;
					}
				}
				mTempVerts.Clear();
				mTempIndices.Clear();
			}

			NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
			NGUIText.dynamicFont = null;
#endif
			// If the selection doesn't move, then we're at the top or bottom-most line
			if (key == KeyCode.UpArrow || key == KeyCode.Home) return 0;
			if (key == KeyCode.DownArrow || key == KeyCode.End) return text.Length;
		}
		return currentIndex;
	}

	/// 
	/// Fill the specified geometry buffer with vertices that would highlight the current selection.
	/// 

	public void PrintOverlay (int start, int end, UIGeometry caret, UIGeometry highlight, Color caretColor, Color highlightColor)
	{
		if (caret != null) caret.Clear();
		if (highlight != null) highlight.Clear();
		if (!isValid) return;

		string text = processedText;
		UpdateNGUIText();

		int startingCaretVerts = caret.verts.size;
		Vector2 center = new Vector2(0.5f, 0.5f);
		float alpha = finalAlpha;

		// If we have a highlight to work with, fill the buffer
		if (highlight != null && start != end)
		{
			int startingVertices = highlight.verts.size;
			NGUIText.PrintCaretAndSelection(text, start, end, caret.verts, highlight.verts);

			if (highlight.verts.size > startingVertices)
			{
				ApplyOffset(highlight.verts, startingVertices);

				Color32 c = new Color(highlightColor.r, highlightColor.g, highlightColor.b, highlightColor.a * alpha);

				for (int i = startingVertices; i < highlight.verts.size; ++i)
				{
					highlight.uvs.Add(center);
					highlight.cols.Add(c);
				}
			}
		}
		else NGUIText.PrintCaretAndSelection(text, start, end, caret.verts, null);

		// Fill the caret UVs and colors
		ApplyOffset(caret.verts, startingCaretVerts);
		Color32 cc = new Color(caretColor.r, caretColor.g, caretColor.b, caretColor.a * alpha);

		for (int i = startingCaretVerts; i < caret.verts.size; ++i)
		{
			caret.uvs.Add(center);
			caret.cols.Add(cc);
		}

		NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
		NGUIText.dynamicFont = null;
#endif
	}

	/// 
	/// Draw the label.
	/// 

	public override void OnFill (BetterList verts, BetterList uvs, BetterList cols)
	{
		if (!isValid) return;

		int offset = verts.size;
		Color col = color;
		col.a = finalAlpha;
		
		if (mFont != null && mFont.premultipliedAlphaShader) col = NGUITools.ApplyPMA(col);

		string text = processedText;
		int start = verts.size;

		UpdateNGUIText();

		NGUIText.tint = col;
		NGUIText.Print(text, verts, uvs, cols);
		NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
		NGUIText.dynamicFont = null;
#endif
		// Center the content within the label vertically
		Vector2 pos = ApplyOffset(verts, start);

		// Effects don't work with packed fonts
		if (mFont != null && mFont.packedFontShader) return;

		// Apply an effect if one was requested
		if (effectStyle != Effect.None)
		{
			int end = verts.size;
			pos.x = mEffectDistance.x;
			pos.y = mEffectDistance.y;

			ApplyShadow(verts, uvs, cols, offset, end, pos.x, -pos.y);

			if (effectStyle == Effect.Outline)
			{
				offset = end;
				end = verts.size;

				ApplyShadow(verts, uvs, cols, offset, end, -pos.x, pos.y);

				offset = end;
				end = verts.size;

				ApplyShadow(verts, uvs, cols, offset, end, pos.x, pos.y);

				offset = end;
				end = verts.size;

				ApplyShadow(verts, uvs, cols, offset, end, -pos.x, -pos.y);
			}
		}

        if (m_callback!=null)
        {
            m_callback();
        }
	}

    //---------------------------------
    private System.Action m_callback;
    public void SetSymbolOffset(System.Action callback)
    {
        m_callback = callback;
    }
    //--------------------------------

	/// 
	/// Align the vertices, making the label positioned correctly based on the pivot.
	/// Returns the offset that was applied.
	/// 

	protected Vector2 ApplyOffset (BetterList verts, int start)
	{
		Vector2 po = pivotOffset;

		float fx = Mathf.Lerp(0f, -mWidth, po.x);
		float fy = Mathf.Lerp(mHeight, 0f, po.y) + Mathf.Lerp((mCalculatedSize.y - mHeight), 0f, po.y);

		fx = Mathf.Round(fx);
		fy = Mathf.Round(fy);

#if UNITY_FLASH
		for (int i = start; i < verts.size; ++i)
		{
			Vector3 buff = verts.buffer[i];
			buff.x += fx;
			buff.y += fy;
			verts.buffer[i] = buff;
		}
#else
		for (int i = start; i < verts.size; ++i)
		{
			verts.buffer[i].x += fx;
			verts.buffer[i].y += fy;
		}
#endif
		return new Vector2(fx, fy);
	}

	/// 
	/// Apply a shadow effect to the buffer.
	/// 

	void ApplyShadow (BetterList verts, BetterList uvs, BetterList cols, int start, int end, float x, float y)
	{
		Color c = mEffectColor;
		c.a *= finalAlpha;
		Color32 col = (bitmapFont != null && bitmapFont.premultipliedAlphaShader) ? NGUITools.ApplyPMA(c) : c;

		for (int i = start; i < end; ++i)
		{
			verts.Add(verts.buffer[i]);
			uvs.Add(uvs.buffer[i]);
			cols.Add(cols.buffer[i]);

			Vector3 v = verts.buffer[i];
			v.x += x;
			v.y += y;
			verts.buffer[i] = v;

			Color32 uc = cols.buffer[i];

			if (uc.a == 255)
			{
				cols.buffer[i] = col;
			}
			else
			{
				Color fc = c;
				fc.a = (uc.a / 255f * c.a);
				cols.buffer[i] = (bitmapFont != null && bitmapFont.premultipliedAlphaShader) ? NGUITools.ApplyPMA(fc) : fc;
			}
		}
	}

	/// 
	/// Calculate the character index offset necessary in order to print the end of the specified text.
	/// 

	public int CalculateOffsetToFit (string text)
	{
		UpdateNGUIText();
		NGUIText.encoding = false;
		NGUIText.symbolStyle = NGUIText.SymbolStyle.None;
		int offset = NGUIText.CalculateOffsetToFit(text);
		NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
		NGUIText.dynamicFont = null;
#endif
		return offset;
	}

	/// 
	/// Convenience function, in case you wanted to associate progress bar, slider or scroll bar's
	/// OnValueChanged function in inspector with a label.
	/// 

	public void SetCurrentProgress ()
	{
		if (UIProgressBar.current != null)
			text = UIProgressBar.current.value.ToString("F");
	}

	/// 
	/// Convenience function, in case you wanted to associate progress bar, slider or scroll bar's
	/// OnValueChanged function in inspector with a label.
	/// 

	public void SetCurrentPercent ()
	{
		if (UIProgressBar.current != null)
			text = Mathf.RoundToInt(UIProgressBar.current.value * 100f) + "%";
	}

	/// 
	/// Convenience function, in case you wanted to automatically set some label's text
	/// by selecting a value in the UIPopupList.
	/// 

	public void SetCurrentSelection ()
	{
		if (UIPopupList.current != null)
		{
			text = UIPopupList.current.isLocalized ?
				Localization.Get(UIPopupList.current.value) :
				UIPopupList.current.value;
		}
	}

	/// 
	/// Convenience function -- wrap the current text given the label's settings and unlimited height.
	/// 

	public bool Wrap (string text, out string final) { return Wrap(text, out final, 1000000); }

	/// 
	/// Convenience function -- wrap the current text given the label's settings and the given height.
	/// 

	public bool Wrap (string text, out string final, int height)
	{
		UpdateNGUIText();
		NGUIText.rectHeight = height;
		bool retVal = NGUIText.WrapText(text, out final);
		NGUIText.bitmapFont = null;
#if DYNAMIC_FONT
		NGUIText.dynamicFont = null;
#endif
		return retVal;
	}

	/// 
	/// Update NGUIText.current with all the properties from this label.
	/// 

	public void UpdateNGUIText ()
	{
		Font ttf = trueTypeFont;
		bool isDynamic = (ttf != null);

		NGUIText.fontSize = mPrintedSize;
		NGUIText.fontStyle = mFontStyle;
		NGUIText.rectWidth = mWidth;
		NGUIText.rectHeight = mHeight;
		NGUIText.gradient = mApplyGradient && (mFont == null || !mFont.packedFontShader);
		NGUIText.gradientTop = mGradientTop;
		NGUIText.gradientBottom = mGradientBottom;
		NGUIText.encoding = mEncoding;
		NGUIText.premultiply = mPremultiply;
		NGUIText.symbolStyle = mSymbols;
		NGUIText.maxLines = mMaxLineCount;
		NGUIText.spacingX = mSpacingX;
		NGUIText.spacingY = mSpacingY;
		NGUIText.fontScale = isDynamic ? mScale : ((float)mFontSize / mFont.defaultSize) * mScale;

		if (mFont != null)
		{
			NGUIText.bitmapFont = mFont;
			
			for (; ; )
			{
				UIFont fnt = NGUIText.bitmapFont.replacement;
				if (fnt == null) break;
				NGUIText.bitmapFont = fnt;
			}

#if DYNAMIC_FONT
			if (NGUIText.bitmapFont.isDynamic)
			{
				NGUIText.dynamicFont = NGUIText.bitmapFont.dynamicFont;
				NGUIText.bitmapFont = null;
			}
			else NGUIText.dynamicFont = null;
#endif
		}
#if DYNAMIC_FONT
		else
		{
			NGUIText.dynamicFont = ttf;
			NGUIText.bitmapFont = null;
		}

		if (isDynamic && keepCrisp)
		{
			UIRoot rt = root;
			if (rt != null) NGUIText.pixelDensity = (rt != null) ? rt.pixelSizeAdjustment : 1f;
		}
		else NGUIText.pixelDensity = 1f;

		if (mDensity != NGUIText.pixelDensity)
		{
			ProcessText(false, false);
			NGUIText.rectWidth = mWidth;
			NGUIText.rectHeight = mHeight;
		}
#endif

		if (alignment == Alignment.Automatic)
		{
			Pivot p = pivot;

			if (p == Pivot.Left || p == Pivot.TopLeft || p == Pivot.BottomLeft)
			{
				NGUIText.alignment = Alignment.Left;
			}
			else if (p == Pivot.Right || p == Pivot.TopRight || p == Pivot.BottomRight)
			{
				NGUIText.alignment = Alignment.Right;
			}
			else NGUIText.alignment = Alignment.Center;
		}
		else NGUIText.alignment = alignment;

		NGUIText.Update();
	}
}

通过这次的修改,对NGUI的代码的熟悉程度得到了很大的提高,对NGUI如何实现的原理也有了一定的了解,还有就是挺佩服NGUI作者的,代码写得出神入化,膜拜!膜拜!

Demo的源码:http://blog.gamerisker.com/archives/530.html


你可能感兴趣的:(Unity3D)