diff --git a/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs b/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs new file mode 100644 index 0000000..29cc61f --- /dev/null +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs @@ -0,0 +1,128 @@ +using FrooxEngine; +using MonkeyLoader.Resonite; + +using GenerationEvent = MonkeyLoader.Resonite.UI.Inspectors.InspectorMemberActionsMenuItemsGenerationEvent; + +namespace DynamicVariablePowerTools.ContextMenu +{ + internal sealed partial class DynamicVariableMemberActions + { + private static ButtonEventHandler GetDriveSyncRefFromVariable(GenerationEvent eventData, SyncRef syncRefTarget, string variable) + where T : class, IWorldElement + => (button, args) => + { + syncRefTarget.DriveFromVariable(variable); + eventData.CloseContextMenu(); + }; + + private static ButtonEventHandler GetOfferSyncRefDriveActions(GenerationEvent eventData, SyncRef syncRefTarget) + where T : class, IWorldElement + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveSyncRefFromVariable(eventData, syncRefTarget, string.Empty); + + foreach (var variable in GetAvailableVariableOptions(eventData.Slot!)) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromVariable", "variable", variable), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveSyncRefFromVariable(eventData, syncRefTarget, variable); + } + }); + }; + + private static ButtonEventHandler GetOfferSyncRefReferenceActions(GenerationEvent eventData, SyncRef syncRefTarget) + where T : class, IWorldElement + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.Blank"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceSyncRefForVariable(eventData, syncRefTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); + + foreach (var space in spaces) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.InSpace", "space", space.SpaceName), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceSyncRefForVariable(eventData, syncRefTarget, $"{space.SpaceName}/"); + } + }); + }; + + private static ButtonEventHandler GetOfferSyncRefSourceActions(GenerationEvent eventData, SyncRef syncRefTarget) + where T : class, IWorldElement + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.Blank"), SourceIcon, SourceColor) + .Button.LocalPressed += GetSourceSyncRefForVariable(eventData, syncRefTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); + + foreach (var space in spaces) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.InSpace", "space", space.SpaceName), SourceIcon, SourceColor) + .Button.LocalPressed += GetSourceSyncRefForVariable(eventData, syncRefTarget, $"{space.SpaceName}/"); + } + }); + }; + + private static ButtonEventHandler GetReferenceSyncRefForVariable(GenerationEvent eventData, ISyncRef syncRefTarget, string variable) + where T : class, IWorldElement + => (button, args) => + { + var dynamicReference = syncRefTarget.FindNearestParent().AttachComponent>>(); + dynamicReference.VariableName.Value = variable; + dynamicReference.Reference.Target = syncRefTarget; + + eventData.CloseContextMenu(); + }; + + private static ButtonEventHandler GetSourceSyncRefForVariable(GenerationEvent eventData, SyncRef syncRefTarget, string variable) + where T : class, IWorldElement + => (button, args) => + { + syncRefTarget.SyncWithVariable(variable); + eventData.CloseContextMenu(); + }; + + private static void OfferSyncRefActions(GenerationEvent eventData) + where T : class, IWorldElement + { + if (eventData.Target is not SyncRef syncRefTarget) + return; + + if (!syncRefTarget.IsLinked) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive"), DriveIcon, DriveColor) + .Button.LocalPressed += GetOfferSyncRefDriveActions(eventData, syncRefTarget); + } + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicReference"), SourceIcon, SourceColor) + .Button.LocalPressed += GetOfferSyncRefSourceActions(eventData, syncRefTarget); + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetOfferSyncRefReferenceActions(eventData, syncRefTarget); + } + } +} \ No newline at end of file diff --git a/DynamicVariablePowerTools/ContextMenu/DVMA.Types.cs b/DynamicVariablePowerTools/ContextMenu/DVMA.Types.cs new file mode 100644 index 0000000..85df0a9 --- /dev/null +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.Types.cs @@ -0,0 +1,121 @@ +using FrooxEngine; +using MonkeyLoader.Resonite; + +using GenerationEvent = MonkeyLoader.Resonite.UI.Inspectors.InspectorMemberActionsMenuItemsGenerationEvent; + +namespace DynamicVariablePowerTools.ContextMenu +{ + internal sealed partial class DynamicVariableMemberActions + { + private static ButtonEventHandler GetDriveTypeFieldFromVariable(GenerationEvent eventData, SyncType syncTypeTarget, string variable) + => (button, args) => + { + syncTypeTarget.DriveFromVariable(variable); + eventData.CloseContextMenu(); + }; + + private static ButtonEventHandler GetOfferTypeFieldDriveActions(GenerationEvent eventData, SyncType syncTypeTarget) + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveTypeFieldFromVariable(eventData, syncTypeTarget, string.Empty); + + foreach (var variable in GetAvailableVariableOptions(eventData.Slot!)) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromVariable", "variable", variable), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveTypeFieldFromVariable(eventData, syncTypeTarget, variable); + } + }); + }; + + private static ButtonEventHandler GetOfferTypeFieldReferenceActions(GenerationEvent eventData, SyncType syncTypeTarget) + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.Blank"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceTypeFieldForVariable(eventData, syncTypeTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); + + foreach (var space in spaces) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.InSpace", "space", space.SpaceName), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceTypeFieldForVariable(eventData, syncTypeTarget, $"{space.SpaceName}/"); + } + }); + }; + + private static ButtonEventHandler GetOfferTypeFieldSourceActions(GenerationEvent eventData, SyncType syncTypeTarget) + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.Blank"), SourceIcon, SourceColor) + .Button.LocalPressed += GetSourceTypeFieldForVariable(eventData, syncTypeTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); + + foreach (var space in spaces) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.InSpace", "space", space.SpaceName), SourceIcon, SourceColor) + .Button.LocalPressed += GetSourceTypeFieldForVariable(eventData, syncTypeTarget, $"{space.SpaceName}/"); + } + }); + }; + + private static ButtonEventHandler GetReferenceTypeFieldForVariable(GenerationEvent eventData, SyncType syncTypeTarget, string variable) + => (button, args) => + { + var dynamicReference = syncTypeTarget.FindNearestParent().AttachComponent>(); + dynamicReference.VariableName.Value = variable; + dynamicReference.Reference.Target = syncTypeTarget; + + eventData.CloseContextMenu(); + }; + + private static ButtonEventHandler GetSourceTypeFieldForVariable(GenerationEvent eventData, SyncType syncTypeTarget, string variable) + => (button, args) => + { + syncTypeTarget.SyncWithVariable(variable); + eventData.CloseContextMenu(); + }; + + private static void OfferTypeFieldActions(GenerationEvent eventData) + { + if (eventData.Target is not SyncType syncTypeTarget) + return; + + if (!syncTypeTarget.IsLinked) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive"), DriveIcon, DriveColor) + .Button.LocalPressed += GetOfferTypeFieldDriveActions(eventData, syncTypeTarget); + } + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicTypeField"), SourceIcon, SourceColor) + .Button.LocalPressed += GetOfferTypeFieldSourceActions(eventData, syncTypeTarget); + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetOfferTypeFieldReferenceActions(eventData, syncTypeTarget); + } + } +} \ No newline at end of file diff --git a/DynamicVariablePowerTools/ContextMenu/DVMA.Values.cs b/DynamicVariablePowerTools/ContextMenu/DVMA.Values.cs new file mode 100644 index 0000000..d9b5dae --- /dev/null +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.Values.cs @@ -0,0 +1,121 @@ +using FrooxEngine; +using MonkeyLoader.Resonite; + +using GenerationEvent = MonkeyLoader.Resonite.UI.Inspectors.InspectorMemberActionsMenuItemsGenerationEvent; + +namespace DynamicVariablePowerTools.ContextMenu +{ + internal sealed partial class DynamicVariableMemberActions + { + private static ButtonEventHandler GetDriveFieldFromVariable(GenerationEvent eventData, IField fieldTarget, string variable) + => (button, args) => + { + fieldTarget.DriveFromVariable(variable); + eventData.CloseContextMenu(); + }; + + private static ButtonEventHandler GetOfferFieldDriveActions(GenerationEvent eventData, IField fieldTarget) + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveFieldFromVariable(eventData, fieldTarget, string.Empty); + + foreach (var variable in GetAvailableVariableOptions(eventData.Slot!)) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromVariable", "variable", variable), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveFieldFromVariable(eventData, fieldTarget, variable); + } + }); + }; + + private static ButtonEventHandler GetOfferFieldReferenceActions(GenerationEvent eventData, IField fieldTarget) + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.Blank"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceFieldForVariable(eventData, fieldTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); + + foreach (var space in spaces) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.InSpace", "space", space.SpaceName), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceFieldForVariable(eventData, fieldTarget, $"{space.SpaceName}/"); + } + }); + }; + + private static ButtonEventHandler GetOfferFieldSourceActions(GenerationEvent eventData, IField fieldTarget) + => (button, args) => + { + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.Blank"), SourceIcon, SourceColor) + .Button.LocalPressed += GetSourceFieldForVariable(eventData, fieldTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); + + foreach (var space in spaces) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.InSpace", "space", space.SpaceName), SourceIcon, SourceColor) + .Button.LocalPressed += GetSourceFieldForVariable(eventData, fieldTarget, $"{space.SpaceName}/"); + } + }); + }; + + private static ButtonEventHandler GetReferenceFieldForVariable(GenerationEvent eventData, IField fieldTarget, string variable) + => (button, args) => + { + var dynamicReference = fieldTarget.FindNearestParent().AttachComponent>>(); + dynamicReference.VariableName.Value = variable; + dynamicReference.Reference.Target = fieldTarget; + + eventData.CloseContextMenu(); + }; + + private static ButtonEventHandler GetSourceFieldForVariable(GenerationEvent eventData, IField fieldTarget, string variable) + => (button, args) => + { + fieldTarget.SyncWithVariable(variable); + eventData.CloseContextMenu(); + }; + + private static void OfferFieldActions(GenerationEvent eventData) + { + if (eventData.Target is not IField fieldTarget) + return; + + if (!fieldTarget.IsLinked) + { + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive"), DriveIcon, DriveColor) + .Button.LocalPressed += GetOfferFieldDriveActions(eventData, fieldTarget); + } + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicField"), SourceIcon, SourceColor) + .Button.LocalPressed += GetOfferFieldSourceActions(eventData, fieldTarget); + + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetOfferFieldReferenceActions(eventData, fieldTarget); + } + } +} \ No newline at end of file diff --git a/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs b/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs new file mode 100644 index 0000000..637e7fe --- /dev/null +++ b/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs @@ -0,0 +1,96 @@ +using Elements.Core; +using FrooxEngine; +using HarmonyLib; +using MonkeyLoader; +using MonkeyLoader.Resonite; +using MonkeyLoader.Resonite.Configuration; +using System.Reflection; + +using GenerationEvent = MonkeyLoader.Resonite.UI.Inspectors.InspectorMemberActionsMenuItemsGenerationEvent; + +namespace DynamicVariablePowerTools.ContextMenu +{ + internal sealed partial class DynamicVariableMemberActions + : ResoniteAsyncEventHandlerMonkey + { + private static readonly Dictionary> _actionOfferersByType = new() + { + { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(OfferTypeFieldActions))) } + }; + + private static readonly MethodInfo _offerFieldActionsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(OfferFieldActions)); + private static readonly MethodInfo _offerSyncRefActionsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(OfferSyncRefActions)); + + public override bool CanBeDisabled => true; + + public override int Priority => HarmonyLib.Priority.Normal; + + private static colorX DriveColor => RadiantUI_Constants.Sub.PURPLE; + private static Uri DriveIcon => OfficialAssets.Graphics.Icons.ProtoFlux.Drive; + + private static colorX ReferenceColor => RadiantUI_Constants.Neutrals.LIGHT; + private static Uri ReferenceIcon => OfficialAssets.Graphics.Icons.ProtoFlux.Reference; + + private static colorX SourceColor => RadiantUI_Constants.Sub.CYAN; + private static Uri SourceIcon => OfficialAssets.Graphics.Icons.ProtoFlux.Source; + + protected override bool AppliesTo(GenerationEvent eventData) + // Check for existence of Slot to filter out fields on UserComponents etc. + => base.AppliesTo(eventData) && eventData.Slot is not null && eventData.Target is IField; + + protected override Task Handle(GenerationEvent eventData) + { + Action? offerActions; + + // Check ISyncRef first because those are IField + if (eventData.Target is ISyncRef syncRef) + { + if (!_actionOfferersByType.TryGetValue(syncRef.TargetType, out offerActions)) + { + offerActions = MakeMethod(_offerSyncRefActionsMethod, syncRef.TargetType); + _actionOfferersByType.Add(syncRef.TargetType, offerActions); + } + } + // This includes SyncType fields, since they're derived from SyncField and thus IField + else if (eventData.Target is IField field) + { + if (!_actionOfferersByType.TryGetValue(field.ValueType, out offerActions)) + { + offerActions = MakeMethod(_offerFieldActionsMethod, field.ValueType); + _actionOfferersByType.Add(field.ValueType, offerActions); + } + } + else + { + Logger.Warn(() => $"Tried to create inspector member action items for unsupported target: {eventData.Target.GetType().CompactDescription()}"); + return Task.CompletedTask; + } + + offerActions(eventData); + + return Task.CompletedTask; + } + + private static IEnumerable GetAvailableVariableOptions(Slot slot) + { + foreach (var identity in slot.GetAvailableVariableIdentities()) + { + if (identity.Name.StartsWith(SharedConfig.Identifier)) + continue; + + yield return !string.IsNullOrWhiteSpace(identity.Space.CurrentName) + ? $"{identity.Space.CurrentName}/{identity.Name}" + : identity.Name; + } + } + + private static Action MakeMethod(MethodInfo method, Type type) + { + method = method.MakeGenericMethod(type); + return AccessTools.MethodDelegate>(method); + } + + private static bool SpaceHasName(DynamicVariableSpace space) + => !string.IsNullOrEmpty(space.SpaceName.Value); + } +} \ No newline at end of file diff --git a/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj b/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj index 9d7d72d..e7c48c5 100644 --- a/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj +++ b/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj @@ -1,9 +1,10 @@ - + + True DynamicVariablePowerTools Dynamic Variable Power Tools Banane9 - 0.2.0-beta + 0.1.1-beta This MonkeyLoader mod for Resonite adds a variety of powerful functions to dynamic variables and their spaces. README.md LGPL-3.0-or-later @@ -13,8 +14,10 @@ - - + + + + diff --git a/DynamicVariablePowerTools/Locale/en.json b/DynamicVariablePowerTools/Locale/en.json index b8bda58..ded7a9e 100644 --- a/DynamicVariablePowerTools/Locale/en.json +++ b/DynamicVariablePowerTools/Locale/en.json @@ -1,30 +1,42 @@ { - "localeCode": "en", - "authors": [ "Banane9" ], - "messages": { - "DynamicVariablePowerTools.EnableLinkedVariablesList.Name": "Variable Definitions", - "DynamicVariablePowerTools.EnableLinkedVariablesList.Description": "Allow generating a list of all dynamic variable definitions linked to a space.", - "DynamicVariablePowerTools.EnableLinkedVariablesList.Button": "Output Variable Definitions", - "DynamicVariablePowerTools.EnableLinkedVariablesList.Tooltip": "Generates a list of all dynamic variable definitions linked to this space in the Output field.", - - "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Name": "Component Hierarchy", - "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Description": "Allow generating a hierarchical list of all dynamic variable components linked to a space.", - "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Button": "Output Component Hierarchy", - "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Tooltip": "Generates a hierarchical list of all dynamic variable components linked to this space in the Output field.", - - "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Name": "Rename Dynamic Variable Spaces", - "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Description": "Adds a button to dynamic variable spaces for renaming all linked variables.", - "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Button": "Rename", - "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Tooltip": "Renames all linked variables.", - - "DynamicVariablePowerTools.RenameDynamicVariables.Name": "Rename Dynamic Variables", - "DynamicVariablePowerTools.RenameDynamicVariables.Description": "Adds a button to dynamic variables for renaming all matching linked variables.", - "DynamicVariablePowerTools.RenameDynamicVariables.Button": "Rename", - "DynamicVariablePowerTools.RenameDynamicVariables.Tooltip": "Renames all matching linked variables.", - - "DynamicVariablePowerTools.Config.RenameOptions.Name": "Rename Options", - "DynamicVariablePowerTools.Config.RenameOptions.Description": "Options for how dynamic variables should be renamed.", - - "DynamicVariablePowerTools.DebugInfo.Name": "Debug Info" - } + "localeCode": "en", + "authors": [ "Banane9" ], + "messages": { + "DynamicVariablePowerTools.EnableLinkedVariablesList.Name": "Variable Definitions", + "DynamicVariablePowerTools.EnableLinkedVariablesList.Description": "Allow generating a list of all dynamic variable definitions linked to a space.", + "DynamicVariablePowerTools.EnableLinkedVariablesList.Button": "Output Variable Definitions", + "DynamicVariablePowerTools.EnableLinkedVariablesList.Tooltip": "Generates a list of all dynamic variable definitions linked to this space in the Output field.", + + "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Name": "Component Hierarchy", + "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Description": "Allow generating a hierarchical list of all dynamic variable components linked to a space.", + "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Button": "Output Component Hierarchy", + "DynamicVariablePowerTools.EnableLinkedComponentHierarchy.Tooltip": "Generates a hierarchical list of all dynamic variable components linked to this space in the Output field.", + + "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Name": "Rename Dynamic Variable Spaces", + "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Description": "Adds a button to dynamic variable spaces for renaming all linked variables.", + "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Button": "Rename", + "DynamicVariablePowerTools.RenameDirectlyLinkedVariables.Tooltip": "Renames all linked variables.", + + "DynamicVariablePowerTools.RenameDynamicVariables.Name": "Rename Dynamic Variables", + "DynamicVariablePowerTools.RenameDynamicVariables.Description": "Adds a button to dynamic variables for renaming all matching linked variables.", + "DynamicVariablePowerTools.RenameDynamicVariables.Button": "Rename", + "DynamicVariablePowerTools.RenameDynamicVariables.Tooltip": "Renames all matching linked variables.", + + "DynamicVariablePowerTools.Config.RenameOptions.Name": "Rename Options", + "DynamicVariablePowerTools.Config.RenameOptions.Description": "Options for how dynamic variables should be renamed.", + + "DynamicVariablePowerTools.DebugInfo.Name": "Debug Info", + + "DynamicVariablePowerTools.Drive": "Drive from Dynamic Variable", + "DynamicVariablePowerTools.Drive.FromBlank": "From [Blank]", + "DynamicVariablePowerTools.Drive.FromVariable": "From {variable}", + + "DynamicVariablePowerTools.Source": "Set up {type}", + "DynamicVariablePowerTools.Source.Blank": "[Blank]", + "DynamicVariablePowerTools.Source.InSpace": "In space {space}", + + "DynamicVariablePowerTools.Reference": "Set up Reference Variable", + "DynamicVariablePowerTools.Reference.Blank": "[Blank]", + "DynamicVariablePowerTools.Reference.InSpace": "In space {space}" + } } \ No newline at end of file diff --git a/DynamicVariablePowerTools/SetupVariableMemberActions.cs b/DynamicVariablePowerTools/SetupVariableMemberActions.cs deleted file mode 100644 index da75477..0000000 --- a/DynamicVariablePowerTools/SetupVariableMemberActions.cs +++ /dev/null @@ -1,150 +0,0 @@ -using FrooxEngine; -using HarmonyLib; -using MonkeyLoader; -using MonkeyLoader.Resonite; -using MonkeyLoader.Resonite.UI.Inspectors; -using System.Reflection; - -namespace DynamicVariablePowerTools -{ - internal sealed class SetupVariableMemberActions - : ResoniteAsyncEventHandlerMonkey - { - private static readonly MethodInfo _createFieldItemsMethod = AccessTools.DeclaredMethod(typeof(SetupVariableMemberActions), nameof(CreateFieldItems)); - private static readonly MethodInfo _createSyncRefItemsMethod = AccessTools.DeclaredMethod(typeof(SetupVariableMemberActions), nameof(CreateSyncRefItems)); - - private static readonly Dictionary> _itemCreatorsByType = new() - { - { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(SetupVariableMemberActions), nameof(CreateTypeFieldItems))) } - }; - - public override bool CanBeDisabled => true; - public override int Priority => HarmonyLib.Priority.Normal; - - protected override bool AppliesTo(InspectorMemberActionsMenuItemsGenerationEvent eventData) - => base.AppliesTo(eventData) && eventData.Target is IField; - - protected override Task Handle(InspectorMemberActionsMenuItemsGenerationEvent eventData) - { - Action? createItems = null; - - // Check ISyncRef first because those are IField - if (eventData.Target is ISyncRef syncRef) - { - if (!_itemCreatorsByType.TryGetValue(syncRef.TargetType, out createItems)) - { - createItems = MakeMethod(_createSyncRefItemsMethod, syncRef.TargetType); - _itemCreatorsByType.Add(syncRef.TargetType, createItems); - } - } - else if (eventData.Target is IField field) - { - if (!_itemCreatorsByType.TryGetValue(field.ValueType, out createItems)) - { - createItems = MakeMethod(_createFieldItemsMethod, field.ValueType); - _itemCreatorsByType.Add(field.ValueType, createItems); - } - } - else - { - Logger.Warn(() => $"Tried to create inspector member action items for unsupported target: {eventData.Target.GetType().CompactDescription()}"); - return Task.CompletedTask; - } - - createItems(eventData); - - return Task.CompletedTask; - } - - private static void CreateFieldItems(InspectorMemberActionsMenuItemsGenerationEvent eventData) - { - var menuItem = eventData.ContextMenu.AddItem("Set up DynamicField", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (button, args) => - { - // Swap to eventData.Worker when updated - var slot = eventData.Target.FindNearestParent(); - var dynamicField = slot.AttachComponent>(); - dynamicField.TargetField.Target = (IField)eventData.Target; - - button.World.LocalUser.CloseContextMenu(eventData.Summoner); - }; - - menuItem = eventData.ContextMenu.AddItem("Drive from Dynamic Variable", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (button, args) => - { - button.World.LocalUser.CloseContextMenu(eventData.Summoner); - - button.Slot.StartTask(async () => - { - await button.World.LocalUser.OpenContextMenu(eventData.Summoner, args.source.Slot); - - // Need to check dynamic variable spaces hiding eachother - // Also use full space/varName for drive - var slot = eventData.Target.FindNearestParent(); - var options = slot.GetComponentsInParents() - .SelectMany(space => space._dynamicValues.Keys.Where(variable => typeof(T).IsAssignableFrom(variable.type))) - .ToArray(); - - var menuItem2 = eventData.ContextMenu.AddItem("Blank", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem2.Button.LocalPressed += (button2, args2) => - { - var driver = slot.AttachComponent>(); - driver.Target.Target = (IField)eventData.Target; - button.World.LocalUser.CloseContextMenu(eventData.Summoner); - }; - - foreach (var option in options) - { - var menuItem3 = eventData.ContextMenu.AddItem(option.name, (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem3.Button.LocalPressed += (button2, args2) => - { - ((IField)eventData.Target).DriveFromVariable(option.name); - button.World.LocalUser.CloseContextMenu(eventData.Summoner); - }; - } - }); - }; - } - - private static void CreateSyncRefItems(InspectorMemberActionsMenuItemsGenerationEvent eventData) - where T : class, IWorldElement - { - if (eventData.Target is not SyncRef syncRefTarget) - return; - - var menuItem = eventData.ContextMenu.AddItem("Set up DynamicReference", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (sender, args) => - { - // Swap to eventData.Worker when updated - var slot = eventData.Target.FindNearestParent(); - var dynamicReference = slot.AttachComponent>(); - dynamicReference.TargetReference.Target = syncRefTarget; - }; - } - - private static void CreateTypeFieldItems(InspectorMemberActionsMenuItemsGenerationEvent eventData) - { - if (eventData.Target is not SyncType syncTypeTarget) - return; - - var menuItem = eventData.ContextMenu.AddItem("Set up DynamicTypeField", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (sender, args) => - { - // Swap to eventData.Worker when updated - var slot = eventData.Target.FindNearestParent(); - var dynamicReference = slot.AttachComponent(); - dynamicReference.TargetField.Target = syncTypeTarget; - }; - } - - private static Action MakeMethod(MethodInfo method, Type type) - { - method = method.MakeGenericMethod(type); - return AccessTools.MethodDelegate>(method); - } - } -} \ No newline at end of file