using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace FairyGUI
{
///
///
///
public class Container : DisplayObject
{
///
///
///
public RenderMode renderMode;
///
///
///
public Camera renderCamera;
///
///
///
public bool opaque;
///
///
///
public Vector4? clipSoftness;
///
///
///
public IHitTest hitArea;
///
///
///
public bool touchChildren;
///
///
///
public event Action onUpdate;
///
///
///
public bool reversedMask;
List _children;
DisplayObject _mask;
Rect? _clipRect;
List _descendants;
internal int _panelOrder;
internal DisplayObject _lastFocus;
///
///
///
public Container()
: base()
{
CreateGameObject("Container");
Init();
}
///
///
///
///
public Container(string gameObjectName)
: base()
{
CreateGameObject(gameObjectName);
Init();
}
///
///
///
///
public Container(GameObject attachTarget)
: base()
{
SetGameObject(attachTarget);
Init();
}
void Init()
{
_children = new List();
touchChildren = true;
}
///
///
///
public int numChildren
{
get { return _children.Count; }
}
///
///
///
///
///
public DisplayObject AddChild(DisplayObject child)
{
AddChildAt(child, _children.Count);
return child;
}
///
///
///
///
///
///
public DisplayObject AddChildAt(DisplayObject child, int index)
{
int count = _children.Count;
if (index >= 0 && index <= count)
{
if (child.parent == this)
{
SetChildIndex(child, index);
}
else
{
child.RemoveFromParent();
if (index == count)
_children.Add(child);
else
_children.Insert(index, child);
child.InternalSetParent(this);
if (stage != null)
{
if (child is Container)
child.BroadcastEvent("onAddedToStage", null);
else
child.DispatchEvent("onAddedToStage", null);
}
InvalidateBatchingState(true);
}
return child;
}
else
{
throw new Exception("Invalid child index");
}
}
///
///
///
///
///
public bool Contains(DisplayObject child)
{
return _children.Contains(child);
}
///
///
///
///
///
public DisplayObject GetChildAt(int index)
{
return _children[index];
}
///
///
///
///
///
public DisplayObject GetChild(string name)
{
int cnt = _children.Count;
for (int i = 0; i < cnt; ++i)
{
if (_children[i].name == name)
return _children[i];
}
return null;
}
///
///
///
///
public DisplayObject[] GetChildren()
{
return _children.ToArray();
}
///
///
///
///
///
public int GetChildIndex(DisplayObject child)
{
return _children.IndexOf(child);
}
///
///
///
///
///
public DisplayObject RemoveChild(DisplayObject child)
{
return RemoveChild(child, false);
}
///
///
///
///
///
///
public DisplayObject RemoveChild(DisplayObject child, bool dispose)
{
if (child.parent != this)
throw new Exception("obj is not a child");
int i = _children.IndexOf(child);
if (i >= 0)
return RemoveChildAt(i, dispose);
else
return null;
}
///
///
///
///
///
public DisplayObject RemoveChildAt(int index)
{
return RemoveChildAt(index, false);
}
///
///
///
///
///
///
public DisplayObject RemoveChildAt(int index, bool dispose)
{
if (index >= 0 && index < _children.Count)
{
DisplayObject child = _children[index];
if (stage != null && (child._flags & Flags.Disposed) == 0)
{
if (child is Container)
{
child.BroadcastEvent("onRemovedFromStage", null);
if (child == Stage.inst.focus || ((Container)child).IsAncestorOf(Stage.inst.focus))
Stage.inst._OnFocusRemoving(this);
}
else
{
child.DispatchEvent("onRemovedFromStage", null);
if (child == Stage.inst.focus)
Stage.inst._OnFocusRemoving(this);
}
}
_children.Remove(child);
InvalidateBatchingState(true);
if (!dispose)
child.InternalSetParent(null);
else
child.Dispose();
return child;
}
else
throw new Exception("Invalid child index");
}
///
///
///
public void RemoveChildren()
{
RemoveChildren(0, int.MaxValue, false);
}
///
///
///
///
///
///
public void RemoveChildren(int beginIndex, int endIndex, bool dispose)
{
if (endIndex < 0 || endIndex >= numChildren)
endIndex = numChildren - 1;
for (int i = beginIndex; i <= endIndex; ++i)
RemoveChildAt(beginIndex, dispose);
}
///
///
///
///
///
public void SetChildIndex(DisplayObject child, int index)
{
int oldIndex = _children.IndexOf(child);
if (oldIndex == index) return;
if (oldIndex == -1) throw new ArgumentException("Not a child of this container");
_children.RemoveAt(oldIndex);
if (index >= _children.Count)
_children.Add(child);
else
_children.Insert(index, child);
InvalidateBatchingState(true);
}
///
///
///
///
///
public void SwapChildren(DisplayObject child1, DisplayObject child2)
{
int index1 = _children.IndexOf(child1);
int index2 = _children.IndexOf(child2);
if (index1 == -1 || index2 == -1)
throw new Exception("Not a child of this container");
SwapChildrenAt(index1, index2);
}
///
///
///
///
///
public void SwapChildrenAt(int index1, int index2)
{
DisplayObject obj1 = _children[index1];
DisplayObject obj2 = _children[index2];
_children[index1] = obj2;
_children[index2] = obj1;
InvalidateBatchingState(true);
}
///
///
///
///
///
public void ChangeChildrenOrder(IList indice, IList objs)
{
int cnt = objs.Count;
for (int i = 0; i < cnt; i++)
{
DisplayObject obj = objs[i];
if (obj.parent != this)
throw new Exception("Not a child of this container");
_children[indice[i]] = obj;
}
InvalidateBatchingState(true);
}
///
///
///
///
public IEnumerator GetDescendants(bool backward)
{
return new DescendantsEnumerator(this, backward);
}
///
///
///
public Rect? clipRect
{
get { return _clipRect; }
set
{
if (_clipRect != value)
{
_clipRect = value;
UpdateBatchingFlags();
}
}
}
///
///
///
public DisplayObject mask
{
get { return _mask; }
set
{
if (_mask != value)
{
_mask = value;
UpdateBatchingFlags();
}
}
}
///
///
///
public void CreateGraphics()
{
if (graphics == null)
{
graphics = new NGraphics(this.gameObject);
graphics.texture = NTexture.Empty;
}
}
public override Rect GetBounds(DisplayObject targetSpace)
{
if (_clipRect != null)
return TransformRect((Rect)_clipRect, targetSpace);
int count = _children.Count;
Rect rect;
if (count == 0)
{
Vector2 v = TransformPoint(Vector2.zero, targetSpace);
rect = Rect.MinMaxRect(v.x, v.y, 0, 0);
}
else if (count == 1)
{
rect = _children[0].GetBounds(targetSpace);
}
else
{
float minX = float.MaxValue, maxX = float.MinValue;
float minY = float.MaxValue, maxY = float.MinValue;
for (int i = 0; i < count; ++i)
{
rect = _children[i].GetBounds(targetSpace);
minX = minX < rect.xMin ? minX : rect.xMin;
maxX = maxX > rect.xMax ? maxX : rect.xMax;
minY = minY < rect.yMin ? minY : rect.yMin;
maxY = maxY > rect.yMax ? maxY : rect.yMax;
}
rect = Rect.MinMaxRect(minX, minY, maxX, maxY);
}
return rect;
}
///
///
///
///
public Camera GetRenderCamera()
{
if (renderMode == RenderMode.ScreenSpaceOverlay)
return StageCamera.main;
else
{
Camera cam = this.renderCamera;
if (cam == null)
{
if (HitTestContext.cachedMainCamera != null)
cam = HitTestContext.cachedMainCamera;
else
{
cam = Camera.main;
if (cam == null)
cam = StageCamera.main;
}
}
return cam;
}
}
///
///
///
///
///
///
///
public DisplayObject HitTest(Vector2 stagePoint, bool forTouch)
{
if (StageCamera.main == null)
{
if (this is Stage)
return this;
else
return null;
}
HitTestContext.screenPoint = new Vector3(stagePoint.x, Screen.height - stagePoint.y, 0);
if (Display.displays.Length > 1)
{
Vector3 p = Display.RelativeMouseAt(HitTestContext.screenPoint);
if (p != Vector3.zero)
HitTestContext.screenPoint = p;
}
HitTestContext.worldPoint = StageCamera.main.ScreenToWorldPoint(HitTestContext.screenPoint);
HitTestContext.direction = Vector3.back;
HitTestContext.forTouch = forTouch;
HitTestContext.camera = StageCamera.main;
DisplayObject ret = HitTest();
if (ret != null)
return ret;
else if (this is Stage)
return this;
else
return null;
}
override protected DisplayObject HitTest()
{
if ((_flags & Flags.UserGameObject) != 0 && !gameObject.activeInHierarchy)
return null;
if (this.cachedTransform.localScale.x == 0 || this.cachedTransform.localScale.y == 0)
return null;
Camera savedCamera = HitTestContext.camera;
Vector3 savedWorldPoint = HitTestContext.worldPoint;
Vector3 savedDirection = HitTestContext.direction;
DisplayObject target;
if (renderMode != RenderMode.ScreenSpaceOverlay || (_flags & Flags.UserGameObject) != 0)
{
Camera cam = GetRenderCamera();
if (cam.targetDisplay != HitTestContext.screenPoint.z)
return null;
HitTestContext.camera = cam;
if (renderMode == RenderMode.WorldSpace)
{
Vector3 screenPoint = HitTestContext.camera.WorldToScreenPoint(this.cachedTransform.position); //only for query z value
screenPoint.x = HitTestContext.screenPoint.x;
screenPoint.y = HitTestContext.screenPoint.y;
//获得本地z轴在世界坐标的方向
HitTestContext.worldPoint = HitTestContext.camera.ScreenToWorldPoint(screenPoint);
Ray ray = HitTestContext.camera.ScreenPointToRay(screenPoint);
HitTestContext.direction = Vector3.zero - ray.direction;
}
else if (renderMode == RenderMode.ScreenSpaceCamera)
{
HitTestContext.worldPoint = HitTestContext.camera.ScreenToWorldPoint(HitTestContext.screenPoint);
}
}
else
{
if (HitTestContext.camera.targetDisplay != HitTestContext.screenPoint.z && !(this is Stage))
return null;
}
target = HitTest_Container();
HitTestContext.camera = savedCamera;
HitTestContext.worldPoint = savedWorldPoint;
HitTestContext.direction = savedDirection;
return target;
}
DisplayObject HitTest_Container()
{
Vector2 localPoint = WorldToLocal(HitTestContext.worldPoint, HitTestContext.direction);
if (_vertexMatrix != null)
HitTestContext.worldPoint = this.cachedTransform.TransformPoint(new Vector2(localPoint.x, -localPoint.y));
if (hitArea != null)
{
if (!hitArea.HitTest(_contentRect, localPoint))
return null;
if (hitArea is MeshColliderHitTest)
localPoint = ((MeshColliderHitTest)hitArea).lastHit;
}
else
{
if (_clipRect != null && !((Rect)_clipRect).Contains(localPoint))
return null;
}
if (_mask != null)
{
DisplayObject tmp = _mask.InternalHitTestMask();
if (!reversedMask && tmp == null || reversedMask && tmp != null)
return null;
}
DisplayObject target = null;
if (touchChildren)
{
int count = _children.Count;
for (int i = count - 1; i >= 0; --i) // front to back!
{
DisplayObject child = _children[i];
if ((child._flags & Flags.GameObjectDisposed) != 0)
{
child.DisplayDisposedWarning();
continue;
}
if (child == _mask || (child._flags & Flags.TouchDisabled) != 0)
continue;
target = child.InternalHitTest();
if (target != null)
break;
}
}
if (target == null && opaque && (hitArea != null || _contentRect.Contains(localPoint)))
target = this;
return target;
}
///
///
///
///
///
public bool IsAncestorOf(DisplayObject obj)
{
if (obj == null)
return false;
Container p = obj.parent;
while (p != null)
{
if (p == this)
return true;
p = p.parent;
}
return false;
}
///
///
///
public bool fairyBatching
{
get { return (_flags & Flags.FairyBatching) != 0; }
set
{
bool oldValue = (_flags & Flags.FairyBatching) != 0;
if (oldValue != value)
{
if (value)
_flags |= Flags.FairyBatching;
else
_flags &= ~Flags.FairyBatching;
UpdateBatchingFlags();
}
}
}
internal void UpdateBatchingFlags()
{
bool oldValue = (_flags & Flags.BatchingRoot) != 0;
bool newValue = (_flags & Flags.FairyBatching) != 0 || _clipRect != null || _mask != null || _paintingMode > 0;
if (newValue)
_flags |= Flags.BatchingRoot;
else
_flags &= ~Flags.BatchingRoot;
if (oldValue != newValue)
{
if (newValue)
_flags |= Flags.BatchingRequested;
else if (_descendants != null)
_descendants.Clear();
InvalidateBatchingState();
}
}
///
///
///
///
public void InvalidateBatchingState(bool childrenChanged)
{
if (childrenChanged && (_flags & Flags.BatchingRoot) != 0)
_flags |= Flags.BatchingRequested;
else
{
Container p = this.parent;
while (p != null)
{
if ((p._flags & Flags.BatchingRoot) != 0)
{
p._flags |= Flags.BatchingRequested;
break;
}
p = p.parent;
}
}
}
///
///
///
///
public void SetChildrenLayer(int value)
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
DisplayObject child = _children[i];
child._SetLayerDirect(value);
if ((child is Container) && child._paintingMode == 0)
((Container)child).SetChildrenLayer(value);
}
}
override public void Update(UpdateContext context)
{
if ((_flags & Flags.UserGameObject) != 0 && !gameObject.activeInHierarchy)
return;
base.Update(context);
if (_paintingMode != 0)
{
if ((_flags & Flags.CacheAsBitmap) != 0 && _paintingInfo.flag == 2)
{
if (onUpdate != null)
onUpdate();
return;
}
context.EnterPaintingMode();
}
if (_mask != null)
{
context.EnterClipping(this.id, reversedMask);
if (_mask.graphics != null)
_mask.graphics._PreUpdateMask(context, _mask.id);
}
else if (_clipRect != null)
context.EnterClipping(this.id, this.TransformRect((Rect)_clipRect, null), clipSoftness);
float savedAlpha = context.alpha;
context.alpha *= this.alpha;
bool savedGrayed = context.grayed;
context.grayed = context.grayed || this.grayed;
if ((_flags & Flags.FairyBatching) != 0)
context.batchingDepth++;
if (context.batchingDepth > 0)
{
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
DisplayObject child = _children[i];
if ((child._flags & Flags.GameObjectDisposed) != 0)
{
child.DisplayDisposedWarning();
continue;
}
if (child.visible)
child.Update(context);
}
}
else
{
if (_mask != null)
_mask.renderingOrder = context.renderingOrder++;
int cnt = _children.Count;
for (int i = 0; i < cnt; i++)
{
DisplayObject child = _children[i];
if ((child._flags & Flags.GameObjectDisposed) != 0)
{
child.DisplayDisposedWarning();
continue;
}
if (child.visible)
{
if (!(child.graphics != null && child.graphics._maskFlag == 1)) //if not a mask
child.renderingOrder = context.renderingOrder++;
child.Update(context);
}
}
if (_mask != null)
{
if (_mask.graphics != null)
_mask.graphics._SetStencilEraserOrder(context.renderingOrder++);
}
}
if ((_flags & Flags.FairyBatching) != 0)
{
if (context.batchingDepth == 1)
SetRenderingOrder(context);
context.batchingDepth--;
}
context.alpha = savedAlpha;
context.grayed = savedGrayed;
if (_clipRect != null || _mask != null)
context.LeaveClipping();
if (_paintingMode != 0)
{
context.LeavePaintingMode();
UpdateContext.OnEnd += _paintingInfo.captureDelegate;
}
if (onUpdate != null)
onUpdate();
}
private void SetRenderingOrder(UpdateContext context)
{
if ((_flags & Flags.BatchingRequested) != 0)
DoFairyBatching();
if (_mask != null)
_mask.renderingOrder = context.renderingOrder++;
int cnt = _descendants.Count;
for (int i = 0; i < cnt; i++)
{
DisplayObject child = _descendants[i];
if (!(child.graphics != null && child.graphics._maskFlag == 1))
child.renderingOrder = context.renderingOrder++;
if ((child._flags & Flags.BatchingRoot) != 0)
((Container)child).SetRenderingOrder(context);
}
if (_mask != null)
{
if (_mask.graphics != null)
_mask.graphics._SetStencilEraserOrder(context.renderingOrder++);
}
}
private void DoFairyBatching()
{
_flags &= ~Flags.BatchingRequested;
if (_descendants == null)
_descendants = new List();
else
_descendants.Clear();
CollectChildren(this, false);
int cnt = _descendants.Count;
int i, j, k, m;
object curMat, testMat, lastMat;
DisplayObject current, test;
float[] bound;
for (i = 0; i < cnt; i++)
{
current = _descendants[i];
bound = current._batchingBounds;
curMat = current.material;
if (curMat == null || (current._flags & Flags.SkipBatching) != 0)
continue;
k = -1;
lastMat = null;
m = i;
for (j = i - 1; j >= 0; j--)
{
test = _descendants[j];
if ((test._flags & Flags.SkipBatching) != 0)
break;
testMat = test.material;
if (testMat != null)
{
if (lastMat != testMat)
{
lastMat = testMat;
m = j + 1;
}
if (curMat == testMat)
k = m;
}
if ((bound[0] > test._batchingBounds[0] ? bound[0] : test._batchingBounds[0])
<= (bound[2] < test._batchingBounds[2] ? bound[2] : test._batchingBounds[2])
&& (bound[1] > test._batchingBounds[1] ? bound[1] : test._batchingBounds[1])
<= (bound[3] < test._batchingBounds[3] ? bound[3] : test._batchingBounds[3]))
{
if (k == -1)
k = m;
break;
}
}
if (k != -1 && i != k)
{
_descendants.RemoveAt(i);
_descendants.Insert(k, current);
}
}
//Debug.Log("DoFairyBatching " + cnt + "," + this.cachedTransform.GetInstanceID());
}
private void CollectChildren(Container initiator, bool outlineChanged)
{
int count = _children.Count;
for (int i = 0; i < count; i++)
{
DisplayObject child = _children[i];
if (!child.visible)
continue;
if (child._batchingBounds == null)
child._batchingBounds = new float[4];
if (child is Container)
{
Container container = (Container)child;
if ((container._flags & Flags.BatchingRoot) != 0)
{
initiator._descendants.Add(container);
if (outlineChanged || (container._flags & Flags.OutlineChanged) != 0)
{
Rect rect = container.GetBounds(initiator);
container._batchingBounds[0] = rect.xMin;
container._batchingBounds[1] = rect.yMin;
container._batchingBounds[2] = rect.xMax;
container._batchingBounds[3] = rect.yMax;
}
if ((container._flags & Flags.BatchingRequested) != 0)
container.DoFairyBatching();
}
else
container.CollectChildren(initiator, outlineChanged || (container._flags & Flags.OutlineChanged) != 0);
}
else if (child != initiator._mask)
{
if (outlineChanged || (child._flags & Flags.OutlineChanged) != 0)
{
Rect rect = child.GetBounds(initiator);
child._batchingBounds[0] = rect.xMin;
child._batchingBounds[1] = rect.yMin;
child._batchingBounds[2] = rect.xMax;
child._batchingBounds[3] = rect.yMax;
}
initiator._descendants.Add(child);
}
child._flags &= ~Flags.OutlineChanged;
}
}
public override void Dispose()
{
if ((_flags & Flags.Disposed) != 0)
return;
base.Dispose(); //Destroy GameObject tree first, avoid destroying each seperately;
int numChildren = _children.Count;
for (int i = numChildren - 1; i >= 0; --i)
{
DisplayObject obj = _children[i];
obj.InternalSetParent(null); //Avoid RemoveParent call
obj.Dispose();
}
}
///
/// If true, when the container is focused, tab navigation is lock inside it.
///
public bool tabStopChildren
{
get { return (_flags & Flags.TabStopChildren) != 0; }
set
{
if (value)
_flags |= Flags.TabStopChildren;
else
_flags &= ~Flags.TabStopChildren;
}
}
struct DescendantsEnumerator : IEnumerator
{
Container _root;
Container _com;
DisplayObject _current;
int _index;
bool _forward;
public DescendantsEnumerator(Container root, bool backward)
{
_root = root;
_com = _root;
_current = null;
_forward = !backward;
if (_forward)
_index = 0;
else
_index = _com._children.Count - 1;
}
public DisplayObject Current
{
get { return _current; }
}
object IEnumerator.Current
{
get { return _current; }
}
public bool MoveNext()
{
if (_forward)
{
if (_index >= _com._children.Count)
{
if (_com == _root)
{
_current = null;
return false;
}
_current = _com;
_com = _com.parent;
_index = _com.GetChildIndex(_current) + 1;
return true;
}
else
{
DisplayObject obj = _com._children[_index];
if (obj is Container)
{
_com = (Container)obj;
_index = 0;
return MoveNext();
}
_index++;
_current = obj;
return true;
}
}
else
{
if (_index < 0)
{
if (_com == _root)
{
_current = null;
return false;
}
_current = _com;
_com = _com.parent;
_index = _com.GetChildIndex(_current) - 1;
return true;
}
else
{
DisplayObject obj = _com._children[_index];
if (obj is Container)
{
_com = (Container)obj;
_index = _com._children.Count - 1;
return MoveNext();
}
_index--;
_current = obj;
return true;
}
}
}
public void Reset()
{
_com = _root;
_current = null;
_index = 0;
}
public void Dispose()
{
}
}
}
}