using UnityEngine; namespace Flexalon { /// /// Base class for all layout componets. See [custom layout](/docs/customLayout) for details /// on how to extend this class. Assigns the Layout method to FlexalonNode and keeps the /// node's children up to date. /// [DisallowMultipleComponent, RequireComponent(typeof(FlexalonObject))] public abstract class LayoutBase : FlexalonComponent, Layout { /// protected override void DoOnEnable() { _node.DetachAllChildren(); for (int i = 0; i < transform.childCount; i++) { _node.AddChild(Flexalon.GetOrCreateNode(transform.GetChild(i).gameObject)); } Flexalon.GetOrCreate().PreUpdate += DetectChanges; _node.SetMethod(this); } /// protected override void DoOnDisable() { _node.SetMethod(null); var flexalon = Flexalon.Get(); if (flexalon) { flexalon.PreUpdate -= DetectChanges; } } /// protected override void ResetProperties() { _node.DetachAllChildren(); } // This function is complicated because it's working around two issues. // First, OnTransformChildrenChanged doesn't always run on 2019.4 due to a bug. // See https://issuetracker.unity3d.com/issues/ontransformchildrenchanged-doesnt-get-called-in-the-edit-mode-when-dragging-a-prefab-from-the-project-window-to-the-hierarchy // Second, we need to deal with undo/redo. The strategy here is to do nothing on undo/redo except fix // the node.Children list, since it isn't serialzed. To detect undo/redo, we check if the Parent or SiblingIndex // values change in the serialized FlexalonResult matches the transform children. private void DetectChanges() { // Check if any old children changed parents. They need to be marked dirty // since their size may change after leaving the layout. for (int i = 0; i < _node.Children.Count; i++) { var childNode = _node.Children[i]; if (!childNode.GameObject) { Flexalon.RecordFrameChanges = true; childNode.Detach(); MarkDirty(); } else if (childNode.GameObject.transform.parent != transform || childNode.IsDragging || childNode.SkipLayout || childNode.Constraint != null) { i--; childNode.Detach(); if (childNode.Result.Parent == transform) { #if UNITY_EDITOR UnityEditor.Undo.RecordObject(childNode.Result, "Parent change"); UnityEditor.PrefabUtility.RecordPrefabInstancePropertyModifications(childNode.Result); Flexalon.RecordFrameChanges = true; #endif childNode.Result.Parent = null; childNode.Result.SiblingIndex = 0; childNode.MarkDirty(); MarkDirty(); } } } // Check if we have any new or out of order children. int index = 0; for (int i = 0; i < transform.childCount; i++) { var child = transform.GetChild(i); var childNode = Flexalon.GetOrCreateNode(child.gameObject); if (childNode.IsDragging || childNode.SkipLayout || childNode.Constraint != null) { continue; } _node.InsertChild(childNode, index); if (childNode.Result.Parent != transform || childNode.Result.SiblingIndex != index) { #if UNITY_EDITOR UnityEditor.Undo.RecordObject(childNode.Result, "Parent change"); UnityEditor.PrefabUtility.RecordPrefabInstancePropertyModifications(childNode.Result); Flexalon.RecordFrameChanges = true; #endif childNode.Result.Parent = transform; childNode.Result.SiblingIndex = index; childNode.MarkDirty(); MarkDirty(); } index++; } } protected override void Initialize() { base.Initialize(); if (!gameObject.TryGetComponent(out var obj)) { obj = Flexalon.AddComponent(gameObject); } if (!Flexalon.IsRootCanvas(gameObject)) { if (obj.WidthType == SizeType.Component) { obj.WidthType = SizeType.Layout; } if (obj.HeightType == SizeType.Component) { obj.HeightType = SizeType.Layout; } if (obj.DepthType == SizeType.Component) { obj.DepthType = SizeType.Layout; } } } /// public virtual Bounds Measure(FlexalonNode node, Vector3 size, Vector3 min, Vector3 max) { throw new System.NotImplementedException(); } /// Helper to assign the fill and shrink size for all children. protected void SetChildrenFillShrinkSize(FlexalonNode node, Vector3 childSize, Vector3 layoutSize) { foreach (var child in node.Children) { child.SetShrinkFillSize(childSize, layoutSize); } } /// public virtual void Arrange(FlexalonNode node, Vector3 layoutSize) {} } }