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) {}
}
}