diff options
author | chai <chaifix@163.com> | 2021-05-08 23:15:13 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2021-05-08 23:15:13 +0800 |
commit | d07e14add74e017b52ab2371efeea1aa4ea10ced (patch) | |
tree | efd07869326e4c428f5bfe43fad0c2583d32a401 /Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs |
+init
Diffstat (limited to 'Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs')
-rw-r--r-- | Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs new file mode 100644 index 0000000..5fba1cc --- /dev/null +++ b/Assets/uGUI-2017.1/UnityEngine.UI/UI/Core/Layout/LayoutRebuilder.cs @@ -0,0 +1,233 @@ +using System.Collections.Generic; +using UnityEngine.Events; + +namespace UnityEngine.UI +{ + public class LayoutRebuilder : ICanvasElement + { + // m_ToRebuild是一个ILayoutGroup + private RectTransform m_ToRebuild; + //There are a few of reasons we need to cache the Hash from the transform: + // - This is a ValueType (struct) and .Net calculates Hash from the Value Type fields. + // - The key of a Dictionary should have a constant Hash value. + // - It's possible for the Transform to get nulled from the Native side. + // We use this struct with the IndexedSet container, which uses a dictionary as part of it's implementation + // So this struct gets used as a key to a dictionary, so we need to guarantee a constant Hash value. + private int m_CachedHashFromTransform; + + // 一个rebuilder结构的池子 + static ObjectPool<LayoutRebuilder> s_Rebuilders = new ObjectPool<LayoutRebuilder>(null, x => x.Clear()); + + private void Initialize(RectTransform controller) + { + m_ToRebuild = controller; + m_CachedHashFromTransform = controller.GetHashCode(); + } + + private void Clear() + { + m_ToRebuild = null; + m_CachedHashFromTransform = 0; + } + + static LayoutRebuilder() + { + RectTransform.reapplyDrivenProperties += ReapplyDrivenProperties; + } + + static void ReapplyDrivenProperties(RectTransform driven) + { + MarkLayoutForRebuild(driven); + } + + public Transform transform { get { return m_ToRebuild; }} + + public bool IsDestroyed() + { + return m_ToRebuild == null; + } + + static void StripDisabledBehavioursFromList(List<Component> components) + { + components.RemoveAll(e => e is Behaviour && !((Behaviour)e).isActiveAndEnabled); + } + + // 立即重新布局一次,而不用等到帧末尾CanvasUpdateReigstry.PerformUpdate的时候 + public static void ForceRebuildLayoutImmediate(RectTransform layoutRoot) + { + var rebuilder = s_Rebuilders.Get(); + rebuilder.Initialize(layoutRoot); + rebuilder.Rebuild(CanvasUpdate.Layout); + s_Rebuilders.Release(rebuilder); + } + + public void Rebuild(CanvasUpdate executing) + { + switch (executing) + { + case CanvasUpdate.Layout: + // It's unfortunate that we'll perform the same GetComponents querys for the tree 2 times, + // but each tree have to be fully iterated before going to the next action, + // so reusing the results would entail storing results in a Dictionary or similar, + // which is probably a bigger overhead than performing GetComponents multiple times. + PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputHorizontal()); + PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutHorizontal()); + PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputVertical()); + PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutVertical()); + break; + } + } + + // 从上到下遍历,执行action + private void PerformLayoutControl(RectTransform rect, UnityAction<Component> action) + { + if (rect == null) + return; + + var components = ListPool<Component>.Get(); + rect.GetComponents(typeof(ILayoutController), components); + StripDisabledBehavioursFromList(components); + + // If there are no controllers on this rect we can skip this entire sub-tree + // We don't need to consider controllers on children deeper in the sub-tree either, + // since they will be their own roots. + if (components.Count > 0) + { + // + // Layout control needs to executed top down with parents being done before their children, + // because the children rely on the sizes of the parents. + + // 做两次遍历,先执行ILayoutSelfController比如ContentSizeFitter,再执行ILayoutGroup + + // First call layout controllers that may change their own RectTransform + for (int i = 0; i < components.Count; i++) + if (components[i] is ILayoutSelfController) + action(components[i]); + + // Then call the remaining, such as layout groups that change their children, taking their own RectTransform size into account. + for (int i = 0; i < components.Count; i++) + if (!(components[i] is ILayoutSelfController)) + action(components[i]); + + for (int i = 0; i < rect.childCount; i++) + PerformLayoutControl(rect.GetChild(i) as RectTransform, action); + } + + ListPool<Component>.Release(components); + } + + private void PerformLayoutCalculation(RectTransform rect, UnityAction<Component> action) + { + if (rect == null) + return; + + var components = ListPool<Component>.Get(); + rect.GetComponents(typeof(ILayoutElement), components); + StripDisabledBehavioursFromList(components); + + // If there are no controllers on this rect we can skip this entire sub-tree + // We don't need to consider controllers on children deeper in the sub-tree either, + // since they will be their own roots. + if (components.Count > 0 || rect.GetComponent(typeof(ILayoutGroup))) + { + + // 先从子节点开始,最后到父节点,这样父节点可以得到子节点的信息 + // Layout calculations needs to executed bottom up with children being done before their parents, + // because the parent calculated sizes rely on the sizes of the children. + + for (int i = 0; i < rect.childCount; i++) + PerformLayoutCalculation(rect.GetChild(i) as RectTransform, action); + + for (int i = 0; i < components.Count; i++) + action(components[i]); + } + + ListPool<Component>.Release(components); + } + + // 找到rect祖先节点中的layoutGroup并加入队列 + public static void MarkLayoutForRebuild(RectTransform rect) + { + if (rect == null) + return; + + var comps = ListPool<Component>.Get(); + RectTransform layoutRoot = rect; // 祖先节点中的layoutGroup + while (true) + { + var parent = layoutRoot.parent as RectTransform; + if (!ValidLayoutGroup(parent, comps)) + break; + layoutRoot = parent; + } + + // We know the layout root is valid if it's not the same as the rect, + // since we checked that above. But if they're the same we still need to check. + if (layoutRoot == rect && !ValidController(layoutRoot, comps)) + { + ListPool<Component>.Release(comps); + return; + } + + MarkLayoutRootForRebuild(layoutRoot); + ListPool<Component>.Release(comps); + } + + private static bool ValidLayoutGroup(RectTransform parent, List<Component> comps) + { + if (parent == null) + return false; + + parent.GetComponents(typeof(ILayoutGroup), comps); + StripDisabledBehavioursFromList(comps); + var validCount = comps.Count > 0; + return validCount; + } + + private static bool ValidController(RectTransform layoutRoot, List<Component> comps) + { + if (layoutRoot == null) + return false; + layoutRoot.GetComponents(typeof(ILayoutController), comps); + StripDisabledBehavioursFromList(comps); + var valid = comps.Count > 0; + return valid; + } + + private static void MarkLayoutRootForRebuild(RectTransform controller) + { + if (controller == null) + return; + + var rebuilder = s_Rebuilders.Get(); + rebuilder.Initialize(controller); + // 注册到CanvasUpdateRegistry里 + if (!CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(rebuilder)) + s_Rebuilders.Release(rebuilder); + } + + public void LayoutComplete() + { + // 放回池子里 + s_Rebuilders.Release(this); + } + + public void GraphicUpdateComplete() + {} + + public override int GetHashCode() + { + return m_CachedHashFromTransform; + } + + public override bool Equals(object obj) + { + return obj.GetHashCode() == GetHashCode(); + } + + public override string ToString() + { + return "(Layout Rebuilder for) " + m_ToRebuild; + } + } +} |