using System.Collections.Generic;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Interactables;
using UnityEngine.XR.Interaction.Toolkit.Interactors;
namespace UnityEditor.XR.Interaction.Toolkit
{
///
/// Multi-column that shows Interactors.
///
class XRInteractorsTreeView : TreeView
{
public static XRInteractorsTreeView Create(List interactionManagers, ref TreeViewState treeState, ref MultiColumnHeaderState headerState)
{
if (treeState == null)
treeState = new TreeViewState();
var newHeaderState = CreateHeaderState();
if (headerState != null)
MultiColumnHeaderState.OverwriteSerializedFields(headerState, newHeaderState);
headerState = newHeaderState;
var header = new MultiColumnHeader(headerState);
return new XRInteractorsTreeView(interactionManagers, treeState, header);
}
const float k_RowHeight = 20f;
const int k_LayerSize = 32;
const string k_LayerMaskOn = "\u25A0";
const string k_LayerMaskOff = "\u25A1";
class Item : TreeViewItem
{
public IXRInteractor interactor;
}
enum ColumnId
{
Name,
Type,
LayerMask,
LayerMaskList,
HoverActive,
SelectActive,
HoverInteractable,
SelectInteractable,
ValidTargets,
Count,
}
static bool exitingPlayMode => EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode;
readonly List m_InteractionManagers = new List();
readonly List m_Targets = new List();
static MultiColumnHeaderState CreateHeaderState()
{
var columns = new MultiColumnHeaderState.Column[(int)ColumnId.Count];
columns[(int)ColumnId.Name] = new MultiColumnHeaderState.Column
{
width = 180f,
minWidth = 60f,
headerContent = EditorGUIUtility.TrTextContent("Name"),
};
columns[(int)ColumnId.Type] = new MultiColumnHeaderState.Column
{
width = 120f,
minWidth = 60f,
headerContent = EditorGUIUtility.TrTextContent("Type"),
};
columns[(int)ColumnId.LayerMask] = new MultiColumnHeaderState.Column
{
width = 240f,
minWidth = 80f,
headerContent = EditorGUIUtility.TrTextContent("Layer Mask")
};
columns[(int)ColumnId.LayerMaskList] = new MultiColumnHeaderState.Column
{
width = 120f,
minWidth = 80f,
headerContent = EditorGUIUtility.TrTextContent("Layer Mask List")
};
columns[(int)ColumnId.HoverActive] = new MultiColumnHeaderState.Column
{
width = 120f,
headerContent = EditorGUIUtility.TrTextContent("Hover Active"),
};
columns[(int)ColumnId.SelectActive] = new MultiColumnHeaderState.Column
{
width = 120f,
headerContent = EditorGUIUtility.TrTextContent("Select Active"),
};
columns[(int)ColumnId.HoverInteractable] = new MultiColumnHeaderState.Column
{
width = 140f,
headerContent = EditorGUIUtility.TrTextContent("Hover Interactable"),
};
columns[(int)ColumnId.SelectInteractable] = new MultiColumnHeaderState.Column
{
width = 140f,
headerContent = EditorGUIUtility.TrTextContent("Select Interactable"),
};
columns[(int)ColumnId.ValidTargets] = new MultiColumnHeaderState.Column
{
width = 140f,
headerContent = EditorGUIUtility.TrTextContent("Valid Targets"),
};
return new MultiColumnHeaderState(columns);
}
XRInteractorsTreeView(List managers, TreeViewState state, MultiColumnHeader header)
: base(state, header)
{
foreach (var manager in managers)
AddManager(manager);
showBorder = false;
rowHeight = k_RowHeight;
Reload();
}
public void UpdateManagersList(List currentManagers)
{
var managerListChanged = false;
// Check for Removal
for (var i = 0; i < m_InteractionManagers.Count; i++)
{
var manager = m_InteractionManagers[i];
if (!currentManagers.Contains(manager))
{
RemoveManager(manager);
managerListChanged = true;
--i;
}
}
// Check for Add
foreach (var manager in currentManagers)
{
if (!m_InteractionManagers.Contains(manager))
{
AddManager(manager);
managerListChanged = true;
}
}
if (managerListChanged)
Reload();
}
void AddManager(XRInteractionManager manager)
{
if (m_InteractionManagers.Contains(manager))
return;
manager.interactorRegistered += OnInteractorRegistered;
manager.interactorUnregistered += OnInteractorUnregistered;
m_InteractionManagers.Add(manager);
Reload();
}
void RemoveManager(XRInteractionManager manager)
{
if (!m_InteractionManagers.Contains(manager))
return;
if (manager != null)
{
manager.interactorRegistered -= OnInteractorRegistered;
manager.interactorUnregistered -= OnInteractorUnregistered;
}
m_InteractionManagers.Remove(manager);
Reload();
}
void OnInteractorRegistered(InteractorRegisteredEventArgs eventArgs)
{
Reload();
}
void OnInteractorUnregistered(InteractorUnregisteredEventArgs eventArgs)
{
// Skip reloading as each interactor is being destroyed when exiting Play mode
if (!exitingPlayMode)
Reload();
}
///
protected override TreeViewItem BuildRoot()
{
// Wrap root control in invisible item required by TreeView.
return new Item
{
id = 0,
children = BuildInteractableTree(),
depth = -1,
};
}
List BuildInteractableTree()
{
var items = new List();
var interactors = new List();
foreach (var interactionManager in m_InteractionManagers)
{
if (interactionManager == null)
continue;
var rootTreeItem = new Item
{
id = XRInteractionDebuggerWindow.GetUniqueTreeViewId(interactionManager),
displayName = XRInteractionDebuggerWindow.GetDisplayName(interactionManager),
depth = 0,
};
// Build children.
interactionManager.GetRegisteredInteractors(interactors);
if (interactors.Count > 0)
{
var children = new List();
foreach (var interactor in interactors)
{
var childItem = new Item
{
id = XRInteractionDebuggerWindow.GetUniqueTreeViewId(interactor),
displayName = XRInteractionDebuggerWindow.GetDisplayName(interactor),
interactor = interactor,
depth = 1,
parent = rootTreeItem,
};
children.Add(childItem);
}
// Sort children by name.
children.Sort((a, b) => string.Compare(a.displayName, b.displayName));
rootTreeItem.children = children;
}
items.Add(rootTreeItem);
}
return items;
}
///
protected override void RowGUI(RowGUIArgs args)
{
if (!Application.isPlaying || exitingPlayMode)
return;
var item = (Item)args.item;
var columnCount = args.GetNumVisibleColumns();
for (var i = 0; i < columnCount; ++i)
{
ColumnGUI(args.GetCellRect(i), item, args.GetColumn(i), ref args);
}
}
void ColumnGUI(Rect cellRect, Item item, int column, ref RowGUIArgs args)
{
CenterRectUsingSingleLineHeight(ref cellRect);
if (column == (int)ColumnId.Name)
{
args.rowRect = cellRect;
base.RowGUI(args);
}
if (item.interactor != null)
{
var selectInteractor = item.interactor as IXRSelectInteractor;
var hoverInteractor = item.interactor as IXRHoverInteractor;
switch (column)
{
case (int)ColumnId.Type:
GUI.Label(cellRect, item.interactor.GetType().Name);
break;
case (int)ColumnId.LayerMask:
GUI.Label(cellRect, XRInteractionDebuggerWindow.GetLayerMaskDisplay(k_LayerSize, item.interactor.interactionLayers.value, k_LayerMaskOn, k_LayerMaskOff));
break;
case (int)ColumnId.LayerMaskList:
var activeLayers = XRInteractionDebuggerWindow.GetActiveLayers(k_LayerSize, item.interactor.interactionLayers.value);
GUI.Label(cellRect, string.Join(", ", activeLayers));
break;
case (int)ColumnId.HoverActive:
if (hoverInteractor != null)
GUI.Label(cellRect, hoverInteractor.isHoverActive.ToString());
break;
case (int)ColumnId.SelectActive:
if (selectInteractor != null)
GUI.Label(cellRect, selectInteractor.isSelectActive.ToString());
break;
case (int)ColumnId.HoverInteractable:
if (hoverInteractor?.interactablesHovered.Count > 0)
GUI.Label(cellRect, XRInteractionDebuggerWindow.JoinNames(",", hoverInteractor.interactablesHovered));
break;
case (int)ColumnId.SelectInteractable:
if (selectInteractor?.interactablesSelected.Count > 0)
GUI.Label(cellRect, XRInteractionDebuggerWindow.JoinNames(",", selectInteractor.interactablesSelected));
break;
case (int)ColumnId.ValidTargets:
item.interactor.GetValidTargets(m_Targets);
if (m_Targets.Count > 0)
GUI.Label(cellRect, XRInteractionDebuggerWindow.JoinNames(",", m_Targets));
break;
}
}
}
///
protected override void DoubleClickedItem(int id)
{
base.DoubleClickedItem(id);
EditorGUIUtility.PingObject(id);
Selection.activeInstanceID = id;
}
}
}