1683 lines
49 KiB
C#
1683 lines
49 KiB
C#
|
using System.Collections.Generic;
|
|||
|
using System.Collections.ObjectModel;
|
|||
|
using UnityEngine;
|
|||
|
using UnityEngine.EventSystems;
|
|||
|
using UnityEngine.SceneManagement;
|
|||
|
using UnityEngine.UI;
|
|||
|
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
|||
|
using UnityEngine.InputSystem;
|
|||
|
#endif
|
|||
|
|
|||
|
namespace RuntimeInspectorNamespace
|
|||
|
{
|
|||
|
public class RuntimeHierarchy : SkinnedWindow, IListViewAdapter, ITooltipManager
|
|||
|
{
|
|||
|
[System.Flags]
|
|||
|
public enum SelectOptions { None = 0, Additive = 1, FocusOnSelection = 2, ForceRevealSelection = 4 };
|
|||
|
public enum LongPressAction { None = 0, CreateDraggedReferenceItem = 1, ShowMultiSelectionToggles = 2, ShowMultiSelectionTogglesThenCreateDraggedReferenceItem = 3 };
|
|||
|
|
|||
|
public delegate void SelectionChangedDelegate( ReadOnlyCollection<Transform> selection );
|
|||
|
public delegate void DoubleClickDelegate( HierarchyData clickedItem );
|
|||
|
public delegate bool GameObjectFilterDelegate( Transform transform );
|
|||
|
|
|||
|
#pragma warning disable 0649
|
|||
|
[SerializeField]
|
|||
|
private float m_refreshInterval = 0f;
|
|||
|
public float RefreshInterval
|
|||
|
{
|
|||
|
get { return m_refreshInterval; }
|
|||
|
set { m_refreshInterval = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private float m_objectNamesRefreshInterval = 10f;
|
|||
|
public float ObjectNamesRefreshInterval
|
|||
|
{
|
|||
|
get { return m_objectNamesRefreshInterval; }
|
|||
|
set { m_objectNamesRefreshInterval = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private float m_searchRefreshInterval = 5f;
|
|||
|
public float SearchRefreshInterval
|
|||
|
{
|
|||
|
get { return m_searchRefreshInterval; }
|
|||
|
set { m_searchRefreshInterval = value; }
|
|||
|
}
|
|||
|
|
|||
|
private float nextHierarchyRefreshTime = -1f;
|
|||
|
private float nextObjectNamesRefreshTime = -1f;
|
|||
|
private float nextSearchRefreshTime = -1f;
|
|||
|
|
|||
|
[Space]
|
|||
|
[SerializeField]
|
|||
|
private bool m_allowMultiSelection = true;
|
|||
|
public bool AllowMultiSelection
|
|||
|
{
|
|||
|
get { return m_allowMultiSelection; }
|
|||
|
set
|
|||
|
{
|
|||
|
if( m_allowMultiSelection != value )
|
|||
|
{
|
|||
|
m_allowMultiSelection = value;
|
|||
|
|
|||
|
if( !value )
|
|||
|
{
|
|||
|
MultiSelectionToggleSelectionMode = false;
|
|||
|
|
|||
|
if( m_currentSelection.Count > 1 )
|
|||
|
{
|
|||
|
for( int i = m_currentSelection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( m_currentSelection[i] )
|
|||
|
{
|
|||
|
singleTransformSelection[0] = m_currentSelection[i];
|
|||
|
SelectInternal( singleTransformSelection );
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DeselectInternal( null );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool m_multiSelectionToggleSelectionMode = false;
|
|||
|
private bool justActivatedMultiSelectionToggleSelectionMode = false;
|
|||
|
public bool MultiSelectionToggleSelectionMode
|
|||
|
{
|
|||
|
get { return m_multiSelectionToggleSelectionMode; }
|
|||
|
set
|
|||
|
{
|
|||
|
if( !m_allowMultiSelection )
|
|||
|
value = false;
|
|||
|
|
|||
|
if( m_multiSelectionToggleSelectionMode != value )
|
|||
|
{
|
|||
|
m_multiSelectionToggleSelectionMode = value;
|
|||
|
shouldRecalculateContentWidth = true;
|
|||
|
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf )
|
|||
|
drawers[i].MultiSelectionToggleVisible = value;
|
|||
|
}
|
|||
|
|
|||
|
deselectAllButton.gameObject.SetActive( value );
|
|||
|
|
|||
|
if( !value )
|
|||
|
EnsureScrollViewIsWithinBounds();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[Space]
|
|||
|
[SerializeField]
|
|||
|
private bool m_exposeUnityScenes = true;
|
|||
|
public bool ExposeUnityScenes
|
|||
|
{
|
|||
|
get { return m_exposeUnityScenes; }
|
|||
|
set
|
|||
|
{
|
|||
|
if( m_exposeUnityScenes != value )
|
|||
|
{
|
|||
|
m_exposeUnityScenes = value;
|
|||
|
|
|||
|
for( int i = 0; i < SceneManager.sceneCount; i++ )
|
|||
|
{
|
|||
|
if( value )
|
|||
|
OnSceneLoaded( SceneManager.GetSceneAt( i ), LoadSceneMode.Single );
|
|||
|
else
|
|||
|
OnSceneUnloaded( SceneManager.GetSceneAt( i ) );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs( "exposedScenes" )]
|
|||
|
private string[] exposedUnityScenesSubset;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private bool m_exposeDontDestroyOnLoadScene = true;
|
|||
|
public bool ExposeDontDestroyOnLoadScene
|
|||
|
{
|
|||
|
get { return m_exposeDontDestroyOnLoadScene; }
|
|||
|
set
|
|||
|
{
|
|||
|
if( m_exposeDontDestroyOnLoadScene != value )
|
|||
|
{
|
|||
|
m_exposeDontDestroyOnLoadScene = value;
|
|||
|
|
|||
|
if( value )
|
|||
|
OnSceneLoaded( GetDontDestroyOnLoadScene(), LoadSceneMode.Single );
|
|||
|
else
|
|||
|
OnSceneUnloaded( GetDontDestroyOnLoadScene() );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private string[] pseudoScenesOrder;
|
|||
|
|
|||
|
[Space]
|
|||
|
[SerializeField]
|
|||
|
private LongPressAction m_pointerLongPressAction = LongPressAction.CreateDraggedReferenceItem;
|
|||
|
public LongPressAction PointerLongPressAction
|
|||
|
{
|
|||
|
get { return m_pointerLongPressAction; }
|
|||
|
set { m_pointerLongPressAction = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs( "m_draggedReferenceHoldTime" )]
|
|||
|
private float m_pointerLongPressDuration = 0.4f;
|
|||
|
public float PointerLongPressDuration
|
|||
|
{
|
|||
|
get { return m_pointerLongPressDuration; }
|
|||
|
set { m_pointerLongPressDuration = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private float m_doubleClickThreshold = 0.5f;
|
|||
|
public float DoubleClickThreshold
|
|||
|
{
|
|||
|
get { return m_doubleClickThreshold; }
|
|||
|
set { m_doubleClickThreshold = value; }
|
|||
|
}
|
|||
|
|
|||
|
[Space]
|
|||
|
[SerializeField]
|
|||
|
private bool m_canReorganizeItems = false;
|
|||
|
public bool CanReorganizeItems
|
|||
|
{
|
|||
|
get { return m_canReorganizeItems; }
|
|||
|
set { m_canReorganizeItems = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private bool m_canDropDraggedParentOnChild = false;
|
|||
|
public bool CanDropDraggedParentOnChild
|
|||
|
{
|
|||
|
get { return m_canDropDraggedParentOnChild; }
|
|||
|
set { m_canDropDraggedParentOnChild = value; }
|
|||
|
}
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private bool m_canDropDraggedObjectsToPseudoScenes = false;
|
|||
|
public bool CanDropDraggedObjectsToPseudoScenes
|
|||
|
{
|
|||
|
get { return m_canDropDraggedObjectsToPseudoScenes; }
|
|||
|
set { m_canDropDraggedObjectsToPseudoScenes = value; }
|
|||
|
}
|
|||
|
|
|||
|
[Space]
|
|||
|
[SerializeField]
|
|||
|
private bool m_showTooltips;
|
|||
|
public bool ShowTooltips { get { return m_showTooltips; } }
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private float m_tooltipDelay = 0.5f;
|
|||
|
public float TooltipDelay
|
|||
|
{
|
|||
|
get { return m_tooltipDelay; }
|
|||
|
set { m_tooltipDelay = value; }
|
|||
|
}
|
|||
|
|
|||
|
internal TooltipListener TooltipListener { get; private set; }
|
|||
|
|
|||
|
[Space]
|
|||
|
[SerializeField]
|
|||
|
private bool m_showHorizontalScrollbar;
|
|||
|
public bool ShowHorizontalScrollbar
|
|||
|
{
|
|||
|
get { return m_showHorizontalScrollbar; }
|
|||
|
set
|
|||
|
{
|
|||
|
if( m_showHorizontalScrollbar != value )
|
|||
|
{
|
|||
|
m_showHorizontalScrollbar = value;
|
|||
|
|
|||
|
if( !value )
|
|||
|
{
|
|||
|
scrollView.content.sizeDelta = new Vector2( 0f, scrollView.content.sizeDelta.y );
|
|||
|
scrollView.horizontalNormalizedPosition = 0f;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf )
|
|||
|
drawers[i].RefreshName();
|
|||
|
}
|
|||
|
|
|||
|
shouldRecalculateContentWidth = true;
|
|||
|
}
|
|||
|
|
|||
|
scrollView.horizontal = value;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public string SearchTerm
|
|||
|
{
|
|||
|
get { return searchInputField.text; }
|
|||
|
set { searchInputField.text = value; }
|
|||
|
}
|
|||
|
|
|||
|
private bool m_isInSearchMode = false;
|
|||
|
public bool IsInSearchMode { get { return m_isInSearchMode; } }
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
[SerializeField]
|
|||
|
private bool syncSelectionWithEditorHierarchy = false;
|
|||
|
#endif
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private RuntimeInspector m_connectedInspector;
|
|||
|
public RuntimeInspector ConnectedInspector
|
|||
|
{
|
|||
|
get { return m_connectedInspector; }
|
|||
|
set
|
|||
|
{
|
|||
|
if( m_connectedInspector != value )
|
|||
|
{
|
|||
|
m_connectedInspector = value;
|
|||
|
|
|||
|
for( int i = m_currentSelection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( m_currentSelection[i] )
|
|||
|
{
|
|||
|
m_connectedInspector.Inspect( m_currentSelection[i].gameObject );
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool m_isLocked = false;
|
|||
|
public bool IsLocked
|
|||
|
{
|
|||
|
get { return m_isLocked; }
|
|||
|
set { m_isLocked = value; }
|
|||
|
}
|
|||
|
|
|||
|
[Header( "Internal Variables" )]
|
|||
|
[SerializeField]
|
|||
|
private ScrollRect scrollView;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private RectTransform drawArea;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private RecycledListView listView;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Image background;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Image verticalScrollbar;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Image horizontalScrollbar;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private InputField searchInputField;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Image searchIcon;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Image searchInputFieldBackground;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private LayoutElement searchBarLayoutElement;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Button deselectAllButton;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private LayoutElement deselectAllLayoutElement;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Text deselectAllLabel;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Image selectedPathBackground;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Text selectedPathText;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private HierarchyDragDropListener dragDropListener;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private HierarchyField drawerPrefab;
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Sprite m_sceneDrawerBackground;
|
|||
|
internal Sprite SceneDrawerBackground { get { return m_sceneDrawerBackground; } }
|
|||
|
|
|||
|
[SerializeField]
|
|||
|
private Sprite m_transformDrawerBackground;
|
|||
|
internal Sprite TransformDrawerBackground { get { return m_transformDrawerBackground; } }
|
|||
|
#pragma warning restore 0649
|
|||
|
|
|||
|
private static int aliveHierarchies = 0;
|
|||
|
|
|||
|
private bool initialized = false;
|
|||
|
|
|||
|
private readonly List<HierarchyField> drawers = new List<HierarchyField>( 32 );
|
|||
|
|
|||
|
private readonly List<HierarchyDataRoot> sceneData = new List<HierarchyDataRoot>( 8 );
|
|||
|
private readonly List<HierarchyDataRoot> searchSceneData = new List<HierarchyDataRoot>( 8 );
|
|||
|
private readonly Dictionary<string, HierarchyDataRootPseudoScene> pseudoSceneDataLookup = new Dictionary<string, HierarchyDataRootPseudoScene>();
|
|||
|
|
|||
|
private readonly List<Transform> m_currentSelection = new List<Transform>( 16 );
|
|||
|
public ReadOnlyCollection<Transform> CurrentSelection { get { return m_currentSelection.AsReadOnly(); } }
|
|||
|
|
|||
|
// Stores the selected Transforms' instanceIDs. An object's instanceID can be retrieved via GetInstanceID() but it
|
|||
|
// makes an unnecessary "EnsureRunningOnMainThread()" call. Luckily, GetHashCode() also returns the instanceID and
|
|||
|
// it doesn't call "EnsureRunningOnMainThread()"
|
|||
|
private readonly HashSet<int> currentSelectionSet = new HashSet<int>();
|
|||
|
private readonly HashSet<int> newSelectionSet = new HashSet<int>();
|
|||
|
|
|||
|
#pragma warning disable 0414 // Value is assigned but never used on Android & iOS
|
|||
|
private Transform multiSelectionPivotTransform;
|
|||
|
private HierarchyDataRoot multiSelectionPivotSceneData;
|
|||
|
private readonly List<int> multiSelectionPivotSiblingIndexTraversalList = new List<int>( 8 );
|
|||
|
#pragma warning restore 0414
|
|||
|
|
|||
|
private readonly Transform[] singleTransformSelection = new Transform[1];
|
|||
|
|
|||
|
private int totalItemCount;
|
|||
|
internal int ItemCount { get { return totalItemCount; } }
|
|||
|
|
|||
|
private bool selectLock = false;
|
|||
|
private bool isListViewDirty = true;
|
|||
|
private bool shouldRecalculateContentWidth;
|
|||
|
|
|||
|
private float lastClickTime;
|
|||
|
private HierarchyField lastClickedDrawer;
|
|||
|
private HierarchyField currentlyPressedDrawer;
|
|||
|
private float pressedDrawerDraggedReferenceCreateTime;
|
|||
|
private PointerEventData pressedDrawerActivePointer;
|
|||
|
|
|||
|
private Canvas m_canvas;
|
|||
|
public Canvas Canvas { get { return m_canvas; } }
|
|||
|
|
|||
|
private float m_autoScrollSpeed;
|
|||
|
internal float AutoScrollSpeed { set { m_autoScrollSpeed = value; } }
|
|||
|
|
|||
|
// Used to make sure that the scrolled content always remains within the scroll view's boundaries
|
|||
|
private PointerEventData nullPointerEventData;
|
|||
|
|
|||
|
public SelectionChangedDelegate OnSelectionChanged;
|
|||
|
public DoubleClickDelegate OnItemDoubleClicked;
|
|||
|
|
|||
|
private GameObjectFilterDelegate m_gameObjectDelegate;
|
|||
|
public GameObjectFilterDelegate GameObjectFilter
|
|||
|
{
|
|||
|
get { return m_gameObjectDelegate; }
|
|||
|
set
|
|||
|
{
|
|||
|
m_gameObjectDelegate = value;
|
|||
|
|
|||
|
for( int i = 0; i < sceneData.Count; i++ )
|
|||
|
{
|
|||
|
if( sceneData[i].IsExpanded )
|
|||
|
{
|
|||
|
sceneData[i].IsExpanded = false;
|
|||
|
sceneData[i].IsExpanded = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( m_isInSearchMode )
|
|||
|
{
|
|||
|
for( int i = 0; i < searchSceneData.Count; i++ )
|
|||
|
{
|
|||
|
if( searchSceneData[i].IsExpanded )
|
|||
|
{
|
|||
|
searchSceneData[i].IsExpanded = false;
|
|||
|
searchSceneData[i].IsExpanded = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int IListViewAdapter.Count { get { return totalItemCount; } }
|
|||
|
float IListViewAdapter.ItemHeight { get { return Skin.LineHeight; } }
|
|||
|
|
|||
|
protected override void Awake()
|
|||
|
{
|
|||
|
base.Awake();
|
|||
|
Initialize();
|
|||
|
}
|
|||
|
|
|||
|
private void Initialize()
|
|||
|
{
|
|||
|
if( initialized )
|
|||
|
return;
|
|||
|
|
|||
|
initialized = true;
|
|||
|
|
|||
|
listView.SetAdapter( this );
|
|||
|
|
|||
|
aliveHierarchies++;
|
|||
|
|
|||
|
m_canvas = GetComponentInParent<Canvas>();
|
|||
|
nullPointerEventData = new PointerEventData( null );
|
|||
|
|
|||
|
searchInputField.onValueChanged.AddListener( OnSearchTermChanged );
|
|||
|
deselectAllButton.onClick.AddListener( () =>
|
|||
|
{
|
|||
|
DeselectInternal( null );
|
|||
|
MultiSelectionToggleSelectionMode = false;
|
|||
|
} );
|
|||
|
|
|||
|
m_showHorizontalScrollbar = !m_showHorizontalScrollbar;
|
|||
|
ShowHorizontalScrollbar = !m_showHorizontalScrollbar;
|
|||
|
|
|||
|
if( m_showTooltips )
|
|||
|
{
|
|||
|
TooltipListener = gameObject.AddComponent<TooltipListener>();
|
|||
|
TooltipListener.Initialize( this );
|
|||
|
}
|
|||
|
|
|||
|
RuntimeInspectorUtils.IgnoredTransformsInHierarchy.Add( drawArea );
|
|||
|
|
|||
|
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
|||
|
// On new Input System, scroll sensitivity is much higher than legacy Input system
|
|||
|
scrollView.scrollSensitivity *= 0.25f;
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
private void Start()
|
|||
|
{
|
|||
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|||
|
SceneManager.sceneUnloaded += OnSceneUnloaded;
|
|||
|
|
|||
|
if( ExposeUnityScenes )
|
|||
|
{
|
|||
|
for( int i = 0; i < SceneManager.sceneCount; i++ )
|
|||
|
OnSceneLoaded( SceneManager.GetSceneAt( i ), LoadSceneMode.Single );
|
|||
|
}
|
|||
|
|
|||
|
if( ExposeDontDestroyOnLoadScene )
|
|||
|
OnSceneLoaded( GetDontDestroyOnLoadScene(), LoadSceneMode.Single );
|
|||
|
}
|
|||
|
|
|||
|
private void OnDestroy()
|
|||
|
{
|
|||
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|||
|
SceneManager.sceneUnloaded -= OnSceneUnloaded;
|
|||
|
|
|||
|
if( --aliveHierarchies == 0 )
|
|||
|
HierarchyData.ClearPool();
|
|||
|
|
|||
|
RuntimeInspectorUtils.IgnoredTransformsInHierarchy.Remove( drawArea );
|
|||
|
}
|
|||
|
|
|||
|
private void OnRectTransformDimensionsChange()
|
|||
|
{
|
|||
|
shouldRecalculateContentWidth = true;
|
|||
|
}
|
|||
|
|
|||
|
private void OnTransformParentChanged()
|
|||
|
{
|
|||
|
m_canvas = GetComponentInParent<Canvas>();
|
|||
|
}
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
private void OnEnable()
|
|||
|
{
|
|||
|
UnityEditor.Selection.selectionChanged -= OnEditorSelectionChanged;
|
|||
|
UnityEditor.Selection.selectionChanged += OnEditorSelectionChanged;
|
|||
|
}
|
|||
|
|
|||
|
private void OnDisable()
|
|||
|
{
|
|||
|
UnityEditor.Selection.selectionChanged -= OnEditorSelectionChanged;
|
|||
|
}
|
|||
|
|
|||
|
private void OnEditorSelectionChanged()
|
|||
|
{
|
|||
|
if( !syncSelectionWithEditorHierarchy )
|
|||
|
return;
|
|||
|
|
|||
|
Transform[] selection = UnityEditor.Selection.GetFiltered<Transform>( UnityEditor.SelectionMode.ExcludePrefab );
|
|||
|
if( selection.Length > 0 )
|
|||
|
Select( selection );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
protected override void Update()
|
|||
|
{
|
|||
|
base.Update();
|
|||
|
|
|||
|
float time = Time.realtimeSinceStartup;
|
|||
|
if( time > nextHierarchyRefreshTime )
|
|||
|
Refresh();
|
|||
|
if( m_isInSearchMode && time > nextSearchRefreshTime )
|
|||
|
RefreshSearchResults();
|
|||
|
|
|||
|
if( isListViewDirty )
|
|||
|
RefreshListView();
|
|||
|
|
|||
|
if( time > nextObjectNamesRefreshTime )
|
|||
|
{
|
|||
|
nextObjectNamesRefreshTime = time + m_objectNamesRefreshInterval;
|
|||
|
|
|||
|
for( int i = sceneData.Count - 1; i >= 0; i-- )
|
|||
|
sceneData[i].ResetCachedNames();
|
|||
|
|
|||
|
for( int i = searchSceneData.Count - 1; i >= 0; i-- )
|
|||
|
searchSceneData[i].ResetCachedNames();
|
|||
|
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf )
|
|||
|
drawers[i].RefreshName();
|
|||
|
}
|
|||
|
|
|||
|
shouldRecalculateContentWidth = true;
|
|||
|
}
|
|||
|
|
|||
|
if( m_showHorizontalScrollbar && shouldRecalculateContentWidth )
|
|||
|
{
|
|||
|
shouldRecalculateContentWidth = false;
|
|||
|
|
|||
|
float preferredWidth = 0f;
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf )
|
|||
|
{
|
|||
|
float drawerWidth = drawers[i].PreferredWidth;
|
|||
|
if( drawerWidth > preferredWidth )
|
|||
|
preferredWidth = drawerWidth;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( m_multiSelectionToggleSelectionMode && drawers.Count > 0 )
|
|||
|
preferredWidth += Skin.LineHeight;
|
|||
|
|
|||
|
float contentMinWidth = listView.ViewportWidth + scrollView.verticalScrollbarSpacing;
|
|||
|
if( preferredWidth > contentMinWidth )
|
|||
|
scrollView.content.sizeDelta = new Vector2( preferredWidth - contentMinWidth, scrollView.content.sizeDelta.y );
|
|||
|
else
|
|||
|
scrollView.content.sizeDelta = new Vector2( 0f, scrollView.content.sizeDelta.y );
|
|||
|
|
|||
|
EnsureScrollViewIsWithinBounds();
|
|||
|
}
|
|||
|
|
|||
|
if( m_pointerLongPressAction != LongPressAction.None && currentlyPressedDrawer && time > pressedDrawerDraggedReferenceCreateTime )
|
|||
|
{
|
|||
|
if( currentlyPressedDrawer.gameObject.activeSelf && currentlyPressedDrawer.Data.BoundTransform )
|
|||
|
{
|
|||
|
if( m_pointerLongPressAction == LongPressAction.CreateDraggedReferenceItem || ( m_pointerLongPressAction == LongPressAction.ShowMultiSelectionTogglesThenCreateDraggedReferenceItem && ( !m_allowMultiSelection || m_multiSelectionToggleSelectionMode ) ) )
|
|||
|
{
|
|||
|
Transform[] draggedReferences = currentlyPressedDrawer.IsSelected ? m_currentSelection.ToArray() : new Transform[1] { currentlyPressedDrawer.Data.BoundTransform };
|
|||
|
if( draggedReferences.Length > 1 )
|
|||
|
{
|
|||
|
// The held drawer's Transform should be the first Transform in the array so that it'll be the Transform that will be received
|
|||
|
// by RuntimeInspector's object reference drawers
|
|||
|
int currentlyPressedDrawerIndex = System.Array.IndexOf( draggedReferences, currentlyPressedDrawer.Data.BoundTransform );
|
|||
|
if( currentlyPressedDrawerIndex > 0 )
|
|||
|
{
|
|||
|
for( int i = currentlyPressedDrawerIndex; i > 0; i-- )
|
|||
|
draggedReferences[i] = draggedReferences[i - 1];
|
|||
|
|
|||
|
draggedReferences[0] = currentlyPressedDrawer.Data.BoundTransform;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( RuntimeInspectorUtils.CreateDraggedReferenceItem( draggedReferences, pressedDrawerActivePointer, Skin, m_canvas ) )
|
|||
|
( (IPointerEnterHandler) dragDropListener ).OnPointerEnter( pressedDrawerActivePointer );
|
|||
|
}
|
|||
|
else if( m_allowMultiSelection && !m_multiSelectionToggleSelectionMode )
|
|||
|
{
|
|||
|
if( currentSelectionSet.Add( currentlyPressedDrawer.Data.BoundTransform.GetHashCode() ) )
|
|||
|
{
|
|||
|
m_currentSelection.Add( currentlyPressedDrawer.Data.BoundTransform );
|
|||
|
currentlyPressedDrawer.IsSelected = true;
|
|||
|
|
|||
|
OnCurrentSelectionChanged();
|
|||
|
}
|
|||
|
|
|||
|
MultiSelectionToggleSelectionMode = true;
|
|||
|
justActivatedMultiSelectionToggleSelectionMode = true;
|
|||
|
|
|||
|
// Tooltip may accidentally appear while long pressing the drawer, hide the tooltip in that case
|
|||
|
if( TooltipListener )
|
|||
|
TooltipListener.OnDrawerHovered( null, null, false );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
currentlyPressedDrawer = null;
|
|||
|
pressedDrawerActivePointer = null;
|
|||
|
}
|
|||
|
|
|||
|
if( m_autoScrollSpeed != 0f )
|
|||
|
scrollView.verticalNormalizedPosition = Mathf.Clamp01( scrollView.verticalNormalizedPosition + m_autoScrollSpeed * Time.unscaledDeltaTime / totalItemCount );
|
|||
|
}
|
|||
|
|
|||
|
public void Refresh()
|
|||
|
{
|
|||
|
nextHierarchyRefreshTime = Time.realtimeSinceStartup + m_refreshInterval;
|
|||
|
|
|||
|
bool hasChanged = false;
|
|||
|
for( int i = 0; i < sceneData.Count; i++ )
|
|||
|
hasChanged |= sceneData[i].Refresh();
|
|||
|
|
|||
|
if( hasChanged )
|
|||
|
isListViewDirty = true;
|
|||
|
else
|
|||
|
{
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf )
|
|||
|
drawers[i].Refresh();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void RefreshListView()
|
|||
|
{
|
|||
|
isListViewDirty = false;
|
|||
|
|
|||
|
totalItemCount = 0;
|
|||
|
if( !m_isInSearchMode )
|
|||
|
{
|
|||
|
for( int i = sceneData.Count - 1; i >= 0; i-- )
|
|||
|
totalItemCount += sceneData[i].Height;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
for( int i = searchSceneData.Count - 1; i >= 0; i-- )
|
|||
|
totalItemCount += searchSceneData[i].Height;
|
|||
|
}
|
|||
|
|
|||
|
listView.UpdateList( false );
|
|||
|
EnsureScrollViewIsWithinBounds();
|
|||
|
}
|
|||
|
|
|||
|
internal void SetListViewDirty()
|
|||
|
{
|
|||
|
isListViewDirty = true;
|
|||
|
}
|
|||
|
|
|||
|
public void RefreshSearchResults()
|
|||
|
{
|
|||
|
if( !m_isInSearchMode )
|
|||
|
return;
|
|||
|
|
|||
|
nextSearchRefreshTime = Time.realtimeSinceStartup + m_searchRefreshInterval;
|
|||
|
|
|||
|
for( int i = 0; i < searchSceneData.Count; i++ )
|
|||
|
{
|
|||
|
HierarchyDataRootSearch data = (HierarchyDataRootSearch) searchSceneData[i];
|
|||
|
|
|||
|
bool wasExpandable = data.CanExpand;
|
|||
|
data.Refresh();
|
|||
|
if( data.CanExpand && !wasExpandable )
|
|||
|
data.IsExpanded = true;
|
|||
|
|
|||
|
isListViewDirty = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void RefreshNameOf( Transform target )
|
|||
|
{
|
|||
|
if( target )
|
|||
|
{
|
|||
|
Scene targetScene = target.gameObject.scene;
|
|||
|
for( int i = sceneData.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
HierarchyDataRoot data = sceneData[i];
|
|||
|
if( ( data is HierarchyDataRootPseudoScene ) || ( (HierarchyDataRootScene) data ).Scene == targetScene )
|
|||
|
sceneData[i].RefreshNameOf( target );
|
|||
|
}
|
|||
|
|
|||
|
if( m_isInSearchMode )
|
|||
|
{
|
|||
|
RefreshSearchResults();
|
|||
|
|
|||
|
for( int i = searchSceneData.Count - 1; i >= 0; i-- )
|
|||
|
searchSceneData[i].RefreshNameOf( target );
|
|||
|
}
|
|||
|
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf && drawers[i].Data.BoundTransform == target )
|
|||
|
drawers[i].RefreshName();
|
|||
|
}
|
|||
|
|
|||
|
shouldRecalculateContentWidth = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected override void RefreshSkin()
|
|||
|
{
|
|||
|
background.color = Skin.BackgroundColor;
|
|||
|
verticalScrollbar.color = Skin.ScrollbarColor;
|
|||
|
horizontalScrollbar.color = Skin.ScrollbarColor;
|
|||
|
|
|||
|
searchInputField.textComponent.SetSkinInputFieldText( Skin );
|
|||
|
searchInputFieldBackground.color = Skin.InputFieldNormalBackgroundColor.Tint( 0.08f );
|
|||
|
searchIcon.color = Skin.ButtonTextColor;
|
|||
|
searchBarLayoutElement.SetHeight( Skin.LineHeight );
|
|||
|
|
|||
|
deselectAllLayoutElement.SetHeight( Skin.LineHeight );
|
|||
|
deselectAllButton.targetGraphic.color = Skin.InputFieldInvalidBackgroundColor;
|
|||
|
deselectAllLabel.SetSkinInputFieldText( Skin );
|
|||
|
|
|||
|
selectedPathBackground.color = Skin.BackgroundColor.Tint( 0.1f );
|
|||
|
selectedPathText.SetSkinButtonText( Skin );
|
|||
|
|
|||
|
Text placeholder = searchInputField.placeholder as Text;
|
|||
|
if( placeholder != null )
|
|||
|
{
|
|||
|
float placeholderAlpha = placeholder.color.a;
|
|||
|
placeholder.SetSkinInputFieldText( Skin );
|
|||
|
|
|||
|
Color placeholderColor = placeholder.color;
|
|||
|
placeholderColor.a = placeholderAlpha;
|
|||
|
placeholder.color = placeholderColor;
|
|||
|
}
|
|||
|
|
|||
|
LayoutRebuilder.ForceRebuildLayoutImmediate( drawArea );
|
|||
|
listView.ResetList();
|
|||
|
}
|
|||
|
|
|||
|
// Makes sure that scroll view's contents are within scroll view's bounds
|
|||
|
private void EnsureScrollViewIsWithinBounds()
|
|||
|
{
|
|||
|
// When scrollbar is snapped to the very bottom of the scroll view, sometimes OnScroll alone doesn't work
|
|||
|
if( scrollView.verticalNormalizedPosition <= Mathf.Epsilon )
|
|||
|
scrollView.verticalNormalizedPosition = 0.0001f;
|
|||
|
|
|||
|
scrollView.OnScroll( nullPointerEventData );
|
|||
|
}
|
|||
|
|
|||
|
void IListViewAdapter.SetItemContent( RecycledListItem item )
|
|||
|
{
|
|||
|
if( isListViewDirty )
|
|||
|
RefreshListView();
|
|||
|
|
|||
|
HierarchyField drawer = (HierarchyField) item;
|
|||
|
HierarchyData data = GetDataAt( drawer.Position );
|
|||
|
if( data != null )
|
|||
|
{
|
|||
|
drawer.Skin = Skin;
|
|||
|
drawer.SetContent( data );
|
|||
|
drawer.IsSelected = data.BoundTransform && currentSelectionSet.Contains( data.BoundTransform.GetHashCode() );
|
|||
|
drawer.MultiSelectionToggleVisible = m_multiSelectionToggleSelectionMode;
|
|||
|
drawer.Refresh();
|
|||
|
|
|||
|
shouldRecalculateContentWidth = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void IListViewAdapter.OnItemClicked( RecycledListItem item )
|
|||
|
{
|
|||
|
HierarchyField drawer = (HierarchyField) item;
|
|||
|
if( OnItemDoubleClicked != null && drawer == lastClickedDrawer && Time.realtimeSinceStartup - lastClickTime <= m_doubleClickThreshold )
|
|||
|
{
|
|||
|
lastClickTime = 0f;
|
|||
|
OnItemDoubleClicked( lastClickedDrawer.Data );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
lastClickTime = Time.realtimeSinceStartup;
|
|||
|
lastClickedDrawer = drawer;
|
|||
|
|
|||
|
bool hasSelectionChanged = false;
|
|||
|
Transform clickedTransform = drawer.Data.BoundTransform;
|
|||
|
int clickedTransformInstanceID = clickedTransform ? clickedTransform.GetHashCode() : -1;
|
|||
|
|
|||
|
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBGL || UNITY_WSA || UNITY_WSA_10_0
|
|||
|
// When Shift key is held, all items from the pivot item to the clicked item will be selected
|
|||
|
int multiSelectionPivotIndex;
|
|||
|
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
|||
|
if( m_allowMultiSelection && FindMultiSelectionPivotAbsoluteIndex( out multiSelectionPivotIndex ) && Keyboard.current != null && Keyboard.current.shiftKey.isPressed )
|
|||
|
#else
|
|||
|
if( m_allowMultiSelection && FindMultiSelectionPivotAbsoluteIndex( out multiSelectionPivotIndex ) && ( Input.GetKey( KeyCode.LeftShift ) || Input.GetKey( KeyCode.RightShift ) ) )
|
|||
|
#endif
|
|||
|
{
|
|||
|
newSelectionSet.Clear();
|
|||
|
|
|||
|
if( clickedTransform )
|
|||
|
{
|
|||
|
newSelectionSet.Add( clickedTransformInstanceID );
|
|||
|
|
|||
|
if( currentSelectionSet.Add( clickedTransformInstanceID ) )
|
|||
|
{
|
|||
|
m_currentSelection.Add( clickedTransform );
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Add new Transforms to selection
|
|||
|
for( int i = multiSelectionPivotIndex, endIndex = drawer.Position, increment = ( multiSelectionPivotIndex < endIndex ) ? 1 : -1; i != endIndex; i += increment )
|
|||
|
{
|
|||
|
Transform newSelection = GetDataAt( i ).BoundTransform;
|
|||
|
if( !newSelection )
|
|||
|
continue;
|
|||
|
|
|||
|
int selectionInstanceID = newSelection.GetHashCode();
|
|||
|
newSelectionSet.Add( selectionInstanceID );
|
|||
|
|
|||
|
if( currentSelectionSet.Add( selectionInstanceID ) )
|
|||
|
{
|
|||
|
m_currentSelection.Add( newSelection );
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Remove old Transforms from selection
|
|||
|
for( int i = m_currentSelection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
Transform oldSelection = m_currentSelection[i];
|
|||
|
if( !oldSelection )
|
|||
|
continue;
|
|||
|
|
|||
|
int selectionInstanceID = oldSelection.GetHashCode();
|
|||
|
if( !newSelectionSet.Contains( selectionInstanceID ) )
|
|||
|
{
|
|||
|
m_currentSelection.RemoveAt( i );
|
|||
|
currentSelectionSet.Remove( selectionInstanceID );
|
|||
|
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
#endif
|
|||
|
{
|
|||
|
multiSelectionPivotTransform = clickedTransform;
|
|||
|
multiSelectionPivotSceneData = drawer.Data.Root;
|
|||
|
drawer.Data.GetSiblingIndexTraversalList( multiSelectionPivotSiblingIndexTraversalList );
|
|||
|
|
|||
|
// When in toggle selection mode or Control key is held, individual items can be multi-selected
|
|||
|
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBGL || UNITY_WSA || UNITY_WSA_10_0
|
|||
|
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
|||
|
if( m_allowMultiSelection && ( m_multiSelectionToggleSelectionMode || ( Keyboard.current != null && Keyboard.current.ctrlKey.isPressed ) ) )
|
|||
|
#else
|
|||
|
if( m_allowMultiSelection && ( m_multiSelectionToggleSelectionMode || Input.GetKey( KeyCode.LeftControl ) || Input.GetKey( KeyCode.RightControl ) ) )
|
|||
|
#endif
|
|||
|
#else
|
|||
|
if( m_allowMultiSelection && m_multiSelectionToggleSelectionMode )
|
|||
|
#endif
|
|||
|
{
|
|||
|
if( clickedTransform )
|
|||
|
{
|
|||
|
if( currentSelectionSet.Add( clickedTransformInstanceID ) )
|
|||
|
m_currentSelection.Add( clickedTransform );
|
|||
|
else
|
|||
|
{
|
|||
|
m_currentSelection.Remove( clickedTransform );
|
|||
|
currentSelectionSet.Remove( clickedTransformInstanceID );
|
|||
|
|
|||
|
if( m_currentSelection.Count == 0 )
|
|||
|
MultiSelectionToggleSelectionMode = false;
|
|||
|
}
|
|||
|
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if( clickedTransform )
|
|||
|
{
|
|||
|
if( m_currentSelection.Count != 1 || m_currentSelection[0] != clickedTransform )
|
|||
|
{
|
|||
|
m_currentSelection.Clear();
|
|||
|
currentSelectionSet.Clear();
|
|||
|
|
|||
|
m_currentSelection.Add( clickedTransform );
|
|||
|
currentSelectionSet.Add( clickedTransformInstanceID );
|
|||
|
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
else if( m_currentSelection.Count > 0 )
|
|||
|
{
|
|||
|
m_currentSelection.Clear();
|
|||
|
currentSelectionSet.Clear();
|
|||
|
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( hasSelectionChanged )
|
|||
|
{
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf )
|
|||
|
{
|
|||
|
Transform drawerTransform = drawers[i].Data.BoundTransform;
|
|||
|
if( drawerTransform )
|
|||
|
{
|
|||
|
if( drawers[i].IsSelected != currentSelectionSet.Contains( drawerTransform.GetHashCode() ) )
|
|||
|
drawers[i].IsSelected = !drawers[i].IsSelected;
|
|||
|
}
|
|||
|
else if( drawers[i].IsSelected )
|
|||
|
drawers[i].IsSelected = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
OnCurrentSelectionChanged();
|
|||
|
}
|
|||
|
|
|||
|
if( m_isInSearchMode )
|
|||
|
{
|
|||
|
bool shouldShowSearchPathText = false;
|
|||
|
for( int i = m_currentSelection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
Transform selection = m_currentSelection[i];
|
|||
|
if( selection )
|
|||
|
{
|
|||
|
// Fetch the object's path and show it in Hierarchy
|
|||
|
System.Text.StringBuilder sb = RuntimeInspectorUtils.stringBuilder;
|
|||
|
sb.Length = 0;
|
|||
|
|
|||
|
sb.AppendLine( "Path:" );
|
|||
|
while( selection )
|
|||
|
{
|
|||
|
sb.Append( " " ).AppendLine( selection.name );
|
|||
|
selection = selection.parent;
|
|||
|
}
|
|||
|
|
|||
|
selectedPathText.text = sb.Append( " " ).Append( drawer.Data.Root.Name ).ToString();
|
|||
|
shouldShowSearchPathText = true;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( selectedPathBackground.gameObject.activeSelf != shouldShowSearchPathText )
|
|||
|
selectedPathBackground.gameObject.SetActive( shouldShowSearchPathText );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private bool FindMultiSelectionPivotAbsoluteIndex( out int pivotAbsoluteIndex )
|
|||
|
{
|
|||
|
pivotAbsoluteIndex = 0;
|
|||
|
|
|||
|
if( multiSelectionPivotSceneData == null )
|
|||
|
return false;
|
|||
|
|
|||
|
bool pivotSceneDataExists = false;
|
|||
|
List<HierarchyDataRoot> sceneData = m_isInSearchMode ? searchSceneData : this.sceneData;
|
|||
|
for( int i = 0; i < sceneData.Count; i++ )
|
|||
|
{
|
|||
|
if( sceneData[i] != multiSelectionPivotSceneData )
|
|||
|
pivotAbsoluteIndex += sceneData[i].Height;
|
|||
|
else
|
|||
|
{
|
|||
|
pivotSceneDataExists = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( !pivotSceneDataExists )
|
|||
|
return false;
|
|||
|
|
|||
|
if( multiSelectionPivotSiblingIndexTraversalList.Count == 0 )
|
|||
|
return true;
|
|||
|
|
|||
|
if( !multiSelectionPivotTransform )
|
|||
|
return false;
|
|||
|
|
|||
|
// To find the pivot Transform, first try traversing its recorded sibling indices in the HierarchyDataRoot that contains it
|
|||
|
HierarchyData multiSelectionPivotData = multiSelectionPivotSceneData.TraverseSiblingIndexList( multiSelectionPivotSiblingIndexTraversalList );
|
|||
|
if( multiSelectionPivotData != null && multiSelectionPivotData.BoundTransform == multiSelectionPivotTransform )
|
|||
|
{
|
|||
|
pivotAbsoluteIndex += multiSelectionPivotData.AbsoluteIndex;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
// Either HierarchyDataRoot no longer contains the pivot Transform, or the pivot Transform's sibling index have changed. Try finding
|
|||
|
// the pivot Transform among all visible children of the HierarchyDataRoot (if it's a pseudo-scene in which a Transform can appear
|
|||
|
// more than once, try finding the pivot Transform at the same depth as it was before)
|
|||
|
multiSelectionPivotData = multiSelectionPivotSceneData.FindTransformInVisibleChildren( multiSelectionPivotTransform, ( multiSelectionPivotSceneData is HierarchyDataRootPseudoScene ) ? multiSelectionPivotSiblingIndexTraversalList.Count : -1 );
|
|||
|
if( multiSelectionPivotData != null )
|
|||
|
{
|
|||
|
pivotAbsoluteIndex += multiSelectionPivotData.AbsoluteIndex;
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
// Try finding the pivot Transform at all among all visible children of the pseudo-scene
|
|||
|
if( multiSelectionPivotSceneData is HierarchyDataRootPseudoScene )
|
|||
|
{
|
|||
|
multiSelectionPivotData = multiSelectionPivotSceneData.FindTransformInVisibleChildren( multiSelectionPivotTransform, -1 );
|
|||
|
if( multiSelectionPivotData != null )
|
|||
|
{
|
|||
|
pivotAbsoluteIndex += multiSelectionPivotData.AbsoluteIndex;
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
internal HierarchyData GetDataAt( int index )
|
|||
|
{
|
|||
|
List<HierarchyDataRoot> rootData = !m_isInSearchMode ? sceneData : searchSceneData;
|
|||
|
for( int i = 0; i < rootData.Count; i++ )
|
|||
|
{
|
|||
|
if( rootData[i].Depth < 0 )
|
|||
|
continue;
|
|||
|
|
|||
|
if( index < rootData[i].Height )
|
|||
|
return index > 0 ? rootData[i].FindDataAtIndex( index - 1 ) : rootData[i];
|
|||
|
else
|
|||
|
index -= rootData[i].Height;
|
|||
|
}
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public void OnDrawerPointerEvent( HierarchyField drawer, PointerEventData eventData, bool isPointerDown )
|
|||
|
{
|
|||
|
if( !isPointerDown )
|
|||
|
{
|
|||
|
currentlyPressedDrawer = null;
|
|||
|
pressedDrawerActivePointer = null;
|
|||
|
|
|||
|
// We have activated MultiSelectionToggleSelectionMode with this press; processing the click would result in
|
|||
|
// deselecting the Transform that we've just selected with this press because its selected state would be toggled
|
|||
|
// again inside OnItemClicked. Simply ignore the click to avoid this issue
|
|||
|
if( justActivatedMultiSelectionToggleSelectionMode )
|
|||
|
{
|
|||
|
justActivatedMultiSelectionToggleSelectionMode = false;
|
|||
|
eventData.eligibleForClick = false;
|
|||
|
}
|
|||
|
|
|||
|
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
|||
|
// On new Input System, DraggedReferenceItems aren't tracked by the PointerEventDatas that initiated them. However, when a DraggedReferenceItem is
|
|||
|
// created by holding a HierarchyField, the PointerEventData's dragged object will be set as the RuntimeHierarchy's ScrollRect. When it happens,
|
|||
|
// trying to scroll the RuntimeHierarchy by holding the DraggedReferenceItem at top/bottom edge of the ScrollRect doesn't work because scrollbar's
|
|||
|
// value is overwritten by the original PointerEventData. We can prevent this issue by stopping original PointerEventData's drag operation here
|
|||
|
if( eventData.dragging && eventData.pointerDrag == scrollView.gameObject && DraggedReferenceItem.InstanceItem )
|
|||
|
{
|
|||
|
eventData.dragging = false;
|
|||
|
eventData.pointerDrag = null;
|
|||
|
}
|
|||
|
#endif
|
|||
|
}
|
|||
|
else if( m_pointerLongPressAction != LongPressAction.None )
|
|||
|
{
|
|||
|
currentlyPressedDrawer = drawer;
|
|||
|
pressedDrawerActivePointer = eventData;
|
|||
|
pressedDrawerDraggedReferenceCreateTime = Time.realtimeSinceStartup + m_pointerLongPressDuration;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool Select( Transform selection, SelectOptions selectOptions = SelectOptions.None )
|
|||
|
{
|
|||
|
singleTransformSelection[0] = selection;
|
|||
|
return Select( singleTransformSelection, selectOptions );
|
|||
|
}
|
|||
|
|
|||
|
public bool Select( IList<Transform> selection, SelectOptions selectOptions = SelectOptions.None )
|
|||
|
{
|
|||
|
return !m_isLocked && SelectInternal( selection, selectOptions );
|
|||
|
}
|
|||
|
|
|||
|
internal bool SelectInternal( IList<Transform> selection, SelectOptions selectOptions = SelectOptions.None )
|
|||
|
{
|
|||
|
if( selectLock )
|
|||
|
return false;
|
|||
|
|
|||
|
if( selection.IsEmpty() )
|
|||
|
{
|
|||
|
DeselectInternal( null );
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
Initialize();
|
|||
|
|
|||
|
bool additive = ( selectOptions & SelectOptions.Additive ) == SelectOptions.Additive;
|
|||
|
if( !m_allowMultiSelection )
|
|||
|
{
|
|||
|
additive = false;
|
|||
|
|
|||
|
if( selection.Count > 1 )
|
|||
|
{
|
|||
|
// Assertion: At least one Transform isn't null because "selection.IsEmpty()" was false
|
|||
|
for( int i = selection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( CanSelectTransform( selection[i] ) )
|
|||
|
{
|
|||
|
singleTransformSelection[0] = selection[i];
|
|||
|
selection = singleTransformSelection;
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool hasSelectionChanged = false;
|
|||
|
if( additive )
|
|||
|
{
|
|||
|
for( int i = 0; i < selection.Count; i++ )
|
|||
|
{
|
|||
|
Transform _selection = selection[i];
|
|||
|
if( CanSelectTransform( _selection ) && currentSelectionSet.Add( _selection.GetHashCode() ) )
|
|||
|
{
|
|||
|
m_currentSelection.Add( _selection );
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
newSelectionSet.Clear();
|
|||
|
|
|||
|
// Add new Transforms to selection
|
|||
|
for( int i = 0; i < selection.Count; i++ )
|
|||
|
{
|
|||
|
Transform newSelection = selection[i];
|
|||
|
if( !CanSelectTransform( newSelection ) )
|
|||
|
continue;
|
|||
|
|
|||
|
int selectionInstanceID = newSelection.GetHashCode();
|
|||
|
newSelectionSet.Add( selectionInstanceID );
|
|||
|
|
|||
|
if( currentSelectionSet.Add( selectionInstanceID ) )
|
|||
|
{
|
|||
|
m_currentSelection.Add( newSelection );
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Remove old Transforms from selection
|
|||
|
for( int i = m_currentSelection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
Transform oldSelection = m_currentSelection[i];
|
|||
|
if( !oldSelection )
|
|||
|
continue;
|
|||
|
|
|||
|
int selectionInstanceID = oldSelection.GetHashCode();
|
|||
|
if( !newSelectionSet.Contains( selectionInstanceID ) )
|
|||
|
{
|
|||
|
m_currentSelection.RemoveAt( i );
|
|||
|
currentSelectionSet.Remove( selectionInstanceID );
|
|||
|
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( !hasSelectionChanged && ( ( selectOptions & SelectOptions.ForceRevealSelection ) != SelectOptions.ForceRevealSelection ) )
|
|||
|
return true;
|
|||
|
|
|||
|
if( hasSelectionChanged )
|
|||
|
OnCurrentSelectionChanged();
|
|||
|
|
|||
|
// Make sure that the contents of the hierarchy are up-to-date
|
|||
|
Refresh();
|
|||
|
RefreshSearchResults();
|
|||
|
|
|||
|
HierarchyDataTransform itemToFocus = null;
|
|||
|
int itemToFocusSceneDataIndex = 0;
|
|||
|
|
|||
|
// Expand the selected Transforms' parents if they are currently collapsed
|
|||
|
List<HierarchyDataRoot> sceneData = m_isInSearchMode ? searchSceneData : this.sceneData;
|
|||
|
for( int i = 0; i < m_currentSelection.Count; i++ )
|
|||
|
{
|
|||
|
Transform _selection = m_currentSelection[i];
|
|||
|
if( !_selection )
|
|||
|
continue;
|
|||
|
|
|||
|
Scene selectionScene = _selection.gameObject.scene;
|
|||
|
for( int j = 0; j < sceneData.Count; j++ )
|
|||
|
{
|
|||
|
HierarchyDataRoot data = sceneData[j];
|
|||
|
if( m_isInSearchMode || ( data is HierarchyDataRootPseudoScene ) || ( (HierarchyDataRootScene) data ).Scene == selectionScene )
|
|||
|
{
|
|||
|
HierarchyDataTransform selectionItem = data.FindTransform( _selection );
|
|||
|
if( selectionItem != null )
|
|||
|
{
|
|||
|
itemToFocus = selectionItem;
|
|||
|
itemToFocusSceneDataIndex = j;
|
|||
|
|
|||
|
// Transform may exist in multiple places (zero or one Unity scene and zero or more pseudo-scene(s)). After expanding the Transform's parent in
|
|||
|
// one of these scenes, we can call it a day. This way, we'd use less CPU-cycles but the Transform's parent in other scene(s) may not be expanded.
|
|||
|
// If performance is important and expanding the Transform's parent in a single scene is OK, then you can uncomment the line below
|
|||
|
//break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RefreshListView();
|
|||
|
|
|||
|
if( itemToFocus != null )
|
|||
|
{
|
|||
|
// Focus on the latest selected HierarchyItem
|
|||
|
if( ( selectOptions & SelectOptions.FocusOnSelection ) == SelectOptions.FocusOnSelection )
|
|||
|
{
|
|||
|
int itemIndex = itemToFocus.AbsoluteIndex;
|
|||
|
for( int i = 0; i < itemToFocusSceneDataIndex; i++ )
|
|||
|
itemIndex += sceneData[i].Height;
|
|||
|
|
|||
|
// This isn't just the drawArea but rather the RuntimeHierarchy itself because in search mode, when SelectedSearchItemPath is visible,
|
|||
|
// it will decrease the scroll view's height which may result in an incorrect viewportHeight value. This is especially obvious
|
|||
|
// when calling Select immediately after exiting the search mode
|
|||
|
LayoutRebuilder.ForceRebuildLayoutImmediate( (RectTransform) transform );
|
|||
|
|
|||
|
// Credit: https://gist.github.com/yasirkula/75ca350fb83ddcc1558d33a8ecf1483f
|
|||
|
float drawAreaHeight = drawArea.rect.height;
|
|||
|
float viewportHeight = ( (RectTransform) drawArea.parent ).rect.height;
|
|||
|
if( drawAreaHeight > viewportHeight )
|
|||
|
{
|
|||
|
float focusPoint = ( (float) itemIndex / totalItemCount ) * drawAreaHeight + Skin.LineHeight * 0.5f;
|
|||
|
scrollView.verticalNormalizedPosition = 1f - Mathf.Clamp01( ( focusPoint - viewportHeight * 0.5f ) / ( drawAreaHeight - viewportHeight ) );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// When itemToFocus isn't null, it also means that we were successfully able to expand the selection's parents in the hierarchy
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
public void Deselect()
|
|||
|
{
|
|||
|
Deselect( (IList<Transform>) null );
|
|||
|
}
|
|||
|
|
|||
|
public void Deselect( Transform deselection )
|
|||
|
{
|
|||
|
singleTransformSelection[0] = deselection;
|
|||
|
Deselect( singleTransformSelection );
|
|||
|
}
|
|||
|
|
|||
|
public void Deselect( IList<Transform> deselection )
|
|||
|
{
|
|||
|
if( !m_isLocked )
|
|||
|
DeselectInternal( deselection );
|
|||
|
}
|
|||
|
|
|||
|
internal void DeselectInternal( IList<Transform> deselection )
|
|||
|
{
|
|||
|
if( selectLock || m_currentSelection.Count == 0 )
|
|||
|
return;
|
|||
|
|
|||
|
Initialize();
|
|||
|
|
|||
|
bool hasSelectionChanged = false;
|
|||
|
if( deselection == null )
|
|||
|
{
|
|||
|
m_currentSelection.Clear();
|
|||
|
currentSelectionSet.Clear();
|
|||
|
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
for( int i = deselection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
Transform deselect = deselection[i];
|
|||
|
if( deselect && currentSelectionSet.Remove( deselect.GetHashCode() ) )
|
|||
|
{
|
|||
|
m_currentSelection.Remove( deselect );
|
|||
|
hasSelectionChanged = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( hasSelectionChanged )
|
|||
|
{
|
|||
|
for( int i = drawers.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( drawers[i].gameObject.activeSelf && drawers[i].IsSelected )
|
|||
|
drawers[i].IsSelected = false;
|
|||
|
}
|
|||
|
|
|||
|
OnCurrentSelectionChanged();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool IsSelected( Transform transform )
|
|||
|
{
|
|||
|
return transform && currentSelectionSet.Contains( transform.GetHashCode() );
|
|||
|
}
|
|||
|
|
|||
|
// Check if Transform is hidden from this RuntimeHierarchy
|
|||
|
private bool CanSelectTransform( Transform transform )
|
|||
|
{
|
|||
|
if( !transform )
|
|||
|
return false;
|
|||
|
|
|||
|
if( RuntimeInspectorUtils.IgnoredTransformsInHierarchy.Contains( transform ) || ( m_gameObjectDelegate != null && !m_gameObjectDelegate( transform ) ) )
|
|||
|
return false;
|
|||
|
|
|||
|
// When Transform passes the above checks, it doesn't necessarily mean that it is visible since one of its parents might also be hidden from this RuntimeHierarchy.
|
|||
|
// We aren't just checking if all parents of the Transform are visible; imagine the scenario where there exist an A/B/C Transform hierarchy in which A is hidden from
|
|||
|
// this RuntimeHierarchy and we're checking if C is visible. Logically, when A is hidden, its grandchild C would also be hidden implicitly. However, if there is a
|
|||
|
// pseudo-scene with either B or C as a root object, then C would be visible in that pseudo-scene because neither B nor C are explicitly hidden from this RuntimeHierarchy
|
|||
|
Transform nearestRoot = null;
|
|||
|
for( int i = 0; i < sceneData.Count; i++ )
|
|||
|
{
|
|||
|
Transform parent = sceneData[i].GetNearestRootOf( transform );
|
|||
|
if( parent && ( !nearestRoot || parent.IsChildOf( nearestRoot ) ) )
|
|||
|
nearestRoot = parent;
|
|||
|
}
|
|||
|
|
|||
|
if( !nearestRoot )
|
|||
|
return false;
|
|||
|
|
|||
|
if( nearestRoot != transform )
|
|||
|
{
|
|||
|
// Check if B is explicitly hidden from this RuntimeHierarchy in the A/B/C example above
|
|||
|
for( Transform _transform = transform.parent; _transform && _transform != nearestRoot; _transform = _transform.parent )
|
|||
|
{
|
|||
|
if( RuntimeInspectorUtils.IgnoredTransformsInHierarchy.Contains( _transform ) || ( m_gameObjectDelegate != null && !m_gameObjectDelegate( _transform ) ) )
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
private void OnCurrentSelectionChanged()
|
|||
|
{
|
|||
|
selectLock = true;
|
|||
|
try
|
|||
|
{
|
|||
|
#if UNITY_EDITOR
|
|||
|
if( syncSelectionWithEditorHierarchy )
|
|||
|
{
|
|||
|
List<GameObject> selection = new List<GameObject>( m_currentSelection.Count );
|
|||
|
for( int i = 0; i < m_currentSelection.Count; i++ )
|
|||
|
{
|
|||
|
if( m_currentSelection[i] )
|
|||
|
selection.Add( m_currentSelection[i].gameObject );
|
|||
|
}
|
|||
|
|
|||
|
UnityEditor.Selection.objects = selection.ToArray();
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
if( m_connectedInspector )
|
|||
|
{
|
|||
|
for( int i = m_currentSelection.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( m_currentSelection[i] )
|
|||
|
{
|
|||
|
m_connectedInspector.Inspect( m_currentSelection[i].gameObject );
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( OnSelectionChanged != null )
|
|||
|
OnSelectionChanged( m_currentSelection.AsReadOnly() );
|
|||
|
}
|
|||
|
catch( System.Exception e )
|
|||
|
{
|
|||
|
Debug.LogException( e );
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
selectLock = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void OnSearchTermChanged( string search )
|
|||
|
{
|
|||
|
if( search != null )
|
|||
|
search = search.Trim();
|
|||
|
|
|||
|
if( string.IsNullOrEmpty( search ) )
|
|||
|
{
|
|||
|
if( m_isInSearchMode )
|
|||
|
{
|
|||
|
for( int i = 0; i < searchSceneData.Count; i++ )
|
|||
|
searchSceneData[i].IsExpanded = false;
|
|||
|
|
|||
|
scrollView.verticalNormalizedPosition = 1f;
|
|||
|
selectedPathBackground.gameObject.SetActive( false );
|
|||
|
|
|||
|
isListViewDirty = true;
|
|||
|
m_isInSearchMode = false;
|
|||
|
|
|||
|
// Focus on currently selected object(s) after exiting search mode
|
|||
|
if( m_currentSelection.Count > 0 )
|
|||
|
SelectInternal( m_currentSelection, SelectOptions.FocusOnSelection | SelectOptions.ForceRevealSelection );
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if( !m_isInSearchMode )
|
|||
|
{
|
|||
|
scrollView.verticalNormalizedPosition = 1f;
|
|||
|
nextSearchRefreshTime = Time.realtimeSinceStartup + m_searchRefreshInterval;
|
|||
|
|
|||
|
isListViewDirty = true;
|
|||
|
m_isInSearchMode = true;
|
|||
|
|
|||
|
RefreshSearchResults();
|
|||
|
|
|||
|
for( int i = 0; i < searchSceneData.Count; i++ )
|
|||
|
searchSceneData[i].IsExpanded = true;
|
|||
|
}
|
|||
|
else
|
|||
|
RefreshSearchResults();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void OnSceneLoaded( Scene arg0, LoadSceneMode arg1 )
|
|||
|
{
|
|||
|
if( !ExposeUnityScenes || ( arg0.buildIndex >= 0 && exposedUnityScenesSubset != null && exposedUnityScenesSubset.Length > 0 && System.Array.IndexOf( exposedUnityScenesSubset, arg0.name ) == -1 ) )
|
|||
|
return;
|
|||
|
|
|||
|
if( !arg0.IsValid() )
|
|||
|
return;
|
|||
|
|
|||
|
for( int i = 0; i < sceneData.Count; i++ )
|
|||
|
{
|
|||
|
if( sceneData[i] is HierarchyDataRootScene && ( (HierarchyDataRootScene) sceneData[i] ).Scene == arg0 )
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
HierarchyDataRootScene data = new HierarchyDataRootScene( this, arg0 );
|
|||
|
data.Refresh();
|
|||
|
|
|||
|
// Unity scenes should come before pseudo-scenes
|
|||
|
int index = sceneData.Count - pseudoSceneDataLookup.Count;
|
|||
|
sceneData.Insert( index, data );
|
|||
|
searchSceneData.Insert( index, new HierarchyDataRootSearch( this, data ) );
|
|||
|
|
|||
|
isListViewDirty = true;
|
|||
|
}
|
|||
|
|
|||
|
private void OnSceneUnloaded( Scene arg0 )
|
|||
|
{
|
|||
|
for( int i = 0; i < sceneData.Count; i++ )
|
|||
|
{
|
|||
|
if( sceneData[i] is HierarchyDataRootScene && ( (HierarchyDataRootScene) sceneData[i] ).Scene == arg0 )
|
|||
|
{
|
|||
|
sceneData[i].IsExpanded = false;
|
|||
|
sceneData.RemoveAt( i );
|
|||
|
|
|||
|
searchSceneData[i].IsExpanded = false;
|
|||
|
searchSceneData.RemoveAt( i );
|
|||
|
|
|||
|
isListViewDirty = true;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private Scene GetDontDestroyOnLoadScene()
|
|||
|
{
|
|||
|
GameObject temp = null;
|
|||
|
try
|
|||
|
{
|
|||
|
temp = new GameObject();
|
|||
|
DontDestroyOnLoad( temp );
|
|||
|
Scene dontDestroyOnLoad = temp.scene;
|
|||
|
DestroyImmediate( temp );
|
|||
|
temp = null;
|
|||
|
|
|||
|
return dontDestroyOnLoad;
|
|||
|
}
|
|||
|
catch( System.Exception e )
|
|||
|
{
|
|||
|
Debug.LogException( e );
|
|||
|
return new Scene();
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
if( temp != null )
|
|||
|
DestroyImmediate( temp );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public void AddToPseudoScene( string scene, Transform transform )
|
|||
|
{
|
|||
|
GetPseudoScene( scene, true ).AddChild( transform );
|
|||
|
}
|
|||
|
|
|||
|
public void AddToPseudoScene( string scene, IEnumerable<Transform> transforms )
|
|||
|
{
|
|||
|
HierarchyDataRootPseudoScene pseudoScene = GetPseudoScene( scene, true );
|
|||
|
foreach( Transform transform in transforms )
|
|||
|
pseudoScene.AddChild( transform );
|
|||
|
}
|
|||
|
|
|||
|
public void RemoveFromPseudoScene( string scene, Transform transform, bool deleteSceneIfEmpty )
|
|||
|
{
|
|||
|
HierarchyDataRootPseudoScene pseudoScene = GetPseudoScene( scene, false );
|
|||
|
if( pseudoScene == null )
|
|||
|
return;
|
|||
|
|
|||
|
pseudoScene.RemoveChild( transform );
|
|||
|
|
|||
|
if( deleteSceneIfEmpty && pseudoScene.ChildCount == 0 )
|
|||
|
DeletePseudoScene( scene );
|
|||
|
}
|
|||
|
|
|||
|
public void RemoveFromPseudoScene( string scene, IEnumerable<Transform> transforms, bool deleteSceneIfEmpty )
|
|||
|
{
|
|||
|
HierarchyDataRootPseudoScene pseudoScene = GetPseudoScene( scene, false );
|
|||
|
if( pseudoScene == null )
|
|||
|
return;
|
|||
|
|
|||
|
foreach( Transform transform in transforms )
|
|||
|
pseudoScene.RemoveChild( transform );
|
|||
|
|
|||
|
if( deleteSceneIfEmpty && pseudoScene.ChildCount == 0 )
|
|||
|
DeletePseudoScene( scene );
|
|||
|
}
|
|||
|
|
|||
|
private HierarchyDataRootPseudoScene GetPseudoScene( string scene, bool createIfNotExists )
|
|||
|
{
|
|||
|
HierarchyDataRootPseudoScene data;
|
|||
|
if( pseudoSceneDataLookup.TryGetValue( scene, out data ) )
|
|||
|
return data;
|
|||
|
|
|||
|
if( createIfNotExists )
|
|||
|
return CreatePseudoSceneInternal( scene );
|
|||
|
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
public void CreatePseudoScene( string scene )
|
|||
|
{
|
|||
|
if( pseudoSceneDataLookup.ContainsKey( scene ) )
|
|||
|
return;
|
|||
|
|
|||
|
CreatePseudoSceneInternal( scene );
|
|||
|
}
|
|||
|
|
|||
|
private HierarchyDataRootPseudoScene CreatePseudoSceneInternal( string scene )
|
|||
|
{
|
|||
|
int index = 0;
|
|||
|
if( pseudoScenesOrder.Length > 0 )
|
|||
|
{
|
|||
|
for( int i = 0; i < pseudoScenesOrder.Length; i++ )
|
|||
|
{
|
|||
|
if( pseudoScenesOrder[i] == scene )
|
|||
|
break;
|
|||
|
|
|||
|
if( pseudoSceneDataLookup.ContainsKey( pseudoScenesOrder[i] ) )
|
|||
|
index++;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
index = pseudoSceneDataLookup.Count;
|
|||
|
|
|||
|
HierarchyDataRootPseudoScene data = new HierarchyDataRootPseudoScene( this, scene );
|
|||
|
|
|||
|
// Pseudo-scenes should come after Unity scenes
|
|||
|
index += sceneData.Count - pseudoSceneDataLookup.Count;
|
|||
|
sceneData.Insert( index, data );
|
|||
|
searchSceneData.Insert( index, new HierarchyDataRootSearch( this, data ) );
|
|||
|
pseudoSceneDataLookup[scene] = data;
|
|||
|
|
|||
|
isListViewDirty = true;
|
|||
|
return data;
|
|||
|
}
|
|||
|
|
|||
|
public void DeleteAllPseudoScenes()
|
|||
|
{
|
|||
|
for( int i = sceneData.Count - 1; i >= 0; i-- )
|
|||
|
{
|
|||
|
if( sceneData[i] is HierarchyDataRootPseudoScene )
|
|||
|
{
|
|||
|
sceneData[i].IsExpanded = false;
|
|||
|
sceneData.RemoveAt( i );
|
|||
|
|
|||
|
searchSceneData[i].IsExpanded = false;
|
|||
|
searchSceneData.RemoveAt( i );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pseudoSceneDataLookup.Clear();
|
|||
|
isListViewDirty = true;
|
|||
|
}
|
|||
|
|
|||
|
public void DeletePseudoScene( string scene )
|
|||
|
{
|
|||
|
for( int i = 0; i < sceneData.Count; i++ )
|
|||
|
{
|
|||
|
HierarchyDataRootPseudoScene pseudoScene = sceneData[i] as HierarchyDataRootPseudoScene;
|
|||
|
if( pseudoScene != null && pseudoScene.Name == scene )
|
|||
|
{
|
|||
|
pseudoSceneDataLookup.Remove( pseudoScene.Name );
|
|||
|
|
|||
|
sceneData[i].IsExpanded = false;
|
|||
|
sceneData.RemoveAt( i );
|
|||
|
|
|||
|
searchSceneData[i].IsExpanded = false;
|
|||
|
searchSceneData.RemoveAt( i );
|
|||
|
|
|||
|
isListViewDirty = true;
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
RecycledListItem IListViewAdapter.CreateItem( Transform parent )
|
|||
|
{
|
|||
|
HierarchyField result = (HierarchyField) Instantiate( drawerPrefab, parent, false );
|
|||
|
result.Initialize( this );
|
|||
|
result.Skin = Skin;
|
|||
|
|
|||
|
drawers.Add( result );
|
|||
|
return result;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|