//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2015 Tasharen Entertainment //---------------------------------------------- #define FUNCELL_MODIFIED using UnityEngine; using System.Collections.Generic; using System; using Alignment = NGUIText.Alignment; using ChineseRotType = NGUIText.ChineseRotType; [ExecuteInEditMode] [AddComponentMenu("NGUI/UI/NGUI Label")] public class UILabel : UIWidget { public enum Effect { None, Shadow, Outline, Outline8, } 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; [HideInInspector] [SerializeField] bool mUseFloatSpacing = false; [HideInInspector] [SerializeField] float mFloatSpacingX = 0; [HideInInspector] [SerializeField] float mFloatSpacingY = 0; [HideInInspector] [SerializeField] bool mOverflowEllipsis = false; // 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 FUNCELL_MODIFIED [HideInInspector] [SerializeField] ChineseRotType chineseRotType = ChineseRotType.None; #endif [System.NonSerialized] Font mActiveTTF = null; [System.NonSerialized] float mDensity = 1f; [System.NonSerialized] bool mShouldBeProcessed = true; [System.NonSerialized] string mProcessedText = null; [System.NonSerialized] bool mPremultiply = false; [System.NonSerialized] Vector2 mCalculatedSize = Vector2.zero; [System.NonSerialized] float mScale = 1f; [System.NonSerialized] int mFinalFontSize = 0; [System.NonSerialized] int mLastWidth = 0; [System.NonSerialized] int mLastHeight = 0; //是否为自动转换多语言 [HideInInspector] [SerializeField] private bool _isStripLanSymbol = false; public bool IsStripLanSymbol { get { return _isStripLanSymbol; } set { if (_isStripLanSymbol != value) { _isStripLanSymbol = value; } } } public delegate void UILabelOnChangeDelegate(UILabel obj); public UILabelOnChangeDelegate TextChangeCallBack { get; set; } /// /// Font size after modifications got taken into consideration such as shrinking content. /// public int finalFontSize { get { if (trueTypeFont) return Mathf.RoundToInt(mScale * mFinalFontSize); return Mathf.RoundToInt(mFinalFontSize * mScale); } } /// /// 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) { RemoveFromPanel(); 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) { SetActiveFont(null); RemoveFromPanel(); mTrueTypeFont = value; shouldBeProcessed = true; mFont = null; SetActiveFont(value); ProcessAndRequest(); if (mActiveTTF != null) base.MarkAsChanged(); } } } /// /// Ambiguous helper function. /// public UnityEngine.Object ambigiousFont { get { return (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(); if(TextChangeCallBack != null) { TextChangeCallBack(this); } } } else if (mText != value) { mText = value; MarkAsChanged(); ProcessAndRequest(); if (TextChangeCallBack != null) { TextChangeCallBack(this); } } 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(); } } } /// /// Whether this label will use float text spacing values, instead of integers. /// public bool useFloatSpacing { get { return mUseFloatSpacing; } set { if (mUseFloatSpacing != value) { mUseFloatSpacing = value; shouldBeProcessed = true; } } } /// /// Additional horizontal spacing between characters when printing text. /// For this to have any effect useFloatSpacing must be true. /// public float floatSpacingX { get { return mFloatSpacingX; } set { if (!Mathf.Approximately(mFloatSpacingX, value)) { mFloatSpacingX = value; MarkAsChanged(); } } } /// /// Additional vertical spacing between lines when printing text. /// For this to have any effect useFloatSpacing must be true. /// public float floatSpacingY { get { return mFloatSpacingY; } set { if (!Mathf.Approximately(mFloatSpacingY, value)) { mFloatSpacingY = value; MarkAsChanged(); } } } /// /// Convenience property to get the used y spacing. /// public float effectiveSpacingY { get { return mUseFloatSpacing ? mFloatSpacingY : mSpacingY; } } /// /// Convenience property to get the used x spacing. /// public float effectiveSpacingX { get { return mUseFloatSpacing ? mFloatSpacingX : mSpacingX; } } /// /// Whether to append "..." at the end of clamped text that didn't fit. /// public bool overflowEllipsis { get { return mOverflowEllipsis; } set { if (mOverflowEllipsis != value) { mOverflowEllipsis = value; MarkAsChanged(); } } } /// /// 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_WP_8_1 || UNITY_BLACKBERRY return (keepCrispWhenShrunk == Crispness.Always); #else return true; #endif } return false; } } /// /// 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. /// bool isValid { get { return mFont != null || mTrueTypeFont != null; } } static Dictionary mList = new Dictionary(); static Dictionary mFontUsage = new Dictionary(); /// /// Register the font texture change listener. /// protected override void OnInit() { base.OnInit(); mList[this.GetHashCode()] = this; SetActiveFont(trueTypeFont); } /// /// Remove the font texture change listener. /// protected override void OnDisable() { SetActiveFont(null); mList.Remove(this.GetHashCode()); base.OnDisable(); } /// /// Set the active font, correctly setting and clearing callbacks. /// protected void SetActiveFont(Font fnt) { if (mActiveTTF != fnt) { Font font = mActiveTTF; if (font != null) { int usage; if (mFontUsage.TryGetValue(font, out usage)) { usage = Mathf.Max(0, --usage); if (usage == 0) { #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 font.textureRebuildCallback = null; #endif mFontUsage.Remove(font); } else mFontUsage[font] = usage; } #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 else font.textureRebuildCallback = null; #endif } mActiveTTF = fnt; font = fnt; if (font != null) { int usage = 0; // Font hasn't been used yet? Register a change delegate callback #if UNITY_4_3 || UNITY_4_5 || UNITY_4_6 || UNITY_4_7 if (!mFontUsage.TryGetValue(font, out usage)) font.textureRebuildCallback = delegate() { OnFontChanged(font); }; #endif #if UNITY_FLASH mFontUsage[font] = usage + 1; #else mFontUsage[font] = ++usage; #endif } } } /// /// 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 OnFontChanged(Font font) { var iter = mList.GetEnumerator(); try { while (iter.MoveNext()) { UILabel lbl = iter.Current.Value; if (lbl != null) { Font fnt = lbl.trueTypeFont; if (fnt == font) { fnt.RequestCharactersInTexture(lbl.mText, lbl.mFinalFontSize, lbl.mFontStyle); lbl.MarkAsChanged(); if (lbl.panel == null) lbl.CreatePanel(); if (mTempDrawcalls == null) mTempDrawcalls = new List(); if (lbl.drawCall != null && !mTempDrawcalls.Contains(lbl.drawCall)) mTempDrawcalls.Add(lbl.drawCall); } } } } finally { iter.Dispose(); } if (mTempDrawcalls != null) { for (int i = 0, imax = mTempDrawcalls.Count; i < imax; ++i) { UIDrawCall dc = mTempDrawcalls[i]; if (dc.panel != null) dc.panel.FillDrawCall(dc); } mTempDrawcalls.Clear(); } } static List mTempDrawcalls; /// /// 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; fontSize = 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 [System.NonSerialized] bool mAllowProcessing = true; [System.NonSerialized] 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; SetActiveFont(null); if (fnt != null) { bitmapFont = fnt; mUsingTTF = false; } else if (ttf != null) { trueTypeFont = ttf; mUsingTTF = true; } shouldBeProcessed = true; mAllowProcessing = true; ProcessAndRequest(); if (autoResizeBoxCollider) ResizeCollider(); } } #endif #if !UNITY_4_3 && !UNITY_4_5 && !UNITY_4_6 && !UNITY_4_7 static bool mTexRebuildAdded = false; protected override void OnEnable() { base.OnEnable(); if (!mTexRebuildAdded) { mTexRebuildAdded = true; Font.textureRebuilt += OnFontChanged; } } #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")); // Request the text within the font ProcessAndRequest(); } /// /// 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; float regionX = mDrawRegion.z - mDrawRegion.x; float regionY = mDrawRegion.w - mDrawRegion.y; NGUIText.rectWidth = legacyMode ? (mMaxLineWidth != 0 ? mMaxLineWidth : 1000000) : width; NGUIText.rectHeight = legacyMode ? (mMaxLineHeight != 0 ? mMaxLineHeight : 1000000) : height; NGUIText.regionWidth = (regionX != 1f) ? Mathf.RoundToInt(NGUIText.rectWidth * regionX) : NGUIText.rectWidth; NGUIText.regionHeight = (regionY != 1f) ? Mathf.RoundToInt(NGUIText.rectHeight * regionY) : NGUIText.rectHeight; mFinalFontSize = Mathf.Abs(legacyMode ? Mathf.RoundToInt(cachedTransform.localScale.x) : defaultFontSize); mScale = 1f; if (NGUIText.regionWidth < 1 || NGUIText.regionHeight < 0) { mProcessedText = ""; return; } bool isDynamic = (trueTypeFont != null); if (isDynamic && keepCrisp) { UIRoot rt = root; if (rt != null) mDensity = (rt != null) ? rt.pixelSizeAdjustment : 1f; } else mDensity = 1f; if (full) UpdateNGUIText(); if (mOverflow == Overflow.ResizeFreely) { NGUIText.rectWidth = 1000000; NGUIText.regionWidth = 1000000; } if (mOverflow == Overflow.ResizeFreely || mOverflow == Overflow.ResizeHeight) { NGUIText.rectHeight = 1000000; NGUIText.regionHeight = 1000000; } if (mFinalFontSize > 0) { bool adjustSize = keepCrisp; if (_isStripLanSymbol) mText = NGUIText.StripLanSymbol(mText); for (int ps = mFinalFontSize; ps > 0; --ps) { // Adjust either the size, or the scale if (adjustSize) { mFinalFontSize = ps; NGUIText.fontSize = mFinalFontSize; } else { mScale = (float)ps / mFinalFontSize; NGUIText.fontScale = isDynamic ? mScale : ((float)mFontSize / mFont.defaultSize) * mScale; } NGUIText.Update(false); // Wrap the text bool fits = NGUIText.WrapText(mText, out mProcessedText, true, false, mOverflowEllipsis && mOverflow == Overflow.ClampContent); 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)); if (regionX != 1f) mWidth = Mathf.RoundToInt(mWidth / regionX); mHeight = Mathf.Max(minHeight, Mathf.RoundToInt(mCalculatedSize.y)); if (regionY != 1f) mHeight = Mathf.RoundToInt(mHeight / regionY); 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 (regionY != 1f) mHeight = Mathf.RoundToInt(mHeight / regionY); 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; NGUIText.dynamicFont = null; } } /// /// 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); if ((minX & 1) == 1) ++minX; if ((minY & 1) == 1) ++minY; 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, false); } [System.Obsolete("Use UILabel.GetCharacterAtPosition instead")] public int GetCharacterIndex(Vector2 localPos) { return GetCharacterIndexAtPosition(localPos, false); } 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, bool precise) { Vector2 localPos = cachedTransform.InverseTransformPoint(worldPos); return GetCharacterIndexAtPosition(localPos, precise); } /// /// Return the index of the character at the specified local position. /// public int GetCharacterIndexAtPosition(Vector2 localPos, bool precise) { if (isValid) { string text = processedText; if (string.IsNullOrEmpty(text)) return 0; UpdateNGUIText(); if (precise) NGUIText.PrintExactCharacterPositions(text, mTempVerts, mTempIndices); else NGUIText.PrintApproximateCharacterPositions(text, mTempVerts, mTempIndices); if (mTempVerts.size > 0) { ApplyOffset(mTempVerts, 0); int retVal = precise ? NGUIText.GetExactCharacterIndex(mTempVerts, mTempIndices, localPos) : NGUIText.GetApproximateCharacterIndex(mTempVerts, mTempIndices, localPos); mTempVerts.Clear(); mTempIndices.Clear(); NGUIText.bitmapFont = null; NGUIText.dynamicFont = null; return retVal; } NGUIText.bitmapFont = null; NGUIText.dynamicFont = null; } return 0; } /// /// Retrieve the word directly below the specified world-space position. /// public string GetWordAtPosition(Vector3 worldPos) { int index = GetCharacterIndexAtPosition(worldPos, true); return GetWordAtCharacterIndex(index); } /// /// Retrieve the word directly below the specified relative-to-label position. /// public string GetWordAtPosition(Vector2 localPos) { int index = GetCharacterIndexAtPosition(localPos, true); return GetWordAtCharacterIndex(index); } /// /// Retrieve the word right under the specified character index. /// public string GetWordAtCharacterIndex(int characterIndex) { if (characterIndex != -1 && characterIndex < mText.Length) { #if UNITY_FLASH int wordStart = LastIndexOfAny(mText, new char[] { ' ', '\n' }, characterIndex) + 1; int wordEnd = IndexOfAny(mText, new char[] { ' ', '\n', ',', '.' }, characterIndex); #else int wordStart = mText.LastIndexOfAny(new char[] { ' ', '\n' }, characterIndex) + 1; int wordEnd = mText.IndexOfAny(new char[] { ' ', '\n', ',', '.' }, characterIndex); #endif if (wordEnd == -1) wordEnd = mText.Length; if (wordStart != wordEnd) { int len = wordEnd - wordStart; if (len > 0) { string word = mText.Substring(wordStart, len); return NGUIText.StripSymbols(word); } } } return null; } #if UNITY_FLASH /// /// Flash is fail IRL: http://www.tasharen.com/forum/index.php?topic=11390.0 /// int LastIndexOfAny (string input, char[] any, int start) { if (start >= 0 && input.Length > 0 && any.Length > 0 && start < input.Length) { for (int w = start; w >= 0; w--) { for (int r = 0; r < any.Length; r++) { if (any[r] == input[w]) { return w; } } } } return -1; } /// /// Flash is fail IRL: http://www.tasharen.com/forum/index.php?topic=11390.0 /// int IndexOfAny (string input, char[] any, int start) { if (start >= 0 && input.Length > 0 && any.Length > 0 && start < input.Length) { for (int w = start; w < input.Length; w++) { for (int r = 0; r < any.Length; r++) { if (any[r] == input[w]) { return w; } } } } return -1; } #endif /// /// Retrieve the URL directly below the specified world-space position. /// public string GetUrlAtPosition(Vector3 worldPos) { return GetUrlAtCharacterIndex(GetCharacterIndexAtPosition(worldPos, true)); } /// /// Retrieve the URL directly below the specified relative-to-label position. /// public string GetUrlAtPosition(Vector2 localPos) { return GetUrlAtCharacterIndex(GetCharacterIndexAtPosition(localPos, true)); } /// /// Retrieve the URL right under the specified character index. /// public string GetUrlAtCharacterIndex(int characterIndex) { if (characterIndex != -1 && characterIndex < mText.Length - 6) { int linkStart; // LastIndexOf() fails if the string happens to begin with the expected text if (mText[characterIndex] == '[' && mText[characterIndex + 1] == 'u' && mText[characterIndex + 2] == 'r' && mText[characterIndex + 3] == 'l' && mText[characterIndex + 4] == '=') { linkStart = characterIndex; } else linkStart = mText.LastIndexOf("[url=", characterIndex); if (linkStart == -1) return null; linkStart += 5; int linkEnd = mText.IndexOf("]", linkStart); if (linkEnd == -1) return null; int urlEnd = mText.IndexOf("[/url]", linkEnd); if (urlEnd == -1 || characterIndex <= urlEnd) 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.PrintApproximateCharacterPositions(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 + effectiveSpacingY; else if (key == KeyCode.DownArrow) localPos.y -= def + effectiveSpacingY; 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.GetApproximateCharacterIndex(mTempVerts, mTempIndices, localPos); if (retVal == currentIndex) break; mTempVerts.Clear(); mTempIndices.Clear(); return retVal; } } mTempVerts.Clear(); mTempIndices.Clear(); } NGUIText.bitmapFont = null; NGUIText.dynamicFont = null; // 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.CheckCacke(4); caret.Clear(); } if (highlight != null) { highlight.CheckCacke(4); 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; float alpha = 1f; // 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; NGUIText.dynamicFont = null; } /// /// Draw the label. /// //优化NGUI GC public static BetterList vertsForOnFill = new BetterList(); public static BetterList uvsForOnFill = new BetterList(); public static BetterList colsForOnFill = new BetterList(); //优化NGUI GC public override void OnFill(BetterList verts, BetterList uvs, BetterList cols) { if (!isValid) return; int offset = verts != null ? verts.size : 0; Color col = color; //col.a = finalAlpha; col.a = 1f; if (mFont != null && mFont.premultipliedAlphaShader) col = NGUITools.ApplyPMA(col); if (QualitySettings.activeColorSpace == ColorSpace.Linear) { col.r = Mathf.Pow(col.r, 2.2f); col.g = Mathf.Pow(col.g, 2.2f); col.b = Mathf.Pow(col.b, 2.2f); } string text = processedText; int start = verts != null ? verts.size : 0; UpdateNGUIText(); NGUIText.tint = col; //优化NGUI GC vertsForOnFill.Clear(); uvsForOnFill.Clear(); colsForOnFill.Clear(); #if FUNCELL_MODIFIED NGUIText.Print(text, vertsForOnFill, uvsForOnFill, colsForOnFill, chineseRotType); #else NGUIText.Print(text, vertsForOnFill, uvsForOnFill, colsForOnFill); #endif var diff = effectStyle == Effect.None ? 0 : vertsForOnFill.size - offset; if (effectStyle == Effect.Outline) { diff += 3 * diff; } diff += vertsForOnFill.size; geometry.CheckCacke(diff); verts = geometry.verts; uvs = geometry.uvs; cols = geometry.cols; verts.AddRange(vertsForOnFill); uvs.AddRange(uvsForOnFill); cols.AddRange(colsForOnFill); //#else // NGUIText.Print(text, verts, uvs, cols); //优化NGUI GC 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; var premultipliedAlpha = (bitmapFont != null && bitmapFont.premultipliedAlphaShader); ApplyShadow(verts, uvs, cols, offset, end, pos.x, -pos.y, premultipliedAlpha); if (effectStyle == Effect.Outline) { offset = end; end = verts.size; ApplyShadow(verts, uvs, cols, offset, end, -pos.x, pos.y, premultipliedAlpha); offset = end; end = verts.size; ApplyShadow(verts, uvs, cols, offset, end, pos.x, pos.y, premultipliedAlpha); offset = end; end = verts.size; ApplyShadow(verts, uvs, cols, offset, end, -pos.x, -pos.y, premultipliedAlpha); } } if (onPostFill != null) onPostFill(this, offset, verts, uvs, cols); } /// /// Align the vertices, making the label positioned correctly based on the pivot. /// Returns the offset that was applied. /// public 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. /// private void ApplyShadow(BetterList verts, BetterList uvs, BetterList cols, int start, int end, float x, float y, bool premultipliedAlpha) { Color c = mEffectColor; //c.a *= finalAlpha; Color32 col = premultipliedAlpha ? NGUITools.ApplyPMA(c) : c; var verBuffer = verts.buffer; var uvBuffer = uvs.buffer; var colBuffer = cols.buffer; Color fc = c; Color32 fc32 = c; for (int i = start; i < end; ++i) { verts.Add(verBuffer[i]); uvs.Add(uvBuffer[i]); cols.Add(colBuffer[i]); Vector3 v = verBuffer[i]; v.x += x; v.y += y; verBuffer[i] = v; var uca = colBuffer[i].a; if (uca == 255) { colBuffer[i] = col; } else { if(premultipliedAlpha) { fc.a = (uca / 255f * c.a); colBuffer[i] = NGUITools.ApplyPMA(fc); } else { fc32.a = (byte)(uca * c.a); colBuffer[i] = fc32; } } } } /// /// 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; NGUIText.dynamicFont = null; 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; NGUIText.regionHeight = height; bool retVal = NGUIText.WrapText(text, out final); NGUIText.bitmapFont = null; NGUIText.dynamicFont = null; return retVal; } /// /// Update NGUIText.current with all the properties from this label. /// public void UpdateNGUIText() { Font ttf = trueTypeFont; bool isDynamic = (ttf != null); NGUIText.fontSize = mFinalFontSize; NGUIText.fontStyle = mFontStyle; NGUIText.rectWidth = mWidth; NGUIText.rectHeight = mHeight; NGUIText.regionWidth = Mathf.RoundToInt(mWidth * (mDrawRegion.z - mDrawRegion.x)); NGUIText.regionHeight = Mathf.RoundToInt(mHeight * (mDrawRegion.w - mDrawRegion.y)); 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; #if FUNCELL_MODIFIED var isTh = false; var isVie = false; var isEastern = false; for (int i = 0; i < mText.Length; ++i) { var ch = mText[i]; if (ch >= '\x0E00' && ch <= '\x0E7F') { isTh = true; break; } if (ch > 12287) { isEastern = true; } if (ch >= '\x0102' && ch <= '\x1EF1') { isVie = true; isEastern = false; break; } } if (isTh) { NGUIText.spacingX = 0; NGUIText.isEastern = true; } else { NGUIText.isVie = isVie; NGUIText.isEastern = isEastern; NGUIText.spacingX = effectiveSpacingX; } #else NGUIText.spacingX = effectiveSpacingX; #endif NGUIText.spacingY = effectiveSpacingY; 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 (NGUIText.bitmapFont.isDynamic) { NGUIText.dynamicFont = NGUIText.bitmapFont.dynamicFont; NGUIText.bitmapFont = null; } else NGUIText.dynamicFont = null; } 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; NGUIText.regionWidth = Mathf.RoundToInt(mWidth * (mDrawRegion.z - mDrawRegion.x)); NGUIText.regionHeight = Mathf.RoundToInt(mHeight * (mDrawRegion.w - mDrawRegion.y)); } 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(); } void OnApplicationPause(bool paused) { if (!paused && mTrueTypeFont != null) Invalidate(false); } }