- Home /
Can I create MenuItem from code?
I can create MenuItem using the attribute on a static method; I was wondering, is it possible to add menu items in editor code?
Eg. Run through all types of certain basetype through reflection, add creation menuitem for every non-abstract type
Would like create $$anonymous$$enuItems from code to create a menu of cameras in the scene. I don't really need/want a separate function per camera. Would prefer each menu item could call the function with a param.
I think the easiest for you would be to create a custom EditorWindow - then you can fill it anything you want. You can then dock it somewhere in the corner
Answer by Pangamini · Oct 25, 2017 at 11:45 AM
So I made a boo macro that does what I needed. Not exactly adding menuItems in runtime, but at least dynamically during compilation; Since C# doesn't support any kind of macros, here's my example class written in BOO. It has to be placed in Editor folder (so the runtime assemblies are already loaded and can be looked into) This particular case creates a MenuItem creation method for every class that derives from Framework.Entity and isn't abstract.
namespace Framework.Editor
macro GenerateMenuItems:
for assembly in System.AppDomain.CurrentDomain.GetAssemblies():
try:
types = assembly.GetTypes();
except e:
continue
for type in types:
baseType = Framework.Entity
if type.IsAbstract: continue
if baseType.IsAssignableFrom(type):
typeName = type.Name;
methodName = "MenuItem_$(typeName)"
menuPath = "GameObject/Entity/$typeName"
yield [|
[UnityEditor.MenuItem($menuPath)]
public static def $methodName():
Framework.Entity.CreateEntity_Editor[of $type]()
return
|]
class EntityCreationMenu:
GenerateMenuItems
After the compilation, the class (decompiled using ILSpy) looks like this:
[Serializable]
public class EntityCreationMenu
{
[MenuItem("GameObject/Entity/Entity")]
public static void MenuItem_Entity()
{
Entity.CreateEntity_Editor<Entity>();
}
[MenuItem("GameObject/Entity/TriggerDelay")]
public static void MenuItem_TriggerDelay()
{
Entity.CreateEntity_Editor<TriggerDelay>();
}
[MenuItem("GameObject/Entity/TriggerIsDead")]
public static void MenuItem_TriggerIsDead()
{
Entity.CreateEntity_Editor<TriggerIsDead>();
}
[MenuItem("GameObject/Entity/TriggerOnEnter")]
public static void MenuItem_TriggerOnEnter()
{
Entity.CreateEntity_Editor<TriggerOnEnter>();
}
//...................... and so on
BTW you can have the test of the project written in C# or any other language
Wow, nice feature in boo. Yes, that simplifies the dynamical code generation quite a bit ^^.
ps: Are boo script compiled after the C# scripts? Because "System.AppDomain.CurrentDomain.GetAssemblies" would access the "old" C# assembly otherwise.
edit
Silly me, of course this is an editor script ^^ so it's compiled after the runtime assemblies...
It won't access the old assembly, because assemblies cannot be unloaded once loaded - the only way (and that's probably what unity is doing) is to $$anonymous$$r down the whole AppDomain and assemble a new one - so in the worst case there would be no assemblies loaded. But I think Boos are loaded after c#s and even if not, this is an editor assembly and those are loaded after runtime assemblies anyway, so no threat there.
Btw the feature is super amazing, you can do similar thing with attributes and access the whole compiled AST, allowing you to create new and modify existing classes, their members, method bodies, anything there is. Such a shame that boo's tool support is so weak
Answer by Bunny83 · Oct 25, 2017 at 10:53 AM
No that's not possible. Attributes are part of the compiled code. They are simply metadata stored directly in the compiled code, So you can't add MenuItems dynamically. Even if it would be possible since they are just metadata they don't do anything on their own. The Unity editor is searching for the attributes when the code has finished compilation and build the menus based on that metadata. This is only done once after the code has been compiled or a an asset import as been completed.
For such a feature Unity would need to add an API to the editor to query, delete and add menu items dynamically which unfortunately doesn't exist (yet).
Well ofc I wouldn't expect this to be done with attributes in any way. But I guess the answer is that there's no API for that.
I wonder if Unity does this search everytime a new assmebly is loaded ( so that I could hack around this by generating assembly with attributes inside :-P )
In theory you could create some sort of post processor code that runs after Unity has finished compilation / importing assets which searches for your target classes and create a seperate auto generated script file which simply holds a static wrapper method and an attribute on it for every class. However you have to make sure you do not update the file every time as writing the file would cause another recompile. So you would need to compare the current methods / attributes with the class list.
If you do something like that it might be better to simply add a menuItem which allows the user to trigger this process manually rather than having it run automatically.
I think I might hack that using boo, because it supports syntactic macros. That way I can generate these static methods during compilation without modifying any scripts during the process
Answer by web · Jan 30, 2020 at 10:51 PM
You can't add an arbitrary number of menu items AFAIK, and I'd like to be wrong about that. This, BTW, seems like basic stuff the Editor should have supported from Day 1. Anyway, those strings in the MenuItem attribute don't have to be baked in. They can be static string vars, you can change them programatically. So if you know how many you'll have, you can fill them in on the fly.
Your answer
Follow this Question
Related Questions
How to change order of dropdowns in menu item? 1 Answer
Make a pop up panel asking if I am sure I want to buy a specific item 0 Answers
MenuItem Glitch: Editor Script MenuItem Calling Wrong Option From Unity Help Menu 0 Answers
Unity Editor project explorer question 1 Answer
Editor scripting - How to show the Assets menu as a context menu 1 Answer