441 lines
10 KiB
C#
441 lines
10 KiB
C#
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
|
|||
|
namespace RuntimeInspectorNamespace
|
|||
|
{
|
|||
|
public abstract class HierarchyData
|
|||
|
{
|
|||
|
private static readonly List<HierarchyDataTransform> transformDataPool = new List<HierarchyDataTransform>( 32 );
|
|||
|
private static readonly List<List<HierarchyDataTransform>> childrenListPool = new List<List<HierarchyDataTransform>>( 32 );
|
|||
|
|
|||
|
public abstract string Name { get; }
|
|||
|
public abstract bool IsActive { get; }
|
|||
|
public abstract int ChildCount { get; }
|
|||
|
public abstract Transform BoundTransform { get; }
|
|||
|
|
|||
|
protected List<HierarchyDataTransform> children;
|
|||
|
protected HierarchyData parent;
|
|||
|
public HierarchyDataRoot Root
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
HierarchyData _parent = this;
|
|||
|
while( _parent.parent != null )
|
|||
|
_parent = _parent.parent;
|
|||
|
|
|||
|
return (HierarchyDataRoot) _parent;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected int m_index;
|
|||
|
public int Index { get { return m_index; } }
|
|||
|
public int AbsoluteIndex
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
int result = m_index;
|
|||
|
HierarchyData _parent = parent;
|
|||
|
while( _parent != null )
|
|||
|
{
|
|||
|
result += _parent.m_index + 1;
|
|||
|
_parent = _parent.parent;
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected int m_height = 1;
|
|||
|
public int Height { get { return m_height; } }
|
|||
|
|
|||
|
protected int m_depth;
|
|||
|
public int Depth { get { return m_depth; } }
|
|||
|
|
|||
|
public bool CanExpand { get { return ChildCount > 0; } }
|
|||
|
public bool IsExpanded
|
|||
|
{
|
|||
|
get { return children != null; }
|
|||
|
set
|
|||
|
{
|
|||
|
if( IsExpanded == value )
|
|||
|
return;
|
|||
|
|
|||
|
if( value )
|
|||
|
{
|
|||
|
if( ChildCount == 0 )
|
|||
|
return;
|
|||
|
|
|||
|
PopChildrenList();
|
|||
|
}
|
|||
|
else
|
|||
|
PoolChildrenList();
|
|||
|
|
|||
|
int prevHeight = m_height;
|
|||
|
Refresh();
|
|||
|
|
|||
|
int deltaHeight = m_height - prevHeight;
|
|||
|
if( deltaHeight != 0 )
|
|||
|
{
|
|||
|
if( parent != null )
|
|||
|
{
|
|||
|
HierarchyData child = this;
|
|||
|
HierarchyData _parent = parent;
|
|||
|
while( _parent != null )
|
|||
|
{
|
|||
|
List<HierarchyDataTransform> children = _parent.children;
|
|||
|
for( int i = children.IndexOf( (HierarchyDataTransform) child ) + 1, childCount = children.Count; i < childCount; i++ )
|
|||
|
children[i].m_index += deltaHeight;
|
|||
|
|
|||
|
_parent.m_height += deltaHeight;
|
|||
|
|
|||
|
child = _parent;
|
|||
|
_parent = _parent.parent;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
HierarchyDataRoot root = Root;
|
|||
|
if( root != null )
|
|||
|
root.Hierarchy.SetListViewDirty();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool Refresh()
|
|||
|
{
|
|||
|
if( m_depth < 0 ) // This object is hidden from Hierarchy
|
|||
|
return false;
|
|||
|
|
|||
|
m_height = 1;
|
|||
|
bool hasChanged = false;
|
|||
|
int childCount = ChildCount;
|
|||
|
|
|||
|
if( IsExpanded )
|
|||
|
{
|
|||
|
if( childCount != children.Count )
|
|||
|
hasChanged = true;
|
|||
|
|
|||
|
//if( childCount == 0 ) // Issue with IsExpanded's Refresh changing iteratedIndex
|
|||
|
// PoolChildrenList();
|
|||
|
//else
|
|||
|
{
|
|||
|
RuntimeHierarchy hierarchy = null; // Root's RuntimeHierarchy will be fetched only once when it is needed
|
|||
|
for( int i = 0; i < childCount; i++ )
|
|||
|
{
|
|||
|
Transform child = GetChild( i );
|
|||
|
if( children.Count <= i )
|
|||
|
{
|
|||
|
if( hierarchy == null )
|
|||
|
hierarchy = Root.Hierarchy;
|
|||
|
|
|||
|
GenerateChildItem( child, i, hierarchy );
|
|||
|
}
|
|||
|
else if( children[i].BoundTransform != child )
|
|||
|
{
|
|||
|
int childIndex;
|
|||
|
for( childIndex = 0; childIndex < children.Count; childIndex++ )
|
|||
|
{
|
|||
|
if( children[childIndex].BoundTransform == child )
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if( childIndex == children.Count )
|
|||
|
{
|
|||
|
if( hierarchy == null )
|
|||
|
hierarchy = Root.Hierarchy;
|
|||
|
|
|||
|
GenerateChildItem( child, i, hierarchy );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
HierarchyDataTransform childItem = children[childIndex];
|
|||
|
children.RemoveAt( childIndex );
|
|||
|
children.Insert( i, childItem );
|
|||
|
}
|
|||
|
|
|||
|
hasChanged = true;
|
|||
|
}
|
|||
|
|
|||
|
hasChanged |= children[i].Refresh();
|
|||
|
children[i].m_index = m_height - 1;
|
|||
|
m_height += children[i].m_height;
|
|||
|
}
|
|||
|
|
|||
|
for( int i = children.Count - 1; i >= childCount; i-- )
|
|||
|
RemoveChildItem( i );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return hasChanged;
|
|||
|
}
|
|||
|
|
|||
|
public HierarchyData FindDataAtIndex( int index )
|
|||
|
{
|
|||
|
int upperBound = children.Count - 1;
|
|||
|
if( index <= upperBound && children[index].m_index == index )
|
|||
|
{
|
|||
|
int middle = index;
|
|||
|
while( middle < upperBound && index == children[middle + 1].m_index )
|
|||
|
middle++;
|
|||
|
|
|||
|
return children[middle];
|
|||
|
}
|
|||
|
|
|||
|
// Binary search
|
|||
|
int min = 0;
|
|||
|
int max = upperBound;
|
|||
|
while( min <= max )
|
|||
|
{
|
|||
|
int middle = ( min + max ) / 2;
|
|||
|
int childIndex = children[middle].m_index;
|
|||
|
if( index == childIndex )
|
|||
|
{
|
|||
|
// Items hidden from the Hierarchy have same indices with their adjacent items
|
|||
|
while( middle < upperBound && index == children[middle + 1].m_index )
|
|||
|
middle++;
|
|||
|
|
|||
|
return children[middle];
|
|||
|
}
|
|||
|
|
|||
|
if( index < childIndex )
|
|||
|
max = middle - 1;
|
|||
|
else
|
|||
|
min = middle + 1;
|
|||
|
}
|
|||
|
|
|||
|
if( max < 0 )
|
|||
|
max = 0;
|
|||
|
|
|||
|
while( max < upperBound && index >= children[max + 1].m_index )
|
|||
|
max++;
|
|||
|
|
|||
|
return children[max].FindDataAtIndex( index - 1 - children[max].m_index );
|
|||
|
}
|
|||
|
|
|||
|
public HierarchyDataTransform FindTransform( Transform target, Transform nextInPath = null )
|
|||
|
{
|
|||
|
if( m_depth < 0 ) // This object is hidden from Hierarchy
|
|||
|
return null;
|
|||
|
|
|||
|
bool isInitSearch = nextInPath == null;
|
|||
|
if( isInitSearch )
|
|||
|
{
|
|||
|
nextInPath = ( this is HierarchyDataRootSearch ) ? target : target.root;
|
|||
|
|
|||
|
// In the current implementation, FindTransform is only called from RuntimeHierarchy.Select which
|
|||
|
// automatically calls RefreshContent prior to FindTransform
|
|||
|
//( (HierarchyDataRoot) this ).RefreshContent();
|
|||
|
}
|
|||
|
|
|||
|
int childIndex = IndexOf( nextInPath );
|
|||
|
if( childIndex < 0 )
|
|||
|
{
|
|||
|
if( isInitSearch && this is HierarchyDataRootPseudoScene )
|
|||
|
{
|
|||
|
nextInPath = target;
|
|||
|
childIndex = IndexOf( nextInPath );
|
|||
|
while( childIndex < 0 && nextInPath != null )
|
|||
|
{
|
|||
|
nextInPath = nextInPath.parent;
|
|||
|
childIndex = IndexOf( nextInPath );
|
|||
|
}
|
|||
|
|
|||
|
if( childIndex < 0 )
|
|||
|
return null;
|
|||
|
}
|
|||
|
else
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
if( !CanExpand )
|
|||
|
return null;
|
|||
|
|
|||
|
bool wasExpanded = IsExpanded;
|
|||
|
if( !wasExpanded )
|
|||
|
IsExpanded = true;
|
|||
|
|
|||
|
HierarchyDataTransform childItem = children[childIndex];
|
|||
|
if( childItem.BoundTransform == target )
|
|||
|
return childItem;
|
|||
|
|
|||
|
HierarchyDataTransform result = null;
|
|||
|
if( childItem.BoundTransform == nextInPath )
|
|||
|
{
|
|||
|
Transform next = target;
|
|||
|
Transform parent = next.parent;
|
|||
|
while( parent != null && parent != nextInPath )
|
|||
|
{
|
|||
|
next = parent;
|
|||
|
parent = next.parent;
|
|||
|
}
|
|||
|
|
|||
|
if( parent != null )
|
|||
|
result = childItem.FindTransform( target, next );
|
|||
|
}
|
|||
|
|
|||
|
if( result != null && result.m_depth < 0 )
|
|||
|
result = null;
|
|||
|
|
|||
|
if( result == null && !wasExpanded )
|
|||
|
IsExpanded = false;
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public virtual HierarchyDataTransform FindTransformInVisibleChildren( Transform target, int targetDepth = -1 )
|
|||
|
{
|
|||
|
for( int i = 0; i < children.Count; i++ )
|
|||
|
{
|
|||
|
HierarchyDataTransform child = children[i];
|
|||
|
if( child.m_depth < 0 )
|
|||
|
continue;
|
|||
|
|
|||
|
if( ReferenceEquals( child.BoundTransform, target ) )
|
|||
|
{
|
|||
|
if( targetDepth <= 0 || child.m_depth == targetDepth )
|
|||
|
return child;
|
|||
|
}
|
|||
|
else if( ( targetDepth <= 0 || child.m_depth < targetDepth ) && child.IsExpanded && child.BoundTransform && target.IsChildOf( child.BoundTransform ) )
|
|||
|
{
|
|||
|
child = child.FindTransformInVisibleChildren( target, targetDepth );
|
|||
|
if( child != null )
|
|||
|
return child;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public abstract Transform GetChild( int index );
|
|||
|
|
|||
|
public int IndexOf( Transform transform )
|
|||
|
{
|
|||
|
for( int i = ChildCount - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( ReferenceEquals( GetChild( i ), transform ) )
|
|||
|
return i;
|
|||
|
}
|
|||
|
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
public void GetSiblingIndexTraversalList( List<int> traversalList )
|
|||
|
{
|
|||
|
traversalList.Clear();
|
|||
|
|
|||
|
HierarchyData current = this;
|
|||
|
while( current.parent != null )
|
|||
|
{
|
|||
|
traversalList.Add( current.parent.children.IndexOf( (HierarchyDataTransform) current ) );
|
|||
|
current = current.parent;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public HierarchyData TraverseSiblingIndexList( List<int> traversalList )
|
|||
|
{
|
|||
|
HierarchyData current = this;
|
|||
|
for( int i = traversalList.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
int siblingIndex = traversalList[i];
|
|||
|
if( current.children == null || siblingIndex >= current.children.Count )
|
|||
|
return null;
|
|||
|
|
|||
|
current = current.children[siblingIndex];
|
|||
|
}
|
|||
|
|
|||
|
return current;
|
|||
|
}
|
|||
|
|
|||
|
private void GenerateChildItem( Transform child, int index, RuntimeHierarchy hierarchy )
|
|||
|
{
|
|||
|
bool isChildVisible = !RuntimeInspectorUtils.IgnoredTransformsInHierarchy.Contains( child );
|
|||
|
if( isChildVisible && hierarchy.GameObjectFilter != null )
|
|||
|
isChildVisible = hierarchy.GameObjectFilter( child );
|
|||
|
|
|||
|
HierarchyDataTransform childData;
|
|||
|
int poolIndex = transformDataPool.Count - 1;
|
|||
|
if( poolIndex >= 0 )
|
|||
|
{
|
|||
|
childData = transformDataPool[poolIndex];
|
|||
|
transformDataPool.RemoveAt( poolIndex );
|
|||
|
}
|
|||
|
else
|
|||
|
childData = new HierarchyDataTransform();
|
|||
|
|
|||
|
childData.Initialize( child, this is HierarchyDataRootSearch );
|
|||
|
childData.parent = this;
|
|||
|
if( isChildVisible )
|
|||
|
{
|
|||
|
childData.m_depth = m_depth + 1;
|
|||
|
childData.m_height = 1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
childData.m_depth = -1;
|
|||
|
childData.m_height = 0;
|
|||
|
}
|
|||
|
|
|||
|
children.Insert( index, childData );
|
|||
|
}
|
|||
|
|
|||
|
private void RemoveChildItem( int index )
|
|||
|
{
|
|||
|
children[index].PoolData();
|
|||
|
transformDataPool.Add( children[index] );
|
|||
|
children.RemoveAt( index );
|
|||
|
}
|
|||
|
|
|||
|
protected void PoolChildrenList()
|
|||
|
{
|
|||
|
if( children != null )
|
|||
|
{
|
|||
|
for( int i = children.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
children[i].PoolData();
|
|||
|
transformDataPool.Add( children[i] );
|
|||
|
}
|
|||
|
|
|||
|
children.Clear();
|
|||
|
childrenListPool.Add( children );
|
|||
|
children = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected void PopChildrenList()
|
|||
|
{
|
|||
|
int desiredSize = ChildCount;
|
|||
|
int bestFittingListIndex = -1;
|
|||
|
int bestFittingListDeltaSize = int.MaxValue;
|
|||
|
for( int i = childrenListPool.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
int deltaSize = childrenListPool[i].Capacity - desiredSize;
|
|||
|
if( deltaSize < 0 )
|
|||
|
deltaSize = -deltaSize;
|
|||
|
|
|||
|
if( deltaSize < bestFittingListDeltaSize )
|
|||
|
{
|
|||
|
bestFittingListDeltaSize = deltaSize;
|
|||
|
bestFittingListIndex = i;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( bestFittingListIndex >= 0 )
|
|||
|
{
|
|||
|
children = childrenListPool[bestFittingListIndex];
|
|||
|
childrenListPool.RemoveAt( bestFittingListIndex );
|
|||
|
}
|
|||
|
else
|
|||
|
children = new List<HierarchyDataTransform>( ChildCount );
|
|||
|
}
|
|||
|
|
|||
|
public static void ClearPool()
|
|||
|
{
|
|||
|
childrenListPool.Clear();
|
|||
|
transformDataPool.Clear();
|
|||
|
|
|||
|
if( childrenListPool.Capacity > 128 )
|
|||
|
childrenListPool.Capacity = 128;
|
|||
|
if( transformDataPool.Capacity > 128 )
|
|||
|
transformDataPool.Capacity = 128;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|