Я пытаюсь внедрить систему галочек для MenuItems
(как это делают переключатели) ( пример ) ...
В течение последних часов я исследовал, как я могу сделать это, используя самый понятный способ для пользователей, которые будут использовать мой API.
Я решил использовать атрибут для внедрения кода в метод, имеющий атрибут MenuItem
, и обнаружил, что есть несколько способов (AOP, DI, IoC ...) сделать это (но любая из библиотек). импортированные в Unity3D работали: PostSharp, KingAOP ...), один из этих методов IL-ткачество , эта библиотека уже реализует его.
Но при импорте библиотеки в мой проект возникает следующая ошибка:
PrecompiledAssemblyException: Multiple precompiled assemblies with the same name Mono.Cecil.dll included for the current platform. Only one assembly with the same name is allowed per platform. Assembly path: {0}
UnityEditor.Scripting.ScriptCompilation.EditorBuildRules.CreateTargetAssemblies (System.Collections.Generic.IEnumerable`1[T] customScriptAssemblies, System.Collections.Generic.IEnumerable`1[T] precompiledAssemblies) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorBuildRules.cs:221)
UnityEditor.Scripting.ScriptCompilation.EditorCompilation.UpdateCustomTargetAssemblies () (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:672)
UnityEditor.Scripting.ScriptCompilation.EditorCompilation.SetAllCustomScriptAssemblyJsonContents (System.String[] paths, System.String[] contents, System.String[] guids) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs:892)
UnityEditor.Scripting.ScriptCompilation.EditorCompilationInterface.SetAllCustomScriptAssemblyJsonContents (System.String[] allAssemblyJsonPaths, System.String[] allAssemblyJsonContents, System.String[] guids) (at C:/buildslave/unity/build/Editor/Mono/Scripting/ScriptCompilation/EditorCompilationInterface.cs:241)
Я уже открыл для этого вопрос.
Что я мог сделать? Я не знаю точно, что я делаю, поэтому я не могу проверить, вызвана ли проблема этим или потому, что я сделал это неправильно.
Я не знаю точно, как подключить мой новый созданный Компонент ...
Вот что я получил на данный момент:
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using UnityEditor;
using Weaver;
using Weaver.Extensions;
namespace UnityEngine.Core.Editor
{
public class RadioMenuComponent : WeaverComponent
{
public override string addinName => "Radio Menu";
private TypeReference m_InvokerTypeReference;
private MethodReference m_PerfomActionMethodRef;
public override void VisitModule(ModuleDefinition moduleDefinition)
{
// Get 'Radio Menu Invoker' type
Type invokerType = typeof(RadioMenuInvoker);
// Import the 'Radio Menu Invoker' type
m_InvokerTypeReference = moduleDefinition.Import(invokerType);
// Get the type def by resolving
TypeDefinition invokerTypeDef = m_InvokerTypeReference.Resolve();
// Get our start sample
m_PerfomActionMethodRef = invokerTypeDef.GetMethod("PerformAction", 1);
// Import everything
moduleDefinition.Import(typeof(RadioMenuAttribute));
moduleDefinition.Import(m_PerfomActionMethodRef);
}
public override void VisitMethod(MethodDefinition methodDefinition)
{
// This isn't executing... When it's supposed to be executed?
Debug.Log(methodDefinition.FullName);
// Check if we have our attribute
CustomAttribute customAttribute = methodDefinition.GetCustomAttribute<RadioMenuAttribute>();
if (customAttribute == null)
return;
// Remove the attribute
methodDefinition.CustomAttributes.Remove(customAttribute);
MethodBody body = methodDefinition.Body;
ILProcessor bodyProcessor = body.GetILProcessor();
// Start of method
{
Instruction _02 = Instruction.Create(OpCodes.Call, methodDefinition.Module.Import(m_PerfomActionMethodRef));
bodyProcessor.InsertBefore(body.Instructions[0], _02);
}
}
public class RadioMenuInvoker
{
public static void PerformAction(RadioMenuAttribute radioMenu)
{
bool enabled = radioMenu.Enabled;
// Set checkmark on menu item
Menu.SetChecked(radioMenu.Path, enabled);
// Saving editor state
EditorPrefs.SetBool(radioMenu.Path, enabled);
radioMenu.Enabled = enabled;
// Perform your logic here...
}
}
}
}
Я не уверен (потому что я не проверял), но мне нужно знать, вызываются ли где-нибудь унаследованные классы из WeaverComponent
класса ...
using System;
using System.Collections.Generic;
namespace UnityEngine.Core.Editor
{
using Extensions;
// [InitializeOnLoad]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class RadioMenuAttribute : Attribute
{
private static Dictionary<string, HashSet<RadioMenuAttribute>> m_menus = new Dictionary<string, HashSet<RadioMenuAttribute>>();
public string Path { get; private set; }
public bool Enabled { get; set; }
private RadioMenuAttribute()
{
}
public RadioMenuAttribute(string name, string path)
{
Path = path;
m_menus.AddOnce(name, new HashSet<RadioMenuAttribute>());
m_menus[name].Add(this);
}
static RadioMenuAttribute()
{
// Find here all methods with 'RadioMenuAttribute'
// Test 1 (Debug Length)
//var attrs = typeof(UnityEditor.Editor).Assembly.GetCustomAttributes<RadioMenuAttribute>();
//foreach (var attr in attrs)
//{
// attr.Enabled = EditorPrefs.GetBool(attr.Path, false);
// // Delaying until first editor tick so that the menu
// // will be populated before setting check state, and
// // re-apply correct action
//}
//EditorApplication.delayCall += () =>
//{
// foreach (var attr in attrs)
// PerformAction(attr);
//};
// Test 2 (Inject this code to all methods that contains 'RadioMenuAttribute')
/*
// Toggling action
PerformAction( !CheckmarkMenuItem.enabled_);
*/
}
}
}
using UnityEditor;
using UnityEngine.Core.Editor;
namespace UnitedTeamworkAssociation.UST_SDK.Utilities.SWW.Editor
{
using Wins;
public static class MenuItems
{
[MenuItem("Source Tools/Open Steam Workshop Wrapper Window...")]
public static void StartVectorEditor()
{
EditorWindow.GetWindow<SteamWorkshopWrapperEditorWindow>("Steam Workshop Wrapper", false, typeof(SceneView));
}
#region "Settings"
public const string BrowseSettings = "BROWSE_SETTINGS",
BrowseSettings_Opt1 = "Source Tools/Settings/Browse items in Editor browser...",
BrowseSettings_Opt2 = "Source Tools/Settings/Browse items in Default browser...";
[RadioMenu(BrowseSettings, BrowseSettings_Opt1)]
[MenuItem(BrowseSettings_Opt1)]
public static void Setting_BrowseInEditor()
{
}
[RadioMenu(BrowseSettings, BrowseSettings_Opt2)]
[MenuItem(BrowseSettings_Opt2)]
public static void Setting_BrowseInApp()
{
}
#endregion "Settings"
}
}
Мой главный вопрос здесь. Как я могу ввести следующий код:
RadioMenuInvoker.PerformAction(xxx);
Кому:
[RadioMenu(BrowseSettings, BrowseSettings_Opt1)]
[MenuItem(BrowseSettings_Opt1)]
public static void Setting_BrowseInEditor()
{
// Logic here...
}
Для вывода следующего:
[MenuItem(BrowseSettings_Opt1)]
public static void Setting_BrowseInEditor()
{
RadioMenuInvoker.PerformAction(xxx);
// Where xxx is a direct reference to the attibute?
// Logic here...
}
Но, как вы видите, я новичок в IL и Mono.Cecil ... Итак, я не могу понять это без помощи.