Skip to content

Instantly share code, notes, and snippets.

@koturn
Created September 2, 2023 00:48
Show Gist options
  • Save koturn/a7c7d93724cefc03ca1be7520dbecf02 to your computer and use it in GitHub Desktop.
Save koturn/a7c7d93724cefc03ca1be7520dbecf02 to your computer and use it in GitHub Desktop.
https://qiita.com/Swanman/items/279b3b679f3f96a5f925 のリフレクション結果をキャッシュするようにしたやつ
using System;
using System.Linq.Expressions;
using System.Reflection;
using UnityEditor;
namespace Koturn
{
/// <summary>
/// Menu helper
/// </summary>
/// <remarks>
/// <seealso href="https://qiita.com/Swanman/items/279b3b679f3f96a5f925"/>
/// </remarks>
public static class MenuHelper
{
/// <summary>
/// Cache of reflection result of EditorUtility.Menu.AddMenuItem().
/// </summary>
/// <remarks>
/// <seealso cref="AddMenuItem"/>
/// <seealso cref="CreateAddMenuItemDelegate"/>
/// </remarks>
private static Action<string, string, bool, int, Action, Func<bool>> _addMenuItem;
/// <summary>
/// Cache of reflection result of EditorUtility.Menu.AddSeparator().
/// </summary>
/// <remarks>
/// <seealso cref="AddSeparator"/>
/// <seealso cref="CreateAddSeparatorDelegate"/>
/// </remarks>
private static Action<string, int> _addSeparator;
/// <summary>
/// Cache of reflection result of EditorUtility.Menu.RemoveMenuItem().
/// </summary>
/// <remarks>
/// <seealso cref="RemoveMenuItem"/>
/// <seealso cref="CreateRemoveMenuItemDelegate"/>
/// </remarks>
private static Action<string> _removeMenuItem;
/// <summary>
/// Cache of reflection result of EditorUtility.Internal_UpdateAllMenus().
/// </summary>
/// <remarks>
/// <seealso cref="UpdateAllMenus"/>
/// <seealso cref="CreateUpdateAllMenusDelegate"/>
/// </remarks>
private static Action _updateAllMenus;
/// <summary>
/// Cache of reflection result about UnityEditor.ShortcutManagement.ShortcutController.RebuildShortcuts().
/// </summary>
/// <remarks>
/// <seealso cref="RebuildShortcuts"/>
/// <seealso cref="CreateRebuildShortcutsDelegate"/>
/// </remarks>
private static Action _rebuildShortcuts;
/// <summary>
/// Add menu item.
/// </summary>
/// <param name="name">Menu name.</param>
/// <param name="priority">Menu priority.</param>
/// <param name="execute">Callback method when this menu clicked.</param>
/// <param name="validate">Validate method when this menu clicked.</param>
public static void AddMenuItem(string name, int priority, Action execute, Func<bool> validate = null)
{
AddMenuItem(name, string.Empty, false, priority, execute, validate);
}
/// <summary>
/// Add menu item.
/// </summary>
/// <param name="name">Menu name.</param>
/// <param name="shortcut">Shortcut.</param>
/// <param name="priority">Menu priority.</param>
/// <param name="execute">Callback method when this menu clicked.</param>
/// <param name="validate">Validate method when this menu clicked.</param>
public static void AddMenuItem(string name, string shortcut, int priority, Action execute, Func<bool> validate = null)
{
(_addMenuItem ?? (_addMenuItem = CreateAddMenuItemDelegate()))(name, shortcut ?? string.Empty, false, priority, execute, validate);
}
/// <summary>
/// Add menu item.
/// </summary>
/// <param name="name">Menu name.</param>
/// <param name="isChecked">A flag checkable or not.</param>
/// <param name="priority">Menu priority.</param>
/// <param name="execute">Callback method when this menu clicked.</param>
/// <param name="validate">Validate method when this menu clicked.</param>
public static void AddMenuItem(string name, bool isChecked, int priority, Action execute, Func<bool> validate = null)
{
AddMenuItem(name, string.Empty, isChecked, priority, execute, validate);
}
/// <summary>
/// Add menu item.
/// </summary>
/// <param name="name">Menu name.</param>
/// <param name="shortcut">Shortcut.</param>
/// <param name="isChecked">A flag checkable or not.</param>
/// <param name="priority">Menu priority.</param>
/// <param name="execute">Callback method when this menu clicked.</param>
/// <param name="validate">Validate method when this menu clicked.</param>
public static void AddMenuItem(string name, string shortcut, bool isChecked, int priority, Action execute, Func<bool> validate = null)
{
(_addMenuItem ?? (_addMenuItem = CreateAddMenuItemDelegate()))(name, shortcut ?? string.Empty, isChecked, priority, execute, validate);
}
/// <summary>
/// Add separator.
/// </summary>
/// <param name="name">Menu name.</param>
/// <param name="priority">Menu priority.</param>
public static void AddSeparator(string name, int priority)
{
(_addSeparator ?? (_addSeparator = CreateAddSeparatorDelegate()))(name, priority);
}
/// <summary>
/// Remove menu by name.
/// </summary>
/// <param name="name">Menu name to remove.</param>
public static void RemoveMenuItem(string name)
{
(_removeMenuItem ?? (_removeMenuItem = CreateRemoveMenuItemDelegate()))(name);
}
/// <summary>
/// Update menu items.
/// </summary>
public static void Update()
{
UpdateAllMenus();
RebuildShortcuts();
}
/// <summary>
/// Update all menus, just calling UnityEditor.EditorUtility.Internal_UpdateAllMenus().
/// </summary>
private static void UpdateAllMenus()
{
(_updateAllMenus ?? (_updateAllMenus = CreateUpdateAllMenusDelegate()))();
}
/// <summary>
/// Rebuild menu shortcuts, just calling UnityEditor.ShortcutManagement.ShortcutController.RebuildShortcuts().
/// </summary>
private static void RebuildShortcuts()
{
(_rebuildShortcuts ?? (_rebuildShortcuts = CreateRebuildShortcutsDelegate()))();
}
/// <summary>
/// <para>Create delegate of reflection results about UnityEditor.Menu.AddMenuItem().</para>
/// <code>() => Menu.AddMenuItem();</code>
/// </summary>
/// <returns>Created delegate.</returns>
private static Action<string, string, bool, int, Action, Func<bool>> CreateAddMenuItemDelegate()
{
// Arguments
var pName = Expression.Parameter(typeof(string), "name");
var pShortcut = Expression.Parameter(typeof(string), "shortcut");
var pIsChecked = Expression.Parameter(typeof(bool), "isChecked");
var pPriority = Expression.Parameter(typeof(int), "priority");
var pExecute = Expression.Parameter(typeof(Action), "execute");
var pValidate = Expression.Parameter(typeof(Func<bool>), "validate");
return Expression.Lambda<Action<string, string, bool, int, Action, Func<bool>>>(
Expression.Call(
typeof(Menu).GetMethod(
"AddMenuItem",
BindingFlags.Static | BindingFlags.NonPublic)
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.Menu.AddMenuItem"),
pName,
pShortcut,
pIsChecked,
pPriority,
pExecute,
pValidate),
"AddMenuItem",
new []
{
pName,
pShortcut,
pIsChecked,
pPriority,
pExecute,
pValidate
}).Compile();
}
/// <summary>
/// <para>Create delegate of reflection results about UnityEditor.Menu.AddSeparator().</para>
/// <code>
/// () => Menu.AddSeparator();
/// </code>
/// </summary>
/// <returns>Created delegate.</returns>
private static Action<string, int> CreateAddSeparatorDelegate()
{
// Arguments
var pName = Expression.Parameter(typeof(string), "name");
var pPriority = Expression.Parameter(typeof(int), "priority");
return Expression.Lambda<Action<string, int>>(
Expression.Call(
typeof(Menu).GetMethod(
"AddSeparator",
BindingFlags.Static | BindingFlags.NonPublic)
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.Menu.AddSeparator"),
pName,
pPriority),
"AddSeparator",
new []
{
pName,
pPriority
}).Compile();
}
/// <summary>
/// <para>Create delegate of reflection results about UnityEditor.Menu.RemoveMenuItem().</para>
/// <code>
/// () => Menu.RemoveMenuItem();
/// </code>
/// </summary>
/// <returns>Created delegate.</returns>
private static Action<string> CreateRemoveMenuItemDelegate()
{
// Arguments
var pName = Expression.Parameter(typeof(string), "name");
return Expression.Lambda<Action<string>>(
Expression.Call(
typeof(Menu).GetMethod(
"RemoveMenuItem",
BindingFlags.Static | BindingFlags.NonPublic)
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.Menu.RemoveMenuItem"),
pName),
"RemoveMenuItem",
new []
{
pName,
}).Compile();
}
/// <summary>
/// <para>Create delegate of reflection results about UnityEditor.EditorUtility.Internal_UpdateAllMenus().</para>
/// <code>
/// () => EditorUtility.Internal_UpdateAllMenus();
/// </code>
/// </summary>
/// <returns>Created delegate.</returns>
private static Action CreateUpdateAllMenusDelegate()
{
return Expression.Lambda<Action>(
Expression.Call(
typeof(EditorUtility).GetMethod(
"Internal_UpdateAllMenus",
BindingFlags.Static | BindingFlags.NonPublic)
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.EditorUtility.Internal_UpdateAllMenus")),
"UpdateAllMenus",
null).Compile();
}
/// <summary>
/// <para>Create delegate of reflection results about UnityEditor.ShortcutManagement.ShortcutController.RebuildShortcuts().</para>
/// <code>
/// () => ShortcutController.instance.RebuildShortcuts();
/// </code>
/// </summary>
/// <returns>Created delegate.</returns>
private static Action CreateRebuildShortcutsDelegate()
{
var asm = Assembly.GetAssembly(typeof(UnityEditor.Menu));
return Expression.Lambda<Action>(
Expression.Call(
Expression.Property(
null, // static property
(asm.GetType("UnityEditor.ShortcutManagement.ShortcutIntegration")
?? throw new InvalidOperationException("Type not found: UnityEditor.ShortcutManagement.ShortcutIntegration")).GetProperty(
"instance",
BindingFlags.Static | BindingFlags.Public)
?? throw new InvalidOperationException("PropertyInfo not found: UnityEditor.ShortcutManagement.ShortcutIntegration.instance")),
(asm.GetType("UnityEditor.ShortcutManagement.ShortcutController")
?? throw new InvalidOperationException("UnityEditor.ShortcutManagement.ShortcutController")).GetMethod(
"RebuildShortcuts",
BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException("MethodInfo not found: UnityEditor.ShortcutManagement.ShortcutController.RebuildShortcuts")),
"RebuildShortcuts",
null).Compile();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment