// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Utilities
{
public abstract class BaseObjectCollection : MonoBehaviour
{
///
/// Action called when collection is updated
///
public Action OnCollectionUpdated { get; set; }
[HideInInspector]
[SerializeField]
private List nodeList = new List();
///
/// List of objects with generated data on the object.
///
protected List NodeList
{
get { return nodeList; }
}
///
/// Read only list of objects with generated data on the object.
///
public IReadOnlyList NodeListReadOnly
{
get { return nodeList.AsReadOnly(); }
}
[Tooltip("Whether to include space for inactive transforms in the layout")]
[SerializeField]
private bool ignoreInactiveTransforms = true;
///
/// Whether to include space for inactive transforms in the layout
///
public bool IgnoreInactiveTransforms
{
get { return ignoreInactiveTransforms; }
set { ignoreInactiveTransforms = value; }
}
[Tooltip("Type of sorting to use")]
[SerializeField]
private CollationOrder sortType = CollationOrder.None;
///
/// Type of sorting to use.
///
public CollationOrder SortType
{
get { return sortType; }
set { sortType = value; }
}
///
/// Rebuilds / updates the collection layout.
/// Update collection is called from the editor button on the inspector.
///
public virtual void UpdateCollection()
{
PruneEmptyNodes();
// Check when children change and adjust
for (int i = 0; i < transform.childCount; i++)
{
Transform child = transform.GetChild(i);
#if UNITY_EDITOR
UnityEditor.Undo.RecordObject(child, "ObjectCollection modify transform");
#endif // UNITY_EDITOR
if (!ContainsNode(child) && (child.gameObject.activeSelf || !IgnoreInactiveTransforms))
{
NodeList.Add(new ObjectCollectionNode { Name = child.name, Transform = child });
}
}
SortNodes();
LayoutChildren();
OnCollectionUpdated?.Invoke(this);
}
///
/// Sorts NodeList based on
///
protected void SortNodes()
{
switch (SortType)
{
case CollationOrder.ChildOrder:
NodeList.Sort((c1, c2) => (c1.Transform.GetSiblingIndex().CompareTo(c2.Transform.GetSiblingIndex())));
break;
case CollationOrder.Alphabetical:
NodeList.Sort((c1, c2) => (string.CompareOrdinal(c1.Name, c2.Name)));
break;
case CollationOrder.AlphabeticalReversed:
NodeList.Sort((c1, c2) => (string.CompareOrdinal(c1.Name, c2.Name)));
NodeList.Reverse();
break;
case CollationOrder.ChildOrderReversed:
NodeList.Sort((c1, c2) => (c1.Transform.GetSiblingIndex().CompareTo(c2.Transform.GetSiblingIndex())));
NodeList.Reverse();
break;
}
}
///
/// Checks for empty nodes and removes them
///
protected void PruneEmptyNodes()
{
// Check for empty nodes and remove them
var emptyNodes = new List();
for (int i = 0; i < NodeList.Count; i++)
{
if (NodeList[i].Transform == null || (IgnoreInactiveTransforms && !NodeList[i].Transform.gameObject.activeSelf) || NodeList[i].Transform.parent == null || !(NodeList[i].Transform.parent.gameObject == gameObject))
{
emptyNodes.Add(NodeList[i]);
}
}
// Now delete the empty nodes
for (int i = 0; i < emptyNodes.Count; i++)
{
NodeList.Remove(emptyNodes[i]);
}
emptyNodes.Clear();
}
///
/// Check if a node exists in the NodeList.
///
/// The Transform belonging to the
/// true when belongs to an element of the list.
protected bool ContainsNode(Transform node)
{
if (node == null)
{
return false;
}
for (int i = 0; i < NodeList.Count; i++)
{
if (NodeList[i].Transform == node)
{
return true;
}
}
return false;
}
///
/// Check if a node exists in the NodeList.
///
/// The Transform belonging to
/// The index of the element in
/// true when belongs to an element of the list.
public bool ContainsNode(Transform node, out int nodeIndex)
{
nodeIndex = 0;
if (node == null)
{
return false;
}
for (int i = 0; i < NodeList.Count; i++)
{
if (NodeList[i].Transform == node)
{
nodeIndex = i;
return true;
}
}
return false;
}
///
/// Implement for laying out all children when UpdateCollection is called.
///
protected abstract void LayoutChildren();
}
}