1+ using FrooxEngine ;
2+ using FrooxEngine . UIX ;
3+ using HarmonyLib ;
4+ using MonkeyLoader ;
5+ using MonkeyLoader . Resonite ;
6+ using MonkeyLoader . Resonite . UI . Inspectors ;
7+ using System ;
8+ using System . Collections . Generic ;
9+ using System . Linq ;
10+ using System . Reflection ;
11+ using System . Text ;
12+ using System . Threading . Tasks ;
13+
14+ namespace DynamicVariablePowerTools
15+ {
16+ internal sealed class SetupVariableMemberActions
17+ : ResoniteAsyncEventHandlerMonkey < SetupVariableMemberActions , InspectorMemberActionsMenuItemsGenerationEvent >
18+ {
19+ private static readonly MethodInfo _createFieldItemsMethod = AccessTools . DeclaredMethod ( typeof ( SetupVariableMemberActions ) , nameof ( CreateFieldItems ) ) ;
20+ private static readonly MethodInfo _createSyncRefItemsMethod = AccessTools . DeclaredMethod ( typeof ( SetupVariableMemberActions ) , nameof ( CreateSyncRefItems ) ) ;
21+
22+ private static readonly Dictionary < Type , Action < InspectorMemberActionsMenuItemsGenerationEvent > > _itemCreatorsByType = new ( )
23+ {
24+ { typeof ( Type ) , AccessTools . MethodDelegate < Action < InspectorMemberActionsMenuItemsGenerationEvent > > ( AccessTools . DeclaredMethod ( typeof ( SetupVariableMemberActions ) , nameof ( CreateTypeFieldItems ) ) ) }
25+ } ;
26+
27+ public override bool CanBeDisabled => true ;
28+ public override int Priority => HarmonyLib . Priority . Normal ;
29+
30+ protected override bool AppliesTo ( InspectorMemberActionsMenuItemsGenerationEvent eventData )
31+ => base . AppliesTo ( eventData ) && eventData . Target is IField ;
32+
33+ protected override Task Handle ( InspectorMemberActionsMenuItemsGenerationEvent eventData )
34+ {
35+ Action < InspectorMemberActionsMenuItemsGenerationEvent > ? createItems = null ;
36+
37+ // Check ISyncRef first because those are IField<RefID>
38+ if ( eventData . Target is ISyncRef syncRef )
39+ {
40+ if ( ! _itemCreatorsByType . TryGetValue ( syncRef . TargetType , out createItems ) )
41+ {
42+ createItems = MakeMethod ( _createSyncRefItemsMethod , syncRef . TargetType ) ;
43+ _itemCreatorsByType . Add ( syncRef . TargetType , createItems ) ;
44+ }
45+ }
46+ else if ( eventData . Target is IField field )
47+ {
48+ if ( ! _itemCreatorsByType . TryGetValue ( field . ValueType , out createItems ) )
49+ {
50+ createItems = MakeMethod ( _createFieldItemsMethod , field . ValueType ) ;
51+ _itemCreatorsByType . Add ( field . ValueType , createItems ) ;
52+ }
53+ }
54+ else
55+ {
56+ Logger . Warn ( ( ) => $ "Tried to create inspector member action items for unsupported target: { eventData . Target . GetType ( ) . CompactDescription ( ) } ") ;
57+ return Task . CompletedTask ;
58+ }
59+
60+ createItems ( eventData ) ;
61+
62+ return Task . CompletedTask ;
63+ }
64+
65+ private static void CreateFieldItems < T > ( InspectorMemberActionsMenuItemsGenerationEvent eventData )
66+ {
67+ var menuItem = eventData . ContextMenu . AddItem ( "Set up DynamicField" , ( Uri ) null ! , RadiantUI_Constants . Sub . PURPLE ) ;
68+
69+ menuItem . Button . LocalPressed += ( button , args ) =>
70+ {
71+ // Swap to eventData.Worker when updated
72+ var slot = eventData . Target . FindNearestParent < Slot > ( ) ;
73+ var dynamicField = slot . AttachComponent < DynamicField < T > > ( ) ;
74+ dynamicField . TargetField . Target = ( IField < T > ) eventData . Target ;
75+
76+ button . World . LocalUser . CloseContextMenu ( eventData . MemberActions ) ;
77+ } ;
78+
79+ menuItem = eventData . ContextMenu . AddItem ( "Drive from Dynamic Variable" , ( Uri ) null ! , RadiantUI_Constants . Sub . PURPLE ) ;
80+
81+ menuItem . Button . LocalPressed += ( button , args ) =>
82+ {
83+ button . World . LocalUser . CloseContextMenu ( eventData . MemberActions ) ;
84+
85+ button . Slot . StartTask ( async ( ) =>
86+ {
87+ await button . World . LocalUser . OpenContextMenu ( eventData . MemberActions , args . source . Slot ) ;
88+
89+ // Need to check dynamic variable spaces hiding eachother
90+ // Also use full space/varName for drive
91+ var slot = eventData . Target . FindNearestParent < Slot > ( ) ;
92+ var options = slot . GetComponentsInParents < DynamicVariableSpace > ( )
93+ . SelectMany ( space => space . _dynamicValues . Keys . Where ( variable => typeof ( T ) . IsAssignableFrom ( variable . type ) ) )
94+ . ToArray ( ) ;
95+
96+ var menuItem2 = eventData . ContextMenu . AddItem ( "Blank" , ( Uri ) null ! , RadiantUI_Constants . Sub . PURPLE ) ;
97+ menuItem2 . Button . LocalPressed += ( button2 , args2 ) =>
98+ {
99+ var driver = slot . AttachComponent < DynamicValueVariableDriver < T > > ( ) ;
100+ driver . Target . Target = ( IField < T > ) eventData . Target ;
101+ button . World . LocalUser . CloseContextMenu ( eventData . MemberActions ) ;
102+ } ;
103+
104+ foreach ( var option in options )
105+ {
106+ var menuItem3 = eventData . ContextMenu . AddItem ( option . name , ( Uri ) null ! , RadiantUI_Constants . Sub . PURPLE ) ;
107+ menuItem3 . Button . LocalPressed += ( button2 , args2 ) =>
108+ {
109+ ( ( IField < T > ) eventData . Target ) . DriveFromVariable ( option . name ) ;
110+ button . World . LocalUser . CloseContextMenu ( eventData . MemberActions ) ;
111+ } ;
112+ }
113+ } ) ;
114+ } ;
115+ }
116+
117+ private static void CreateSyncRefItems < T > ( InspectorMemberActionsMenuItemsGenerationEvent eventData )
118+ where T : class , IWorldElement
119+ {
120+ if ( eventData . Target is not SyncRef < T > syncRefTarget )
121+ return ;
122+
123+ var menuItem = eventData . ContextMenu . AddItem ( "Set up DynamicReference" , ( Uri ) null ! , RadiantUI_Constants . Sub . PURPLE ) ;
124+
125+ menuItem . Button . LocalPressed += ( sender , args ) =>
126+ {
127+ // Swap to eventData.Worker when updated
128+ var slot = eventData . Target . FindNearestParent < Slot > ( ) ;
129+ var dynamicReference = slot . AttachComponent < DynamicReference < T > > ( ) ;
130+ dynamicReference . TargetReference . Target = syncRefTarget ;
131+ } ;
132+ }
133+
134+ private static void CreateTypeFieldItems ( InspectorMemberActionsMenuItemsGenerationEvent eventData )
135+ {
136+ if ( eventData . Target is not SyncType syncTypeTarget )
137+ return ;
138+
139+ var menuItem = eventData . ContextMenu . AddItem ( "Set up DynamicTypeField" , ( Uri ) null ! , RadiantUI_Constants . Sub . PURPLE ) ;
140+
141+ menuItem . Button . LocalPressed += ( sender , args ) =>
142+ {
143+ // Swap to eventData.Worker when updated
144+ var slot = eventData . Target . FindNearestParent < Slot > ( ) ;
145+ var dynamicReference = slot . AttachComponent < DynamicTypeField > ( ) ;
146+ dynamicReference . TargetField . Target = syncTypeTarget ;
147+ } ;
148+ }
149+
150+ private static Action < InspectorMemberActionsMenuItemsGenerationEvent > MakeMethod ( MethodInfo method , Type type )
151+ {
152+ method = method . MakeGenericMethod ( type ) ;
153+ return AccessTools . MethodDelegate < Action < InspectorMemberActionsMenuItemsGenerationEvent > > ( method ) ;
154+ }
155+ }
156+ }
0 commit comments