Skip to content

Instantly share code, notes, and snippets.

@nukadelic
Created September 29, 2021 23:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nukadelic/d5c64526c6404d3f3d33ed1b0d41abf9 to your computer and use it in GitHub Desktop.
Save nukadelic/d5c64526c6404d3f3d33ed1b0d41abf9 to your computer and use it in GitHub Desktop.
Unity Context Menu Editor Window
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
public class ContextMenuEditor : EditorWindow
{
[MenuItem("Tools/Edit Context Menu")] static void Init() => GetWindow<ContextMenuEditor>().Show();
System.Type UnityEditorMenuClass;
void GetMenuItemDefaultShortcuts(List<string> outItemNames, List<string> outItemDefaultShortcuts)
=> UnityEditorMenuClass?.GetMethod("GetMenuItemDefaultShortcuts", BindingFlags.Static | BindingFlags.NonPublic)
.Invoke(null, new object[] { outItemNames, outItemDefaultShortcuts });
void RemoveMenuItem(string name)
=> UnityEditorMenuClass?.GetMethod("RemoveMenuItem", BindingFlags.Static | BindingFlags.NonPublic)
.Invoke(null, new object[] { name });
class Node
{
public bool open = false;
public bool delete = false;
public string name;
public string path;
public Node parent;
public List<Node> children;
}
List<string> menuItemNames;
List<string> menuItemDefaultShortcuts;
private void OnEnable()
{
UnityEditorMenuClass = null;
System.Type[] assemblyTypes = Assembly.GetAssembly(typeof(Editor)).GetTypes();
foreach( var t in assemblyTypes ) if( t.Namespace == "UnityEditor" && t.Name == "Menu" ) UnityEditorMenuClass = t;
if (UnityEditorMenuClass == null) { broken = true; return; }
FetchMenu();
}
void FetchMenu()
{
menuItemNames = new List<string>();
menuItemDefaultShortcuts = new List<string>();
GetMenuItemDefaultShortcuts(menuItemNames, menuItemDefaultShortcuts);
root = new Node { children = new List<Node>() };
foreach (var path in menuItemNames)
{
var node = root;
var items = path.Split('/');
foreach (var item in items)
{
var found = false;
foreach (var n in node.children)
{
if (n.name == item)
{
found = true;
node = n;
break;
}
}
if (!found)
{
var n = new Node
{
children = new List<Node>(),
parent = node,
name = item,
path = path
};
node.children.Add(n);
node = n;
}
}
}
}
bool broken = false;
Node root;
Vector2 scroll;
void OnGUI()
{
if( broken ) { GUILayout.Label("This is broken, fix me !", EditorStyles.boldLabel); return; }
using( var scope = new GUILayout.ScrollViewScope( scroll ) )
{
GUILayout.Space(20);
GUILayout.Label( "Context Menu Tree: " , EditorStyles.boldLabel );
foreach (var node in root.children) DrawNode( node );
scroll = scope.scrollPosition;
}
GUILayout.Space( 10 );
if( GUILayout.Button( "Delete Selected" ) )
{
EachNode( n =>
{
if( n.delete ) RemoveMenuItem( n.path );
} );
}
}
void DrawNode( Node node, int intend = 0 )
{
using (new GUILayout.HorizontalScope())
{
GUILayout.Space( intend * 20 );
var content = new GUIContent( node.name, node.path );
if( node.children != null && node.children.Count > 0 )
{
node.open = EditorGUILayout.Foldout( node.open, content );
}
else GUILayout.Label( content );
GUILayout.FlexibleSpace();
node.delete = GUILayout.Toggle( node.delete, GUIContent.none );
}
if( node.open )
{
if( node.children != null && node.children.Count > 0 )
{
foreach (var child in node.children)
{
DrawNode( child , intend + 1 );
}
}
}
}
void EachNode(System.Action<Node> callback, Node node = null)
{
if (node == null) node = root;
foreach (var n in node.children)
{
EachNode(callback, n);
callback(n);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment