Files
tianrunCRM/Assets/CoolapeFrame/3rd/NGUI_Enhance/Scripts/Internal/UIWidget.cs
2020-07-11 12:36:54 +08:00

1675 lines
40 KiB
C#

//----------------------------------------------
// NGUI: Next-Gen UI kit
// Copyright © 2011-2015 Tasharen Entertainment
//----------------------------------------------
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// Base class for all UI components that should be derived from when creating new widget types.
/// </summary>
[ExecuteInEditMode]
[AddComponentMenu("NGUI/UI/NGUI Widget")]
public class UIWidget : UIRect
{
public enum Pivot
{
TopLeft,
Top,
TopRight,
Left,
Center,
Right,
BottomLeft,
Bottom,
BottomRight,
}
// Cached and saved values
[HideInInspector][SerializeField] protected Color mColor = Color.white;
[HideInInspector][SerializeField] protected Pivot mPivot = Pivot.Center;
[HideInInspector][SerializeField] protected int mWidth = 100;
[HideInInspector][SerializeField] protected int mHeight = 100;
[HideInInspector][SerializeField] protected int mDepth = 0;
public delegate void OnDimensionsChanged ();
public delegate void OnPostFillCallback (UIWidget widget, int bufferOffset, BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols);
/// <summary>
/// Notification triggered when the widget's dimensions or position changes.
/// </summary>
public OnDimensionsChanged onChange;
/// <summary>
/// Notification triggered after the widget's buffer has been filled.
/// </summary>
public OnPostFillCallback onPostFill;
/// <summary>
/// Callback triggered when the widget is about to be renderered (OnWillRenderObject).
/// NOTE: This property is only exposed for the sake of speed to avoid property execution.
/// In most cases you will want to use UIWidget.onRender instead.
/// </summary>
public UIDrawCall.OnRenderCallback mOnRender;
/// <summary>
/// Set the callback that will be triggered when the widget is being rendered (OnWillRenderObject).
/// This is where you would set material properties and shader values.
/// </summary>
public UIDrawCall.OnRenderCallback onRender
{
get
{
return mOnRender;
}
set
{
#if UNITY_FLASH
if (!(mOnRender == value))
#else
if (mOnRender != value)
#endif
{
#if !UNITY_FLASH
if (drawCall != null && drawCall.onRender != null && mOnRender != null)
drawCall.onRender -= mOnRender;
#endif
mOnRender = value;
if (drawCall != null) drawCall.onRender += value;
}
}
}
/// <summary>
/// If set to 'true', the box collider's dimensions will be adjusted to always match the widget whenever it resizes.
/// </summary>
public bool autoResizeBoxCollider = false;
/// <summary>
/// Hide the widget if it happens to be off-screen.
/// </summary>
public bool hideIfOffScreen = false;
public enum AspectRatioSource
{
Free,
BasedOnWidth,
BasedOnHeight,
}
/// <summary>
/// Whether the rectangle will attempt to maintain a specific aspect ratio.
/// </summary>
public AspectRatioSource keepAspectRatio = AspectRatioSource.Free;
/// <summary>
/// If you want the anchored rectangle to keep a specific aspect ratio, set this value.
/// </summary>
public float aspectRatio = 1f;
public delegate bool HitCheck (Vector3 worldPos);
/// <summary>
/// Custom hit check function. If set, all hit checks (including events) will call this function,
/// passing the world position. Return 'true' if it's within the bounds of your choice, 'false' otherwise.
/// </summary>
public HitCheck hitCheck;
/// <summary>
/// Panel that's managing this widget.
/// </summary>
[System.NonSerialized] public UIPanel panel;
/// <summary>
/// Widget's generated geometry.
/// </summary>
[System.NonSerialized] public UIGeometry geometry = new UIGeometry();
/// <summary>
/// If set to 'false', the widget's OnFill function will not be called, letting you define custom geometry at will.
/// </summary>
[System.NonSerialized] public bool fillGeometry = true;
[System.NonSerialized] protected bool mPlayMode = true;
[System.NonSerialized] protected Vector4 mDrawRegion = new Vector4(0f, 0f, 1f, 1f);
[System.NonSerialized] Matrix4x4 mLocalToPanel;
[System.NonSerialized] bool mIsVisibleByAlpha = true;
[System.NonSerialized] bool mIsVisibleByPanel = true;
[System.NonSerialized] bool mIsInFront = true;
[System.NonSerialized] float mLastAlpha = 0f;
[System.NonSerialized] bool mMoved = false;
/// <summary>
/// Internal usage -- draw call that's drawing the widget.
/// </summary>
[System.NonSerialized] public UIDrawCall drawCall;
[System.NonSerialized] protected Vector3[] mCorners = new Vector3[4];
/// <summary>
/// Draw region alters how the widget looks without modifying the widget's rectangle.
/// A region is made up of 4 relative values (0-1 range). The order is Left (X), Bottom (Y), Right (Z) and Top (W).
/// To have a widget's left edge be 30% from the left side, set X to 0.3. To have the widget's right edge be 30%
/// from the right hand side, set Z to 0.7.
/// </summary>
public Vector4 drawRegion
{
get
{
return mDrawRegion;
}
set
{
if (mDrawRegion != value)
{
mDrawRegion = value;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
}
/// <summary>
/// Pivot offset in relative coordinates. Bottom-left is (0, 0). Top-right is (1, 1).
/// </summary>
public Vector2 pivotOffset { get { return NGUIMath.GetPivotOffset(pivot); } }
/// <summary>
/// Widget's width in pixels.
/// </summary>
public int width
{
get
{
return mWidth;
}
set
{
int min = minWidth;
if (value < min) value = min;
if (mWidth != value && keepAspectRatio != AspectRatioSource.BasedOnHeight)
{
if (isAnchoredHorizontally)
{
if (leftAnchor.target != null && rightAnchor.target != null)
{
if (mPivot == Pivot.BottomLeft || mPivot == Pivot.Left || mPivot == Pivot.TopLeft)
{
NGUIMath.AdjustWidget(this, 0f, 0f, value - mWidth, 0f);
}
else if (mPivot == Pivot.BottomRight || mPivot == Pivot.Right || mPivot == Pivot.TopRight)
{
NGUIMath.AdjustWidget(this, mWidth - value, 0f, 0f, 0f);
}
else
{
int diff = value - mWidth;
diff = diff - (diff & 1);
if (diff != 0) NGUIMath.AdjustWidget(this, -diff * 0.5f, 0f, diff * 0.5f, 0f);
}
}
else if (leftAnchor.target != null)
{
NGUIMath.AdjustWidget(this, 0f, 0f, value - mWidth, 0f);
}
else NGUIMath.AdjustWidget(this, mWidth - value, 0f, 0f, 0f);
}
else SetDimensions(value, mHeight);
}
}
}
/// <summary>
/// Widget's height in pixels.
/// </summary>
public int height
{
get
{
return mHeight;
}
set
{
int min = minHeight;
if (value < min) value = min;
if (mHeight != value && keepAspectRatio != AspectRatioSource.BasedOnWidth)
{
if (isAnchoredVertically)
{
if (bottomAnchor.target != null && topAnchor.target != null)
{
if (mPivot == Pivot.BottomLeft || mPivot == Pivot.Bottom || mPivot == Pivot.BottomRight)
{
NGUIMath.AdjustWidget(this, 0f, 0f, 0f, value - mHeight);
}
else if (mPivot == Pivot.TopLeft || mPivot == Pivot.Top || mPivot == Pivot.TopRight)
{
NGUIMath.AdjustWidget(this, 0f, mHeight - value, 0f, 0f);
}
else
{
int diff = value - mHeight;
diff = diff - (diff & 1);
if (diff != 0) NGUIMath.AdjustWidget(this, 0f, -diff * 0.5f, 0f, diff * 0.5f);
}
}
else if (bottomAnchor.target != null)
{
NGUIMath.AdjustWidget(this, 0f, 0f, 0f, value - mHeight);
}
else NGUIMath.AdjustWidget(this, 0f, mHeight - value, 0f, 0f);
}
else SetDimensions(mWidth, value);
}
}
}
/// <summary>
/// Color used by the widget.
/// </summary>
public Color color
{
get
{
return mColor;
}
set
{
if (mColor != value)
{
bool alphaChange = (mColor.a != value.a);
mColor = value;
Invalidate(alphaChange);
}
}
}
/// <summary>
/// Widget's alpha -- a convenience method.
/// </summary>
public override float alpha
{
get
{
return mColor.a;
}
set
{
if (mColor.a != value)
{
mColor.a = value;
Invalidate(true);
}
}
}
/// <summary>
/// Whether the widget is currently visible.
/// </summary>
public bool isVisible { get { return mIsVisibleByPanel && mIsVisibleByAlpha && mIsInFront && finalAlpha > 0.001f && NGUITools.GetActive(this); } }
/// <summary>
/// Whether the widget has vertices to draw.
/// </summary>
public bool hasVertices { get { return geometry != null && geometry.hasVertices; } }
/// <summary>
/// Change the pivot point and do not attempt to keep the widget in the same place by adjusting its transform.
/// </summary>
public Pivot rawPivot
{
get
{
return mPivot;
}
set
{
if (mPivot != value)
{
mPivot = value;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
}
/// <summary>
/// Set or get the value that specifies where the widget's pivot point should be.
/// </summary>
public Pivot pivot
{
get
{
return mPivot;
}
set
{
if (mPivot != value)
{
Vector3 before = worldCorners[0];
mPivot = value;
mChanged = true;
Vector3 after = worldCorners[0];
Transform t = cachedTransform;
Vector3 pos = t.position;
float z = t.localPosition.z;
pos.x += (before.x - after.x);
pos.y += (before.y - after.y);
cachedTransform.position = pos;
pos = cachedTransform.localPosition;
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
pos.z = z;
cachedTransform.localPosition = pos;
}
}
}
/// <summary>
/// Depth controls the rendering order -- lowest to highest.
/// </summary>
public int depth
{
get
{
// Experiment with a transform-based depth, uGUI style
//if (mDepth == int.MinValue)
//{
// int val = cachedTransform.GetSiblingIndex();
// UIWidget pt = parent as UIWidget;
// if (pt != null) val += pt.depth;
// return val;
//}
return mDepth;
}
set
{
if (mDepth != value)
{
if (panel != null) panel.RemoveWidget(this);
mDepth = value;
if (panel != null)
{
// if (material != null) { // add by chenbin
panel.AddWidget (this);
// }
if (!Application.isPlaying)
{
panel.SortWidgets();
panel.RebuildAllDrawCalls();
}
}
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
}
}
/// <summary>
/// Raycast depth order on widgets takes the depth of their panel into consideration.
/// This functionality is used to determine the "final" depth of the widget for drawing and raycasts.
/// </summary>
public int raycastDepth
{
get
{
if (panel == null) CreatePanel();
return (panel != null) ? mDepth + panel.depth * 1000 : mDepth;
}
}
/// <summary>
/// Local space corners of the widget. The order is bottom-left, top-left, top-right, bottom-right.
/// </summary>
public override Vector3[] localCorners
{
get
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
mCorners[0] = new Vector3(x0, y0);
mCorners[1] = new Vector3(x0, y1);
mCorners[2] = new Vector3(x1, y1);
mCorners[3] = new Vector3(x1, y0);
return mCorners;
}
}
/// <summary>
/// Local width and height of the widget in pixels.
/// </summary>
public virtual Vector2 localSize
{
get
{
Vector3[] cr = localCorners;
return cr[2] - cr[0];
}
}
/// <summary>
/// Widget's center in local coordinates. Don't forget to transform by the widget's transform.
/// </summary>
public Vector3 localCenter
{
get
{
Vector3[] cr = localCorners;
return Vector3.Lerp(cr[0], cr[2], 0.5f);
}
}
/// <summary>
/// World-space corners of the widget. The order is bottom-left, top-left, top-right, bottom-right.
/// </summary>
public override Vector3[] worldCorners
{
get
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
Transform wt = cachedTransform;
mCorners[0] = wt.TransformPoint(x0, y0, 0f);
mCorners[1] = wt.TransformPoint(x0, y1, 0f);
mCorners[2] = wt.TransformPoint(x1, y1, 0f);
mCorners[3] = wt.TransformPoint(x1, y0, 0f);
return mCorners;
}
}
/// <summary>
/// World-space center of the widget.
/// </summary>
public Vector3 worldCenter { get { return cachedTransform.TransformPoint(localCenter); } }
/// <summary>
/// Local space region where the actual drawing will take place.
/// X = left, Y = bottom, Z = right, W = top.
/// </summary>
public virtual Vector4 drawingDimensions
{
get
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
return new Vector4(
mDrawRegion.x == 0f ? x0 : Mathf.Lerp(x0, x1, mDrawRegion.x),
mDrawRegion.y == 0f ? y0 : Mathf.Lerp(y0, y1, mDrawRegion.y),
mDrawRegion.z == 1f ? x1 : Mathf.Lerp(x0, x1, mDrawRegion.z),
mDrawRegion.w == 1f ? y1 : Mathf.Lerp(y0, y1, mDrawRegion.w));
}
}
/// <summary>
/// Material used by the widget.
/// </summary>
public virtual Material material
{
get
{
return null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no material setter");
}
}
/// <summary>
/// Texture used by the widget.
/// </summary>
public virtual Texture mainTexture
{
get
{
Material mat = material;
return (mat != null) ? mat.mainTexture : null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no mainTexture setter");
}
}
/// <summary>
/// Shader is used to create a dynamic material if the widget has no material to work with.
/// </summary>
public virtual Shader shader
{
get
{
Material mat = material;
return (mat != null) ? mat.shader : null;
}
set
{
throw new System.NotImplementedException(GetType() + " has no shader setter");
}
}
/// <summary>
/// Do not use this, it's obsolete.
/// </summary>
[System.Obsolete("There is no relative scale anymore. Widgets now have width and height instead")]
public Vector2 relativeSize { get { return Vector2.one; } }
/// <summary>
/// Convenience function that returns 'true' if the widget has a box collider.
/// </summary>
public bool hasBoxCollider
{
get
{
#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
BoxCollider box = collider as BoxCollider;
#else
BoxCollider box = GetComponent<Collider>() as BoxCollider;
#endif
if (box != null) return true;
return GetComponent<BoxCollider2D>() != null;
}
}
/// <summary>
/// Adjust the widget's dimensions without going through the anchor validation logic.
/// </summary>
public void SetDimensions (int w, int h)
{
if (mWidth != w || mHeight != h)
{
mWidth = w;
mHeight = h;
if (keepAspectRatio == AspectRatioSource.BasedOnWidth)
mHeight = Mathf.RoundToInt(mWidth / aspectRatio);
else if (keepAspectRatio == AspectRatioSource.BasedOnHeight)
mWidth = Mathf.RoundToInt(mHeight * aspectRatio);
else if (keepAspectRatio == AspectRatioSource.Free)
aspectRatio = mWidth / (float)mHeight;
mMoved = true;
if (autoResizeBoxCollider) ResizeCollider();
MarkAsChanged();
}
}
/// <summary>
/// Get the sides of the rectangle relative to the specified transform.
/// The order is left, top, right, bottom.
/// </summary>
public override Vector3[] GetSides (Transform relativeTo)
{
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
float cx = (x0 + x1) * 0.5f;
float cy = (y0 + y1) * 0.5f;
Transform trans = cachedTransform;
mCorners[0] = trans.TransformPoint(x0, cy, 0f);
mCorners[1] = trans.TransformPoint(cx, y1, 0f);
mCorners[2] = trans.TransformPoint(x1, cy, 0f);
mCorners[3] = trans.TransformPoint(cx, y0, 0f);
if (relativeTo != null)
{
for (int i = 0; i < 4; ++i)
mCorners[i] = relativeTo.InverseTransformPoint(mCorners[i]);
}
return mCorners;
}
[System.NonSerialized] int mAlphaFrameID = -1;
/// <summary>
/// Widget's final alpha, after taking the panel's alpha into account.
/// </summary>
public override float CalculateFinalAlpha (int frameID)
{
#if UNITY_EDITOR
if (mAlphaFrameID != frameID || !Application.isPlaying)
#else
if (mAlphaFrameID != frameID)
#endif
{
mAlphaFrameID = frameID;
UpdateFinalAlpha(frameID);
}
return finalAlpha;
}
/// <summary>
/// Force-calculate the final alpha value.
/// </summary>
protected void UpdateFinalAlpha (int frameID)
{
if (!mIsVisibleByAlpha || !mIsInFront)
{
finalAlpha = 0f;
}
else
{
UIRect pt = parent;
finalAlpha = (pt != null) ? pt.CalculateFinalAlpha(frameID) * mColor.a : mColor.a;
}
}
/// <summary>
/// Update the widget's visibility and final alpha.
/// </summary>
public override void Invalidate (bool includeChildren)
{
mChanged = true;
mAlphaFrameID = -1;
if (panel != null)
{
bool vis = (hideIfOffScreen || panel.hasCumulativeClipping) ? panel.IsVisible(this) : true;
UpdateVisibility(CalculateCumulativeAlpha(Time.frameCount) > 0.001f, vis);
UpdateFinalAlpha(Time.frameCount);
if (includeChildren) base.Invalidate(true);
}
}
/// <summary>
/// Same as final alpha, except it doesn't take own visibility into consideration. Used by panels.
/// </summary>
public float CalculateCumulativeAlpha (int frameID)
{
UIRect pt = parent;
return (pt != null) ? pt.CalculateFinalAlpha(frameID) * mColor.a : mColor.a;
}
/// <summary>
/// Set the widget's rectangle.
/// </summary>
public override void SetRect (float x, float y, float width, float height)
{
Vector2 po = pivotOffset;
float fx = Mathf.Lerp(x, x + width, po.x);
float fy = Mathf.Lerp(y, y + height, po.y);
int finalWidth = Mathf.FloorToInt(width + 0.5f);
int finalHeight = Mathf.FloorToInt(height + 0.5f);
if (po.x == 0.5f) finalWidth = ((finalWidth >> 1) << 1);
if (po.y == 0.5f) finalHeight = ((finalHeight >> 1) << 1);
Transform t = cachedTransform;
Vector3 pos = t.localPosition;
pos.x = Mathf.Floor(fx + 0.5f);
pos.y = Mathf.Floor(fy + 0.5f);
if (finalWidth < minWidth) finalWidth = minWidth;
if (finalHeight < minHeight) finalHeight = minHeight;
t.localPosition = pos;
this.width = finalWidth;
this.height = finalHeight;
if (isAnchored)
{
t = t.parent;
if (leftAnchor.target) leftAnchor.SetHorizontal(t, x);
if (rightAnchor.target) rightAnchor.SetHorizontal(t, x + width);
if (bottomAnchor.target) bottomAnchor.SetVertical(t, y);
if (topAnchor.target) topAnchor.SetVertical(t, y + height);
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
}
/// <summary>
/// Adjust the widget's collider size to match the widget's dimensions.
/// </summary>
public void ResizeCollider () { if (NGUITools.GetActive(this)) NGUITools.UpdateWidgetCollider(gameObject); }
/// <summary>
/// Static widget comparison function used for depth sorting.
/// </summary>
[System.Diagnostics.DebuggerHidden]
[System.Diagnostics.DebuggerStepThrough]
static public int FullCompareFunc (UIWidget left, UIWidget right)
{
int val = UIPanel.CompareFunc(left.panel, right.panel);
return (val == 0) ? PanelCompareFunc(left, right) : val;
}
/// <summary>
/// Static widget comparison function used for inter-panel depth sorting.
/// </summary>
[System.Diagnostics.DebuggerHidden]
[System.Diagnostics.DebuggerStepThrough]
static public int PanelCompareFunc (UIWidget left, UIWidget right)
{
if (left.mDepth < right.mDepth) return -1;
if (left.mDepth > right.mDepth) return 1;
Material leftMat = left.material;
Material rightMat = right.material;
if (leftMat == rightMat) return 0;
if (leftMat != null) return -1;
if (rightMat != null) return 1;
return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;
}
/// <summary>
/// Calculate the widget's bounds, optionally making them relative to the specified transform.
/// </summary>
public Bounds CalculateBounds () { return CalculateBounds(null); }
/// <summary>
/// Calculate the widget's bounds, optionally making them relative to the specified transform.
/// </summary>
public Bounds CalculateBounds (Transform relativeParent)
{
if (relativeParent == null)
{
Vector3[] corners = localCorners;
Bounds b = new Bounds(corners[0], Vector3.zero);
for (int j = 1; j < 4; ++j) b.Encapsulate(corners[j]);
return b;
}
else
{
Matrix4x4 toLocal = relativeParent.worldToLocalMatrix;
Vector3[] corners = worldCorners;
Bounds b = new Bounds(toLocal.MultiplyPoint3x4(corners[0]), Vector3.zero);
for (int j = 1; j < 4; ++j) b.Encapsulate(toLocal.MultiplyPoint3x4(corners[j]));
return b;
}
}
/// <summary>
/// Mark the widget as changed so that the geometry can be rebuilt.
/// </summary>
public void SetDirty ()
{
if (drawCall != null)
{
drawCall.isDirty = true;
}
else if (isVisible && hasVertices)
{
CreatePanel();
}
}
/// <summary>
/// Remove this widget from the panel.
/// </summary>
public void RemoveFromPanel ()
{
if (panel != null)
{
panel.RemoveWidget(this);
panel = null;
}
drawCall = null;
#if UNITY_EDITOR
mOldTex = null;
mOldShader = null;
#endif
}
#if UNITY_EDITOR
[System.NonSerialized] Texture mOldTex;
[System.NonSerialized] Shader mOldShader;
/// <summary>
/// This callback is sent inside the editor notifying us that some property has changed.
/// </summary>
protected override void OnValidate()
{
if (NGUITools.GetActive(this))
{
base.OnValidate();
// Prior to NGUI 2.7.0 width and height was specified as transform's local scale
if ((mWidth == 100 || mWidth == minWidth) &&
(mHeight == 100 || mHeight == minHeight) && cachedTransform.localScale.magnitude > 8f)
{
UpgradeFrom265();
cachedTransform.localScale = Vector3.one;
}
if (mWidth < minWidth) mWidth = minWidth;
if (mHeight < minHeight) mHeight = minHeight;
if (autoResizeBoxCollider) ResizeCollider();
// If the texture is changing, we need to make sure to rebuild the draw calls
if (mOldTex != mainTexture || mOldShader != shader)
{
mOldTex = mainTexture;
mOldShader = shader;
}
aspectRatio = (keepAspectRatio == AspectRatioSource.Free) ?
(float)mWidth / mHeight : Mathf.Max(0.01f, aspectRatio);
if (keepAspectRatio == AspectRatioSource.BasedOnHeight)
{
mWidth = Mathf.RoundToInt(mHeight * aspectRatio);
}
else if (keepAspectRatio == AspectRatioSource.BasedOnWidth)
{
mHeight = Mathf.RoundToInt(mWidth / aspectRatio);
}
if (!Application.isPlaying)
{
if (panel != null)
{
panel.RemoveWidget(this);
panel = null;
}
CreatePanel();
}
}
else
{
if (mWidth < minWidth) mWidth = minWidth;
if (mHeight < minHeight) mHeight = minHeight;
}
}
#endif
/// <summary>
/// Tell the panel responsible for the widget that something has changed and the buffers need to be rebuilt.
/// </summary>
public virtual void MarkAsChanged ()
{
if (NGUITools.GetActive(this))
{
mMoved = true; // add by chenbin
mChanged = true;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
// If we're in the editor, update the panel right away so its geometry gets updated.
if (panel != null && enabled && NGUITools.GetActive(gameObject) && !mPlayMode)
{
SetDirty();
CheckLayer();
#if UNITY_EDITOR
// Mark the panel as dirty so it gets updated
if (material != null) NGUITools.SetDirty(panel.gameObject);
#endif
}
}
}
/// <summary>
/// Ensure we have a panel referencing this widget.
/// </summary>
public UIPanel CreatePanel ()
{
if (mStarted && panel == null && enabled && NGUITools.GetActive(gameObject))
{
panel = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);
if (panel != null)
{
mParentFound = false;
// if (material != null) { //add by chenbin
panel.AddWidget (this);
// }
CheckLayer();
Invalidate(true);
}
}
return panel;
}
/// <summary>
/// Check to ensure that the widget resides on the same layer as its panel.
/// </summary>
public void CheckLayer ()
{
if (panel != null && panel.gameObject.layer != gameObject.layer)
{
#if UNITY_EDITOR
Debug.LogWarning("You can't place widgets on a layer different than the UIPanel that manages them.\n" +
"If you want to move widgets to a different layer, parent them to a new panel instead.", this);
#endif
gameObject.layer = panel.gameObject.layer;
}
}
/// <summary>
/// Checks to ensure that the widget is still parented to the right panel.
/// </summary>
public override void ParentHasChanged ()
{
base.ParentHasChanged();
if (panel != null)
{
UIPanel p = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);
if (panel != p)
{
RemoveFromPanel();
CreatePanel();
}
}
}
/// <summary>
/// Remember whether we're in play mode.
/// </summary>
protected virtual void Awake ()
{
mGo = gameObject;
mPlayMode = Application.isPlaying;
}
/// <summary>
/// Mark the widget and the panel as having been changed.
/// </summary>
protected override void OnInit ()
{
base.OnInit();
RemoveFromPanel();
mMoved = true;
// Prior to NGUI 2.7.0 width and height was specified as transform's local scale
if (mWidth == 100 && mHeight == 100 && cachedTransform.localScale.magnitude > 8f)
{
UpgradeFrom265();
cachedTransform.localScale = Vector3.one;
#if UNITY_EDITOR
NGUITools.SetDirty(this);
#endif
}
Update();
}
/// <summary>
/// Facilitates upgrading from NGUI 2.6.5 or earlier versions.
/// </summary>
protected virtual void UpgradeFrom265 ()
{
Vector3 scale = cachedTransform.localScale;
mWidth = Mathf.Abs(Mathf.RoundToInt(scale.x));
mHeight = Mathf.Abs(Mathf.RoundToInt(scale.y));
NGUITools.UpdateWidgetCollider(gameObject, true);
}
/// <summary>
/// Virtual Start() functionality for widgets.
/// </summary>
protected override void OnStart ()
{
#if UNITY_EDITOR
if (GetComponent<UIPanel>() != null)
{
Debug.LogError("Widgets and panels should not be on the same object! Widget must be a child of the panel.", this);
}
else if (!Application.isPlaying && GetComponents<UIWidget>().Length > 1)
{
Debug.LogError("You should not place more than one widget on the same object. Weird stuff will happen!", this);
}
#endif
CreatePanel();
}
/// <summary>
/// Update the anchored edges and ensure the widget is registered with a panel.
/// </summary>
protected override void OnAnchor ()
{
float lt, bt, rt, tt;
Transform trans = cachedTransform;
Transform parent = trans.parent;
Vector3 pos = trans.localPosition;
Vector2 pvt = pivotOffset;
float sizeAdjust = UIRoot.GetPixelSizeAdjustment(gameObject);
#region add by chenbin
Vector4 offsetRect = NGUITools.offsetRect;
float leftOffset = offsetRect.x;
float rightOffset = offsetRect.z;
float topOffset = offsetRect.y;
float bottomOffset = offsetRect.w;
UIPanel panel = null;
#endregion
// Attempt to fast-path if all anchors match
if (leftAnchor.target == bottomAnchor.target &&
leftAnchor.target == rightAnchor.target &&
leftAnchor.target == topAnchor.target)
{
//add by chenbin
panel = leftAnchor.target.GetComponent<UIPanel>();
if (panel != null)
{
leftOffset *= panel.width;
rightOffset *= panel.width;
topOffset *= panel.height;
bottomOffset *= panel.height;
} else
{
leftOffset = 0;
rightOffset = 0;
topOffset = 0;
}
Vector3[] sides = leftAnchor.GetSides(parent);
if (sides != null)
{
lt = NGUIMath.Lerp(sides[0].x, sides[2].x, leftAnchor.relative) + leftAnchor.absolute + leftOffset;
rt = NGUIMath.Lerp(sides[0].x, sides[2].x, rightAnchor.relative) + rightAnchor.absolute - rightOffset;
if(bottomAnchor.absolute < 0)
{
bt = NGUIMath.Lerp(sides[3].y, sides[1].y, bottomAnchor.relative) + bottomAnchor.absolute - topOffset;
} else
{
bt = NGUIMath.Lerp(sides[3].y, sides[1].y, bottomAnchor.relative) + bottomAnchor.absolute + bottomOffset;
}
tt = NGUIMath.Lerp(sides[3].y, sides[1].y, topAnchor.relative) + topAnchor.absolute - topOffset;
mIsInFront = true;
}
else
{
// Anchored to a single transform
Vector3 lp = GetLocalPos(leftAnchor, parent);
lt = lp.x + leftAnchor.absolute + leftOffset;
bt = lp.y + bottomAnchor.absolute - topOffset;
rt = lp.x + rightAnchor.absolute + rightOffset;
tt = lp.y + topAnchor.absolute - topOffset;
mIsInFront = (!hideIfOffScreen || lp.z >= 0f);
}
}
else
{
mIsInFront = true;
// Left anchor point
if (leftAnchor.target)
{
//add by chenbin
panel = leftAnchor.target.GetComponent<UIPanel>();
if (panel != null)
{
leftOffset *= panel.width;
rightOffset *= panel.width;
topOffset *= panel.height;
}
else
{
leftOffset = 0;
rightOffset = 0;
topOffset = 0;
}
Vector3[] sides = leftAnchor.GetSides(parent);
if (sides != null)
{
lt = NGUIMath.Lerp(sides[0].x, sides[2].x, leftAnchor.relative) + leftAnchor.absolute + leftOffset;
}
else
{
lt = GetLocalPos(leftAnchor, parent).x + leftAnchor.absolute + leftOffset;
}
}
else lt = pos.x - pvt.x * mWidth;
// Right anchor point
if (rightAnchor.target)
{
//add by chenbin
panel = rightAnchor.target.GetComponent<UIPanel>();
if (panel != null)
{
leftOffset *= panel.width;
rightOffset *= panel.width;
topOffset *= panel.height;
}
else
{
leftOffset = 0;
rightOffset = 0;
topOffset = 0;
}
Vector3[] sides = rightAnchor.GetSides(parent);
if (sides != null)
{
rt = NGUIMath.Lerp(sides[0].x, sides[2].x, rightAnchor.relative) + rightAnchor.absolute - rightOffset;
}
else
{
rt = GetLocalPos(rightAnchor, parent).x + rightAnchor.absolute - rightOffset;
}
}
else rt = pos.x - pvt.x * mWidth + mWidth;
// Bottom anchor point
if (bottomAnchor.target)
{
//add by chenbin
panel = bottomAnchor.target.GetComponent<UIPanel>();
if (panel != null)
{
leftOffset *= panel.width;
rightOffset *= panel.width;
topOffset *= panel.height;
bottomOffset *= panel.height;
}
else
{
leftOffset = 0;
rightOffset = 0;
topOffset = 0;
}
Vector3[] sides = bottomAnchor.GetSides(parent);
if (sides != null)
{
bt = NGUIMath.Lerp(sides[3].y, sides[1].y, bottomAnchor.relative) + bottomAnchor.absolute + bottomOffset;
}
else
{
bt = GetLocalPos(bottomAnchor, parent).y + bottomAnchor.absolute + bottomOffset;
}
}
else bt = pos.y - pvt.y * mHeight;
// Top anchor point
if (topAnchor.target)
{
//add by chenbin
panel = topAnchor.target.GetComponent<UIPanel>();
if (panel != null)
{
leftOffset *= panel.width;
rightOffset *= panel.width;
topOffset *= panel.height;
}
else
{
leftOffset = 0;
rightOffset = 0;
topOffset = 0;
}
Vector3[] sides = topAnchor.GetSides(parent);
if (sides != null)
{
tt = NGUIMath.Lerp(sides[3].y, sides[1].y, topAnchor.relative) + topAnchor.absolute - topOffset;
}
else
{
tt = GetLocalPos(topAnchor, parent).y + topAnchor.absolute - topOffset;
}
}
else tt = pos.y - pvt.y * mHeight + mHeight;
}
// Calculate the new position, width and height
Vector3 newPos = new Vector3(Mathf.Lerp(lt, rt, pvt.x), Mathf.Lerp(bt, tt, pvt.y), pos.z);
newPos.x = Mathf.Round(newPos.x);
newPos.y = Mathf.Round(newPos.y);
int w = Mathf.FloorToInt(rt - lt + 0.5f);
int h = Mathf.FloorToInt(tt - bt + 0.5f);
// Maintain the aspect ratio if requested and possible
if (keepAspectRatio != AspectRatioSource.Free && aspectRatio != 0f)
{
if (keepAspectRatio == AspectRatioSource.BasedOnHeight)
{
w = Mathf.RoundToInt(h * aspectRatio);
}
else h = Mathf.RoundToInt(w / aspectRatio);
}
// Don't let the width and height get too small
if (w < minWidth) w = minWidth;
if (h < minHeight) h = minHeight;
// Update the position if it has changed
if (Vector3.SqrMagnitude(pos - newPos) > 0.001f)
{
cachedTransform.localPosition = newPos;
if (mIsInFront) mChanged = true;
}
// Update the width and height if it has changed
if (mWidth != w || mHeight != h)
{
mWidth = w;
mHeight = h;
if (mIsInFront) mChanged = true;
if (autoResizeBoxCollider) ResizeCollider();
}
}
/// <summary>
/// Ensure we have a panel to work with.
/// </summary>
protected override void OnUpdate ()
{
if (panel == null) CreatePanel();
#if UNITY_EDITOR
else if (!mPlayMode) ParentHasChanged();
#endif
}
#if !UNITY_EDITOR
/// <summary>
/// Mark the UI as changed when returning from paused state.
/// </summary>
void OnApplicationPause (bool paused) { if (!paused) MarkAsChanged(); }
#endif
/// <summary>
/// Clear references.
/// </summary>
protected override void OnDisable ()
{
RemoveFromPanel();
base.OnDisable();
}
/// <summary>
/// Unregister this widget.
/// </summary>
void OnDestroy () { RemoveFromPanel(); }
#if UNITY_EDITOR
static int mHandles = -1;
/// <summary>
/// Whether widgets will show handles with the Move Tool, or just the View Tool.
/// </summary>
static public bool showHandlesWithMoveTool
{
get
{
if (mHandles == -1)
{
mHandles = UnityEditor.EditorPrefs.GetInt("NGUI Handles", 1);
}
return (mHandles == 1);
}
set
{
int val = value ? 1 : 0;
if (mHandles != val)
{
mHandles = val;
UnityEditor.EditorPrefs.SetInt("NGUI Handles", mHandles);
}
}
}
/// <summary>
/// Whether the widget should have some form of handles shown.
/// </summary>
static public bool showHandles
{
get
{
#if UNITY_4_3 || UNITY_4_5
if (showHandlesWithMoveTool)
{
return UnityEditor.Tools.current == UnityEditor.Tool.Move;
}
return UnityEditor.Tools.current == UnityEditor.Tool.View;
#else
return UnityEditor.Tools.current == UnityEditor.Tool.Rect;
#endif
}
}
/// <summary>
/// Draw some selectable gizmos.
/// </summary>
void OnDrawGizmos ()
{
if (isVisible && NGUITools.GetActive(this))
{
if (UnityEditor.Selection.activeGameObject == gameObject && showHandles) return;
Color outline = new Color(1f, 1f, 1f, 0.2f);
float adjustment = (root != null) ? 0.05f : 0.001f;
Vector2 offset = pivotOffset;
Vector3 center = new Vector3(mWidth * (0.5f - offset.x), mHeight * (0.5f - offset.y), -mDepth * adjustment);
Vector3 size = new Vector3(mWidth, mHeight, 1f);
// Draw the gizmo
Gizmos.matrix = cachedTransform.localToWorldMatrix;
Gizmos.color = (UnityEditor.Selection.activeGameObject == cachedTransform) ? Color.white : outline;
Gizmos.DrawWireCube(center, size);
// Make the widget selectable
size.z = 0.01f;
Gizmos.color = Color.clear;
Gizmos.DrawCube(center, size);
}
}
#endif // UNITY_EDITOR
/// <summary>
/// Update the widget's visibility state.
/// </summary>
public bool UpdateVisibility (bool visibleByAlpha, bool visibleByPanel)
{
if (mIsVisibleByAlpha != visibleByAlpha || mIsVisibleByPanel != visibleByPanel)
{
mChanged = true;
mIsVisibleByAlpha = visibleByAlpha;
mIsVisibleByPanel = visibleByPanel;
return true;
}
return false;
}
int mMatrixFrame = -1;
Vector3 mOldV0;
Vector3 mOldV1;
/// <summary>
/// Check to see if the widget has moved relative to the panel that manages it
/// </summary>
public bool UpdateTransform (int frame)
{
#if UNITY_EDITOR
if (!mMoved && !panel.widgetsAreStatic || !mPlayMode)
#else
if (!mMoved && !panel.widgetsAreStatic)
#endif
{
#if UNITY_3_5 || UNITY_4_0
if (HasTransformChanged())
{
#else
if (cachedTransform.hasChanged)
{
mTrans.hasChanged = false;
#endif
mLocalToPanel = panel.worldToLocal * cachedTransform.localToWorldMatrix;
mMatrixFrame = frame;
Vector2 offset = pivotOffset;
float x0 = -offset.x * mWidth;
float y0 = -offset.y * mHeight;
float x1 = x0 + mWidth;
float y1 = y0 + mHeight;
Transform wt = cachedTransform;
Vector3 v0 = wt.TransformPoint(x0, y0, 0f);
Vector3 v1 = wt.TransformPoint(x1, y1, 0f);
v0 = panel.worldToLocal.MultiplyPoint3x4(v0);
v1 = panel.worldToLocal.MultiplyPoint3x4(v1);
if (Vector3.SqrMagnitude(mOldV0 - v0) > 0.000001f ||
Vector3.SqrMagnitude(mOldV1 - v1) > 0.000001f)
{
mMoved = true;
mOldV0 = v0;
mOldV1 = v1;
}
}
}
// Notify the listeners
if (mMoved && onChange != null) onChange();
return mMoved || mChanged;
}
#if UNITY_3_5 || UNITY_4_0
[System.NonSerialized] Vector3 mOldPos;
[System.NonSerialized] Quaternion mOldRot;
[System.NonSerialized] Vector3 mOldScale;
/// <summary>
/// Whether the transform has changed since the last time it was checked.
/// </summary>
bool HasTransformChanged ()
{
Transform t = cachedTransform;
if (t.position != mOldPos || t.rotation != mOldRot || t.lossyScale != mOldScale)
{
mOldPos = t.position;
mOldRot = t.rotation;
mOldScale = t.lossyScale;
return true;
}
return false;
}
#endif
/// <summary>
/// Update the widget and fill its geometry if necessary. Returns whether something was changed.
/// </summary>
public bool UpdateGeometry (int frame)
{
// Has the alpha changed?
float finalAlpha = CalculateFinalAlpha(frame);
if (mIsVisibleByAlpha && mLastAlpha != finalAlpha) mChanged = true;
mLastAlpha = finalAlpha;
if (mChanged)
{
mChanged = false;
if (mIsVisibleByAlpha && finalAlpha > 0.001f && shader != null)
{
bool hadVertices = geometry.hasVertices;
if (fillGeometry)
{
geometry.Clear();
OnFill(geometry.verts, geometry.uvs, geometry.cols);
}
if (geometry.hasVertices)
{
// Want to see what's being filled? Uncomment this line.
//Debug.Log("Fill " + name + " (" + Time.frameCount + ")");
if (mMatrixFrame != frame)
{
mLocalToPanel = panel.worldToLocal * cachedTransform.localToWorldMatrix;
mMatrixFrame = frame;
}
geometry.ApplyTransform(mLocalToPanel);
mMoved = false;
return true;
}
return hadVertices;
}
else if (geometry.hasVertices)
{
if (fillGeometry) geometry.Clear();
mMoved = false;
return true;
}
}
else if (mMoved && geometry.hasVertices)
{
if (mMatrixFrame != frame)
{
mLocalToPanel = panel.worldToLocal * cachedTransform.localToWorldMatrix;
mMatrixFrame = frame;
}
geometry.ApplyTransform(mLocalToPanel);
mMoved = false;
return true;
}
mMoved = false;
return false;
}
/// <summary>
/// Append the local geometry buffers to the specified ones.
/// </summary>
public void WriteToBuffers (BetterList<Vector3> v, BetterList<Vector2> u, BetterList<Color32> c, BetterList<Vector3> n, BetterList<Vector4> t)
{
geometry.WriteToBuffers(v, u, c, n, t);
}
/// <summary>
/// Make the widget pixel-perfect.
/// </summary>
virtual public void MakePixelPerfect ()
{
Vector3 pos = cachedTransform.localPosition;
pos.z = Mathf.Round(pos.z);
pos.x = Mathf.Round(pos.x);
pos.y = Mathf.Round(pos.y);
cachedTransform.localPosition = pos;
Vector3 ls = cachedTransform.localScale;
cachedTransform.localScale = new Vector3(Mathf.Sign(ls.x), Mathf.Sign(ls.y), 1f);
}
/// <summary>
/// Minimum allowed width for this widget.
/// </summary>
virtual public int minWidth { get { return 2; } }
/// <summary>
/// Minimum allowed height for this widget.
/// </summary>
virtual public int minHeight { get { return 2; } }
/// <summary>
/// Dimensions of the sprite's border, if any.
/// </summary>
virtual public Vector4 border { get { return Vector4.zero; } set { } }
/// <summary>
/// Virtual function called by the UIPanel that fills the buffers.
/// </summary>
virtual public void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
{
// Call this in your derived classes:
//if (onPostFill != null)
// onPostFill(this, verts.size, verts, uvs, cols);
}
}