From 4014a91565d4e8757d95f5a9f2c25d89728351f3 Mon Sep 17 00:00:00 2001 From: Arne Kiesewetter Date: Fri, 24 Apr 2026 18:10:49 +0200 Subject: [PATCH 1/4] Test out dynamic variable member actions logic --- DynamicVariablePowerTools/Locale/en.json | 53 +++--- .../SetupVariableMemberActions.cs | 168 ++++++++++++++---- 2 files changed, 166 insertions(+), 55 deletions(-) diff --git a/DynamicVariablePowerTools/Locale/en.json b/DynamicVariablePowerTools/Locale/en.json index b8bda58..926a6eb 100644 --- a/DynamicVariablePowerTools/Locale/en.json +++ b/DynamicVariablePowerTools/Locale/en.json @@ -1,30 +1,37 @@ { - "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.", + "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.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.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.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.Config.RenameOptions.Name": "Rename Options", + "DynamicVariablePowerTools.Config.RenameOptions.Description": "Options for how dynamic variables should be renamed.", - "DynamicVariablePowerTools.DebugInfo.Name": "Debug Info" - } + "DynamicVariablePowerTools.DebugInfo.Name": "Debug Info", + + "DynamicVariablePowerTools.Source": "Set up {type}", + "DynamicVariablePowerTools.Source.FromBlank": "[Blank]", + "DynamicVariablePowerTools.Source.InSpace": "In space {space}", + "DynamicVariablePowerTools.Reference": "Set up Reference Variable", + "DynamicVariablePowerTools.Drive": "Drive from Dynamic Variable", + "DynamicVariablePowerTools.Drive.FromBlank": "[Blank]" + } } \ No newline at end of file diff --git a/DynamicVariablePowerTools/SetupVariableMemberActions.cs b/DynamicVariablePowerTools/SetupVariableMemberActions.cs index da75477..bdd8ad1 100644 --- a/DynamicVariablePowerTools/SetupVariableMemberActions.cs +++ b/DynamicVariablePowerTools/SetupVariableMemberActions.cs @@ -1,4 +1,5 @@ using FrooxEngine; +using FrooxEngine.UIX; using HarmonyLib; using MonkeyLoader; using MonkeyLoader.Resonite; @@ -7,22 +8,24 @@ namespace DynamicVariablePowerTools { - internal sealed class SetupVariableMemberActions - : ResoniteAsyncEventHandlerMonkey + internal sealed class SourceVariableMemberActions + : 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 MethodInfo _createFieldItemsMethod = AccessTools.DeclaredMethod(typeof(SourceVariableMemberActions), nameof(CreateFieldItems)); + private static readonly MethodInfo _createSyncRefItemsMethod = AccessTools.DeclaredMethod(typeof(SourceVariableMemberActions), nameof(CreateSyncRefItems)); private static readonly Dictionary> _itemCreatorsByType = new() { - { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(SetupVariableMemberActions), nameof(CreateTypeFieldItems))) } + { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(SourceVariableMemberActions), 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; + // Check for existence of Slot parent to filter out fields on UserComponents etc. + => base.AppliesTo(eventData) && eventData.Target is IField && eventData.Target.FindNearestParent() is not null; protected override Task Handle(InspectorMemberActionsMenuItemsGenerationEvent eventData) { @@ -37,6 +40,7 @@ protected override Task Handle(InspectorMemberActionsMenuItemsGenerationEvent ev _itemCreatorsByType.Add(syncRef.TargetType, createItems); } } + // This includes SyncType fields, since they're derived from SyncField and thus IField else if (eventData.Target is IField field) { if (!_itemCreatorsByType.TryGetValue(field.ValueType, out createItems)) @@ -58,50 +62,82 @@ protected override Task Handle(InspectorMemberActionsMenuItemsGenerationEvent ev private static void CreateFieldItems(InspectorMemberActionsMenuItemsGenerationEvent eventData) { - var menuItem = eventData.ContextMenu.AddItem("Set up DynamicField", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + if (eventData.Target is not IField fieldTarget) + return; + + var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicField"), OfficialAssets.Graphics.Icons.ProtoFlux.Source, RadiantUI_Constants.Sub.CYAN); 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; + eventData.CloseContextMenu(); + + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.Blank"), (Uri)null!, RadiantUI_Constants.Sub.CYAN); + + menuItem2.Button.LocalPressed += (button2, args2) => + { + fieldTarget.SyncWithVariable(""); + eventData.CloseContextMenu(); + }; + + foreach (var space in eventData.Target.FindNearestParent().GetAvailableSpaces()) + { + menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.InSpace", "space", space.SpaceName.Value ?? "null"), (Uri)null!, RadiantUI_Constants.Sub.CYAN); + + menuItem2.Button.LocalPressed += (button2, args2) => + { + var name = space.SpaceName.Value is null ? "" : $"{space.SpaceName}/"; + fieldTarget.SyncWithVariable(name); + eventData.CloseContextMenu(); + }; + } + }); + }; + + menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference"), OfficialAssets.Graphics.Icons.ProtoFlux.Reference, RadiantUI_Constants.Neutrals.LIGHT); - button.World.LocalUser.CloseContextMenu(eventData.Summoner); + menuItem.Button.LocalPressed += (button, args) => + { + var dynamicReference = fieldTarget.FindNearestParent().AttachComponent>>(); + dynamicReference.Reference.Target = fieldTarget; + + eventData.CloseContextMenu(); }; - menuItem = eventData.ContextMenu.AddItem("Drive from Dynamic Variable", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + if (fieldTarget.IsLinked) + return; + + menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive"), OfficialAssets.Graphics.Icons.ProtoFlux.Drive, RadiantUI_Constants.Sub.PURPLE); menuItem.Button.LocalPressed += (button, args) => { - button.World.LocalUser.CloseContextMenu(eventData.Summoner); + eventData.CloseContextMenu(); button.Slot.StartTask(async () => { - await button.World.LocalUser.OpenContextMenu(eventData.Summoner, args.source.Slot); + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; - // 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); + var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (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); + fieldTarget.DriveFromVariable(""); + eventData.CloseContextMenu(); }; - foreach (var option in options) + foreach (var option in slot.GetAvailableVariableIdentities()) { - var menuItem3 = eventData.ContextMenu.AddItem(option.name, (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{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); + fieldTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); + eventData.CloseContextMenu(); }; } }); @@ -114,14 +150,48 @@ private static void CreateSyncRefItems(InspectorMemberActionsMenuItemsGenerat if (eventData.Target is not SyncRef syncRefTarget) return; - var menuItem = eventData.ContextMenu.AddItem("Set up DynamicReference", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "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; + + eventData.CloseContextMenu(); + }; + + if (syncRefTarget.IsLinked) + return; + + menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + + menuItem.Button.LocalPressed += (button, args) => + { + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + var slot = eventData.Target.FindNearestParent(); + + var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem2.Button.LocalPressed += (button2, args2) => + { + syncRefTarget.DriveFromVariable(""); + eventData.CloseContextMenu(); + }; + + foreach (var option in slot.GetAvailableVariableIdentities()) + { + var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem3.Button.LocalPressed += (button2, args2) => + { + syncRefTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); + eventData.CloseContextMenu(); + }; + } + }); }; } @@ -130,14 +200,48 @@ private static void CreateTypeFieldItems(InspectorMemberActionsMenuItemsGenerati if (eventData.Target is not SyncType syncTypeTarget) return; - var menuItem = eventData.ContextMenu.AddItem("Set up DynamicTypeField", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "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; + + eventData.CloseContextMenu(); + }; + + if (syncTypeTarget.IsLinked) + return; + + menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + + menuItem.Button.LocalPressed += (button, args) => + { + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + var slot = eventData.Target.FindNearestParent(); + + var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem2.Button.LocalPressed += (button2, args2) => + { + syncTypeTarget.DriveFromVariable(""); + eventData.CloseContextMenu(); + }; + + foreach (var option in slot.GetAvailableVariableIdentities()) + { + var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem3.Button.LocalPressed += (button2, args2) => + { + syncTypeTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); + eventData.CloseContextMenu(); + }; + } + }); }; } From f94124e7db46656139d8ada19a23cffad392f80a Mon Sep 17 00:00:00 2001 From: Arne Kiesewetter Date: Fri, 24 Apr 2026 18:24:50 +0200 Subject: [PATCH 2/4] Move dynamic variable member actions to sub-namespace --- .../DynamicVariableMemberActions.cs} | 13 ++++++------- .../DynamicVariablePowerTools.csproj | 5 +++-- 2 files changed, 9 insertions(+), 9 deletions(-) rename DynamicVariablePowerTools/{SetupVariableMemberActions.cs => ContextMenu/DynamicVariableMemberActions.cs} (95%) diff --git a/DynamicVariablePowerTools/SetupVariableMemberActions.cs b/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs similarity index 95% rename from DynamicVariablePowerTools/SetupVariableMemberActions.cs rename to DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs index bdd8ad1..935458d 100644 --- a/DynamicVariablePowerTools/SetupVariableMemberActions.cs +++ b/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs @@ -1,22 +1,21 @@ using FrooxEngine; -using FrooxEngine.UIX; using HarmonyLib; using MonkeyLoader; using MonkeyLoader.Resonite; using MonkeyLoader.Resonite.UI.Inspectors; using System.Reflection; -namespace DynamicVariablePowerTools +namespace DynamicVariablePowerTools.ContextMenu { - internal sealed class SourceVariableMemberActions - : ResoniteAsyncEventHandlerMonkey + internal sealed class DynamicVariableMemberActions + : ResoniteAsyncEventHandlerMonkey { - private static readonly MethodInfo _createFieldItemsMethod = AccessTools.DeclaredMethod(typeof(SourceVariableMemberActions), nameof(CreateFieldItems)); - private static readonly MethodInfo _createSyncRefItemsMethod = AccessTools.DeclaredMethod(typeof(SourceVariableMemberActions), nameof(CreateSyncRefItems)); + private static readonly MethodInfo _createFieldItemsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateFieldItems)); + private static readonly MethodInfo _createSyncRefItemsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateSyncRefItems)); private static readonly Dictionary> _itemCreatorsByType = new() { - { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(SourceVariableMemberActions), nameof(CreateTypeFieldItems))) } + { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateTypeFieldItems))) } }; public override bool CanBeDisabled => true; diff --git a/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj b/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj index 9d7d72d..0d38c5b 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 From b641be1e8d6397e2402675fb6109e155f79895e6 Mon Sep 17 00:00:00 2001 From: Arne Kiesewetter Date: Fri, 24 Apr 2026 23:47:50 +0200 Subject: [PATCH 3/4] Split up DVMA handlers and flesh out Values one --- .../ContextMenu/DVMA.References.cs | 61 +++++ .../ContextMenu/DVMA.Types.cs | 60 +++++ .../ContextMenu/DVMA.Values.cs | 121 +++++++++ .../DynamicVariableMemberActions.cs | 249 ++++-------------- .../DynamicVariablePowerTools.csproj | 6 +- DynamicVariablePowerTools/Locale/en.json | 11 +- 6 files changed, 300 insertions(+), 208 deletions(-) create mode 100644 DynamicVariablePowerTools/ContextMenu/DVMA.References.cs create mode 100644 DynamicVariablePowerTools/ContextMenu/DVMA.Types.cs create mode 100644 DynamicVariablePowerTools/ContextMenu/DVMA.Values.cs diff --git a/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs b/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs new file mode 100644 index 0000000..71e8f3e --- /dev/null +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs @@ -0,0 +1,61 @@ +using FrooxEngine; +using MonkeyLoader.Resonite; + +using GenerationEvent = MonkeyLoader.Resonite.UI.Inspectors.InspectorMemberActionsMenuItemsGenerationEvent; + +namespace DynamicVariablePowerTools.ContextMenu +{ + internal sealed partial class DynamicVariableMemberActions + { + private static void CreateSyncRefItems(GenerationEvent eventData) + where T : class, IWorldElement + { + if (eventData.Target is not SyncRef syncRefTarget) + return; + + var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicReference"), SourceIcon, SourceColor); + + menuItem.Button.LocalPressed += (sender, args) => + { + var slot = eventData.Target.FindNearestParent(); + var dynamicReference = slot.AttachComponent>(); + dynamicReference.TargetReference.Target = syncRefTarget; + + eventData.CloseContextMenu(); + }; + + if (syncRefTarget.IsLinked) + return; + + menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + + menuItem.Button.LocalPressed += (button, args) => + { + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + var slot = eventData.Target.FindNearestParent(); + + var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem2.Button.LocalPressed += (button2, args2) => + { + syncRefTarget.DriveFromVariable(""); + eventData.CloseContextMenu(); + }; + + foreach (var option in slot.GetAvailableVariableIdentities()) + { + var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem3.Button.LocalPressed += (button2, args2) => + { + syncRefTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); + eventData.CloseContextMenu(); + }; + } + }); + }; + } + } +} \ 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..0320068 --- /dev/null +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.Types.cs @@ -0,0 +1,60 @@ +using FrooxEngine; +using MonkeyLoader.Resonite; + +using GenerationEvent = MonkeyLoader.Resonite.UI.Inspectors.InspectorMemberActionsMenuItemsGenerationEvent; + +namespace DynamicVariablePowerTools.ContextMenu +{ + internal sealed partial class DynamicVariableMemberActions + { + private static void CreateTypeFieldItems(GenerationEvent eventData) + { + if (eventData.Target is not SyncType syncTypeTarget) + return; + + var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicTypeField"), SourceIcon, SourceColor); + + menuItem.Button.LocalPressed += (sender, args) => + { + var slot = eventData.Target.FindNearestParent(); + var dynamicReference = slot.AttachComponent(); + dynamicReference.TargetField.Target = syncTypeTarget; + + eventData.CloseContextMenu(); + }; + + if (syncTypeTarget.IsLinked) + return; + + menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + + menuItem.Button.LocalPressed += (button, args) => + { + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; + + var slot = eventData.Target.FindNearestParent(); + + var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem2.Button.LocalPressed += (button2, args2) => + { + syncTypeTarget.DriveFromVariable(""); + eventData.CloseContextMenu(); + }; + + foreach (var option in slot.GetAvailableVariableIdentities()) + { + var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + menuItem3.Button.LocalPressed += (button2, args2) => + { + syncTypeTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); + eventData.CloseContextMenu(); + }; + } + }); + }; + } + } +} \ 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..0620b25 --- /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.Target.FindNearestParent() + .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.Target.FindNearestParent() + .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 index 935458d..5cbbb5c 100644 --- a/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs +++ b/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs @@ -1,51 +1,63 @@ -using FrooxEngine; +using Elements.Core; +using FrooxEngine; using HarmonyLib; using MonkeyLoader; using MonkeyLoader.Resonite; -using MonkeyLoader.Resonite.UI.Inspectors; +using MonkeyLoader.Resonite.Configuration; using System.Reflection; +using GenerationEvent = MonkeyLoader.Resonite.UI.Inspectors.InspectorMemberActionsMenuItemsGenerationEvent; + namespace DynamicVariablePowerTools.ContextMenu { - internal sealed class DynamicVariableMemberActions - : ResoniteAsyncEventHandlerMonkey + internal sealed partial class DynamicVariableMemberActions + : ResoniteAsyncEventHandlerMonkey { - private static readonly MethodInfo _createFieldItemsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateFieldItems)); - private static readonly MethodInfo _createSyncRefItemsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateSyncRefItems)); - - private static readonly Dictionary> _itemCreatorsByType = new() + private static readonly Dictionary> _actionOfferersByType = new() { - { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateTypeFieldItems))) } + { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateTypeFieldItems))) } }; + private static readonly MethodInfo _offerFieldActionsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(OfferFieldActions)); + private static readonly MethodInfo _offerSyncRefActionsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateSyncRefItems)); + public override bool CanBeDisabled => true; public override int Priority => HarmonyLib.Priority.Normal; - protected override bool AppliesTo(InspectorMemberActionsMenuItemsGenerationEvent eventData) - // Check for existence of Slot parent to filter out fields on UserComponents etc. - => base.AppliesTo(eventData) && eventData.Target is IField && eventData.Target.FindNearestParent() is not null; + 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(InspectorMemberActionsMenuItemsGenerationEvent eventData) + protected override Task Handle(GenerationEvent eventData) { - Action? createItems = null; + Action? offerActions; // Check ISyncRef first because those are IField if (eventData.Target is ISyncRef syncRef) { - if (!_itemCreatorsByType.TryGetValue(syncRef.TargetType, out createItems)) + if (!_actionOfferersByType.TryGetValue(syncRef.TargetType, out offerActions)) { - createItems = MakeMethod(_createSyncRefItemsMethod, syncRef.TargetType); - _itemCreatorsByType.Add(syncRef.TargetType, createItems); + 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 (!_itemCreatorsByType.TryGetValue(field.ValueType, out createItems)) + if (!_actionOfferersByType.TryGetValue(field.ValueType, out offerActions)) { - createItems = MakeMethod(_createFieldItemsMethod, field.ValueType); - _itemCreatorsByType.Add(field.ValueType, createItems); + offerActions = MakeMethod(_offerFieldActionsMethod, field.ValueType); + _actionOfferersByType.Add(field.ValueType, offerActions); } } else @@ -54,200 +66,31 @@ protected override Task Handle(InspectorMemberActionsMenuItemsGenerationEvent ev return Task.CompletedTask; } - createItems(eventData); + offerActions(eventData); return Task.CompletedTask; } - private static void CreateFieldItems(InspectorMemberActionsMenuItemsGenerationEvent eventData) + private static IEnumerable GetAvailableVariableOptions(Slot slot) { - if (eventData.Target is not IField fieldTarget) - return; - - var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicField"), OfficialAssets.Graphics.Icons.ProtoFlux.Source, RadiantUI_Constants.Sub.CYAN); - - menuItem.Button.LocalPressed += (button, args) => - { - eventData.CloseContextMenu(); - - button.Slot.StartTask(async () => - { - if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) - return; - - var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.Blank"), (Uri)null!, RadiantUI_Constants.Sub.CYAN); - - menuItem2.Button.LocalPressed += (button2, args2) => - { - fieldTarget.SyncWithVariable(""); - eventData.CloseContextMenu(); - }; - - foreach (var space in eventData.Target.FindNearestParent().GetAvailableSpaces()) - { - menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.InSpace", "space", space.SpaceName.Value ?? "null"), (Uri)null!, RadiantUI_Constants.Sub.CYAN); - - menuItem2.Button.LocalPressed += (button2, args2) => - { - var name = space.SpaceName.Value is null ? "" : $"{space.SpaceName}/"; - fieldTarget.SyncWithVariable(name); - eventData.CloseContextMenu(); - }; - } - }); - }; - - menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference"), OfficialAssets.Graphics.Icons.ProtoFlux.Reference, RadiantUI_Constants.Neutrals.LIGHT); - - menuItem.Button.LocalPressed += (button, args) => + foreach (var identity in slot.GetAvailableVariableIdentities()) { - var dynamicReference = fieldTarget.FindNearestParent().AttachComponent>>(); - dynamicReference.Reference.Target = fieldTarget; - - eventData.CloseContextMenu(); - }; - - if (fieldTarget.IsLinked) - return; - - menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive"), OfficialAssets.Graphics.Icons.ProtoFlux.Drive, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (button, args) => - { - eventData.CloseContextMenu(); - - button.Slot.StartTask(async () => - { - if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) - return; - - var slot = eventData.Target.FindNearestParent(); - - var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem2.Button.LocalPressed += (button2, args2) => - { - fieldTarget.DriveFromVariable(""); - eventData.CloseContextMenu(); - }; - - foreach (var option in slot.GetAvailableVariableIdentities()) - { - var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem3.Button.LocalPressed += (button2, args2) => - { - fieldTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); - eventData.CloseContextMenu(); - }; - } - }); - }; - } - - private static void CreateSyncRefItems(InspectorMemberActionsMenuItemsGenerationEvent eventData) - where T : class, IWorldElement - { - if (eventData.Target is not SyncRef syncRefTarget) - return; - - var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicReference"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + if (identity.Name.StartsWith(SharedConfig.Identifier)) + continue; - menuItem.Button.LocalPressed += (sender, args) => - { - var slot = eventData.Target.FindNearestParent(); - var dynamicReference = slot.AttachComponent>(); - dynamicReference.TargetReference.Target = syncRefTarget; - - eventData.CloseContextMenu(); - }; - - if (syncRefTarget.IsLinked) - return; - - menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (button, args) => - { - button.Slot.StartTask(async () => - { - if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) - return; - - var slot = eventData.Target.FindNearestParent(); - - var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem2.Button.LocalPressed += (button2, args2) => - { - syncRefTarget.DriveFromVariable(""); - eventData.CloseContextMenu(); - }; - - foreach (var option in slot.GetAvailableVariableIdentities()) - { - var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem3.Button.LocalPressed += (button2, args2) => - { - syncRefTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); - eventData.CloseContextMenu(); - }; - } - }); - }; - } - - private static void CreateTypeFieldItems(InspectorMemberActionsMenuItemsGenerationEvent eventData) - { - if (eventData.Target is not SyncType syncTypeTarget) - return; - - var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicTypeField"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (sender, args) => - { - var slot = eventData.Target.FindNearestParent(); - var dynamicReference = slot.AttachComponent(); - dynamicReference.TargetField.Target = syncTypeTarget; - - eventData.CloseContextMenu(); - }; - - if (syncTypeTarget.IsLinked) - return; - - menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - - menuItem.Button.LocalPressed += (button, args) => - { - button.Slot.StartTask(async () => - { - if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) - return; - - var slot = eventData.Target.FindNearestParent(); - - var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem2.Button.LocalPressed += (button2, args2) => - { - syncTypeTarget.DriveFromVariable(""); - eventData.CloseContextMenu(); - }; - - foreach (var option in slot.GetAvailableVariableIdentities()) - { - var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem3.Button.LocalPressed += (button2, args2) => - { - syncTypeTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); - eventData.CloseContextMenu(); - }; - } - }); - }; + yield return !string.IsNullOrWhiteSpace(identity.Space.CurrentName) + ? $"{identity.Space.CurrentName}/{identity.Name}" + : identity.Name; + } } - private static Action MakeMethod(MethodInfo method, Type type) + private static Action MakeMethod(MethodInfo method, Type type) { method = method.MakeGenericMethod(type); - return AccessTools.MethodDelegate>(method); + 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 0d38c5b..e7c48c5 100644 --- a/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj +++ b/DynamicVariablePowerTools/DynamicVariablePowerTools.csproj @@ -14,8 +14,10 @@ - - + + + + diff --git a/DynamicVariablePowerTools/Locale/en.json b/DynamicVariablePowerTools/Locale/en.json index 926a6eb..ded7a9e 100644 --- a/DynamicVariablePowerTools/Locale/en.json +++ b/DynamicVariablePowerTools/Locale/en.json @@ -27,11 +27,16 @@ "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.FromBlank": "[Blank]", + "DynamicVariablePowerTools.Source.Blank": "[Blank]", "DynamicVariablePowerTools.Source.InSpace": "In space {space}", + "DynamicVariablePowerTools.Reference": "Set up Reference Variable", - "DynamicVariablePowerTools.Drive": "Drive from Dynamic Variable", - "DynamicVariablePowerTools.Drive.FromBlank": "[Blank]" + "DynamicVariablePowerTools.Reference.Blank": "[Blank]", + "DynamicVariablePowerTools.Reference.InSpace": "In space {space}" } } \ No newline at end of file From 64e621a557c65d180eda8c9655d08bab3be99558 Mon Sep 17 00:00:00 2001 From: Arne Kiesewetter Date: Sat, 25 Apr 2026 00:29:18 +0200 Subject: [PATCH 4/4] Finish implementation of dynamic variable inspector member actions --- .../ContextMenu/DVMA.References.cs | 125 ++++++++++++++---- .../ContextMenu/DVMA.Types.cs | 119 +++++++++++++---- .../ContextMenu/DVMA.Values.cs | 4 +- .../DynamicVariableMemberActions.cs | 4 +- 4 files changed, 190 insertions(+), 62 deletions(-) diff --git a/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs b/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs index 71e8f3e..29cc61f 100644 --- a/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.References.cs @@ -7,55 +7,122 @@ namespace DynamicVariablePowerTools.ContextMenu { internal sealed partial class DynamicVariableMemberActions { - private static void CreateSyncRefItems(GenerationEvent eventData) + private static ButtonEventHandler GetDriveSyncRefFromVariable(GenerationEvent eventData, SyncRef syncRefTarget, string variable) where T : class, IWorldElement - { - if (eventData.Target is not SyncRef syncRefTarget) - return; - - var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicReference"), SourceIcon, SourceColor); - - menuItem.Button.LocalPressed += (sender, args) => + => (button, args) => { - var slot = eventData.Target.FindNearestParent(); - var dynamicReference = slot.AttachComponent>(); - dynamicReference.TargetReference.Target = syncRefTarget; - + syncRefTarget.DriveFromVariable(variable); eventData.CloseContextMenu(); }; - if (syncRefTarget.IsLinked) - return; + 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; - menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveSyncRefFromVariable(eventData, syncRefTarget, string.Empty); - menuItem.Button.LocalPressed += (button, args) => + 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; - var slot = eventData.Target.FindNearestParent(); + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.Blank"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceSyncRefForVariable(eventData, syncRefTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); - var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem2.Button.LocalPressed += (button2, args2) => + foreach (var space in spaces) { - syncRefTarget.DriveFromVariable(""); - eventData.CloseContextMenu(); - }; + 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); - foreach (var option in slot.GetAvailableVariableIdentities()) + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); + + foreach (var space in spaces) { - var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem3.Button.LocalPressed += (button2, args2) => - { - syncRefTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); - eventData.CloseContextMenu(); - }; + 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 index 0320068..85df0a9 100644 --- a/DynamicVariablePowerTools/ContextMenu/DVMA.Types.cs +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.Types.cs @@ -7,54 +7,115 @@ namespace DynamicVariablePowerTools.ContextMenu { internal sealed partial class DynamicVariableMemberActions { - private static void CreateTypeFieldItems(GenerationEvent eventData) - { - if (eventData.Target is not SyncType syncTypeTarget) - return; - - var menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source", "type", "DynamicTypeField"), SourceIcon, SourceColor); + private static ButtonEventHandler GetDriveTypeFieldFromVariable(GenerationEvent eventData, SyncType syncTypeTarget, string variable) + => (button, args) => + { + syncTypeTarget.DriveFromVariable(variable); + eventData.CloseContextMenu(); + }; - menuItem.Button.LocalPressed += (sender, args) => + private static ButtonEventHandler GetOfferTypeFieldDriveActions(GenerationEvent eventData, SyncType syncTypeTarget) + => (button, args) => { - var slot = eventData.Target.FindNearestParent(); - var dynamicReference = slot.AttachComponent(); - dynamicReference.TargetField.Target = syncTypeTarget; - eventData.CloseContextMenu(); - }; - if (syncTypeTarget.IsLinked) - return; + button.Slot.StartTask(async () => + { + if (await eventData.OpenContextMenuAsync(args.source.Slot) is null) + return; - menuItem = eventData.ContextMenu.AddItem(Mod.GetLocaleString("DriveFrom"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), DriveIcon, DriveColor) + .Button.LocalPressed += GetDriveTypeFieldFromVariable(eventData, syncTypeTarget, string.Empty); - menuItem.Button.LocalPressed += (button, args) => + 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; - var slot = eventData.Target.FindNearestParent(); + eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.Blank"), ReferenceIcon, ReferenceColor) + .Button.LocalPressed += GetReferenceTypeFieldForVariable(eventData, syncTypeTarget, string.Empty); + + var spaces = eventData.Slot! + .GetAvailableSpaces(SpaceHasName); - var menuItem2 = eventData.ContextMenu.AddItem(Mod.GetLocaleString("Drive.FromBlank"), (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem2.Button.LocalPressed += (button2, args2) => + foreach (var space in spaces) { - syncTypeTarget.DriveFromVariable(""); - eventData.CloseContextMenu(); - }; + 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(); - foreach (var option in slot.GetAvailableVariableIdentities()) + 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) { - var menuItem3 = eventData.ContextMenu.AddItem($"{option.Space.SpaceName}/{option.Name}", (Uri)null!, RadiantUI_Constants.Sub.PURPLE); - menuItem3.Button.LocalPressed += (button2, args2) => - { - syncTypeTarget.DriveFromVariable($"{option.Space.SpaceName}/{option.Name}"); - eventData.CloseContextMenu(); - }; + 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 index 0620b25..d9b5dae 100644 --- a/DynamicVariablePowerTools/ContextMenu/DVMA.Values.cs +++ b/DynamicVariablePowerTools/ContextMenu/DVMA.Values.cs @@ -48,7 +48,7 @@ private static ButtonEventHandler GetOfferFieldReferenceActions(GenerationEve eventData.ContextMenu.AddItem(Mod.GetLocaleString("Reference.Blank"), ReferenceIcon, ReferenceColor) .Button.LocalPressed += GetReferenceFieldForVariable(eventData, fieldTarget, string.Empty); - var spaces = eventData.Target.FindNearestParent() + var spaces = eventData.Slot! .GetAvailableSpaces(SpaceHasName); foreach (var space in spaces) @@ -72,7 +72,7 @@ private static ButtonEventHandler GetOfferFieldSourceActions(GenerationEvent eventData.ContextMenu.AddItem(Mod.GetLocaleString("Source.Blank"), SourceIcon, SourceColor) .Button.LocalPressed += GetSourceFieldForVariable(eventData, fieldTarget, string.Empty); - var spaces = eventData.Target.FindNearestParent() + var spaces = eventData.Slot! .GetAvailableSpaces(SpaceHasName); foreach (var space in spaces) diff --git a/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs b/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs index 5cbbb5c..637e7fe 100644 --- a/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs +++ b/DynamicVariablePowerTools/ContextMenu/DynamicVariableMemberActions.cs @@ -15,11 +15,11 @@ internal sealed partial class DynamicVariableMemberActions { private static readonly Dictionary> _actionOfferersByType = new() { - { typeof(Type), AccessTools.MethodDelegate>(AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(CreateTypeFieldItems))) } + { 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(CreateSyncRefItems)); + private static readonly MethodInfo _offerSyncRefActionsMethod = AccessTools.DeclaredMethod(typeof(DynamicVariableMemberActions), nameof(OfferSyncRefActions)); public override bool CanBeDisabled => true;