[CustomEditor(typeof(Initializer))]
public class InitializerEditor : Editor
{
public override VisualElement CreateInspectorGUI()
{
VisualElement inspectorElement = new VisualElement();
// Draw the default UI of the base class.
inspectorElement.Add(new IMGUIContainer(() => DrawDefaultInspector()));
// Store the reference to the object this editor is modifying
Initializer initializer = (Initializer)target;
Dictionary<string, Type> ghostParts = GetSubtypesOf<AbstractGhostPart>();
List<string> ghostPartNames = ghostParts.Keys.ToList();
if (ghostPartNames.Count == 0)
{
inspectorElement.Add(new HelpBox("Could not detect any implementations of AbstractGhostPart in the project. Check your Assembly Definition references, and ensure you have a class that inherits from AbstractGhostPart.", HelpBoxMessageType.Error));
initializer.ghostPartType = null;
} else
{
int selectedIndex = 0;
//If there is no GhostPart set, set it to the first element in the dropdown, which is what will be selected by default.
if (initializer.ghostPartType == null || !initializer.ghostPartType.IsSubclassOf(typeof(AbstractGhostPart)))
initializer.ghostPartType = ghostParts[ghostPartNames[0]];
else
selectedIndex = ghostPartNames.IndexOf(initializer.ghostPartType.Name);
DropdownField ghostPartDropdown = new DropdownField("GhostPart Type", ghostPartNames, selectedIndex);
ghostPartDropdown.RegisterValueChangedCallback(evt => initializer.ghostPartType = ghostParts[evt.newValue]);
inspectorElement.Add(ghostPartDropdown);
}
return inspectorElement;
}
/// <summary>
/// Gets a list of all types in the project that are subtypes of a given type.
/// </summary>
/// <typeparam name="TypeToCheck">The type to look for subtypes of. Note that a Type is not a subtype of itself. This Type will not be returned.</typeparam>
/// <returns>A Dictionary from name to Type of the Types that are subtypes of the given type</returns>
private Dictionary<string, Type> GetSubtypesOf<TypeToCheck>()
{
Dictionary<string, Type> availableGhostPartTypes = new();
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
Type[] types = assembly.GetTypes();
foreach (Type t in types)
{
if (isOfType(t, typeof(TypeToCheck)))
{
availableGhostPartTypes[t.Name] = t;
}
}
}
return availableGhostPartTypes;
}
/// <summary>
/// Checks if the given Type is a sub-type of the given Parent Type.
/// Checks the given Type's immediate base type, and if it doesn't match, recurses up the Type tree until we run out or find a match.
/// </summary>
/// <param name="originalType">The Type we want to check</param>
/// <param name="parentTypeToCheck">The Original Parent Type we want to see if the given Type is a sub-type of</param>
/// <returns>True if originalType is a sub-type of parentTypeToCheck. False if otherwise. Not that if originalType _is_ the parent type, this will intentionally return false.</returns>
private static bool isOfType(Type originalType, Type parentTypeToCheck)
{
Type t = originalType.BaseType;
while (t != null)
{
if (t.Equals(parentTypeToCheck)) return true;
t = t.BaseType;
}
return false;
}
}
}