2024-06-23 19:12:22 +01:00

144 lines
5.3 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.Linq;
using System.Reflection;
using UnityEditor.IMGUI.Controls;
using MBT;
namespace MBTEditor
{
public class ClassTypeDropdownItem : AdvancedDropdownItem
{
public Type classType;
public int order;
public string path;
public ClassTypeDropdownItem(string name, Type type = null, int order = 1000, string path = "") : base(name)
{
this.classType = type;
this.order = order;
this.path = path;
}
}
public class NodeDropdown : AdvancedDropdown
{
protected Action<ClassTypeDropdownItem> Callback;
public NodeDropdown(AdvancedDropdownState state, Action<ClassTypeDropdownItem> callback) : base(state)
{
this.Callback = callback;
minimumSize = new Vector2(230,320);
}
protected override AdvancedDropdownItem BuildRoot()
{
var root = new ClassTypeDropdownItem("Nodes");
// List for all found subclasses
List<Type> results = new List<Type>();
// Search all assemblies
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
// Find all subclasses of Node
IEnumerable<Type> enumerable = assembly.GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(Node)));
results.AddRange(enumerable);
}
// Keep track of all paths to correctly build tree later
Dictionary<string, ClassTypeDropdownItem> nodePathsDictionary = new Dictionary<string, ClassTypeDropdownItem>();
nodePathsDictionary.Add("", root);
// Create list of items
List<ClassTypeDropdownItem> items = new List<ClassTypeDropdownItem>();
foreach (Type type in results)
{
if(type.IsDefined(typeof(MBTNode), false))
{
MBTNode nodeMeta = type.GetCustomAttribute<MBTNode>();
string itemName;
string nodePath = "";
if (String.IsNullOrEmpty(nodeMeta.name))
{
itemName = type.Name;
}
else
{
string[] path = nodeMeta.name.Split('/');
itemName = path[path.Length-1];
nodePath = BuildPathIfNotExists(path, ref nodePathsDictionary);
}
ClassTypeDropdownItem classTypeDropdownItem = new ClassTypeDropdownItem(itemName, type, nodeMeta.order, nodePath);
if (nodeMeta.icon != null)
{
classTypeDropdownItem.icon = Resources.Load(nodeMeta.icon, typeof(Texture2D)) as Texture2D;
}
items.Add(classTypeDropdownItem);
}
}
// Sort items
items.Sort((x, y) => {
int result = x.order.CompareTo(y.order);
return result != 0 ? result : x.name.CompareTo(y.name);
});
// Add all nodes to menu
for (int i = 0; i < items.Count; i++)
{
nodePathsDictionary[items[i].path].AddChild(items[i]);
}
// Remove root to avoid infinite root folder loop
nodePathsDictionary.Remove("");
List<ClassTypeDropdownItem> parentNodes = nodePathsDictionary.Values.ToList();
parentNodes.Sort((x, y) => {
return x.name.CompareTo(y.name);
});
// Add folders
for (int i = 0; i < parentNodes.Count(); i++)
{
root.AddChild(parentNodes[i]);
}
return root;
}
protected override void ItemSelected(AdvancedDropdownItem item)
{
Callback(item as ClassTypeDropdownItem);
}
/// <summary>
/// Creates nodes if path does not exists. Supports only signle level folders.
/// </summary>
/// <param name="path">Path to build. Last element should be actual node name.</param>
/// <param name="dictionary">Reference to dictionary to store references to items</param>
/// <returns>Path to provided node in path</returns>
protected string BuildPathIfNotExists(string[] path, ref Dictionary<string, ClassTypeDropdownItem> dictionary)
{
// IMPORTANT: This code supports only single level folders. Nodes can't be nested more than one level.
if (path.Length != 2)
{
return "";
}
AdvancedDropdownItem root = dictionary[""];
// // This code assumes the last element of path is actual name of node
// string nodePath = String.Join("/", path, 0, path.Length-1);
string nodePath = path[0];
// Create path nodes if does not exists
if(!dictionary.ContainsKey(nodePath))
{
ClassTypeDropdownItem node = new ClassTypeDropdownItem(nodePath);
dictionary.Add(nodePath, node);
}
return nodePath;
}
}
}