Skip to content
This repository was archived by the owner on Jan 24, 2023. It is now read-only.

Commit 16280f0

Browse files
authored
Merge pull request #20 from ddakebono/radio-toggle-menu
Added implementation of radio toggle menu
2 parents 823f20e + a4fd337 commit 16280f0

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed

ReMod.Core.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<VRCPath Condition="Exists('C:/Program Files (x86)/Steam/steamapps/common/VRChat')">C:/Program Files (x86)/Steam/steamapps/common/VRChat</VRCPath>
44
<VRCPath Condition="Exists('$(HOME)/.steam/steam/steamapps/common/VRChat')">$(HOME)/.steam/steam/steamapps/common/VRChat</VRCPath>
55
<VRCPath Condition="Exists('S:\Games\steamapps\common\VRChat')">S:\Games\steamapps\common\VRChat</VRCPath>
6+
<VRCPath Condition="Exists('G:\SteamLibrary\steamapps\common\VRChat')">G:\SteamLibrary\steamapps\common\VRChat</VRCPath>
67
</PropertyGroup>
78

89
<PropertyGroup Condition="'$(MlPath)'==''">
@@ -109,6 +110,7 @@
109110
<Compile Include="EnumExtensions.cs" />
110111
<Compile Include="Managers\ConfigManager.cs" />
111112
<Compile Include="UI\QuickMenu\IButtonPage.cs" />
113+
<Compile Include="UI\QuickMenu\ReRadioTogglePage.cs" />
112114
<Compile Include="UI\Wings\ReMirroredWingButton.cs" />
113115
<Compile Include="UI\Wings\ReMirroredWingMenu.cs" />
114116
<Compile Include="UI\Wings\ReMirroredWingToggle.cs" />

UI/QuickMenu/ReRadioTogglePage.cs

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using ReMod.Core.Unity;
4+
using ReMod.Core.VRChat;
5+
using TMPro;
6+
using UnityEngine;
7+
using UnityEngine.UI;
8+
using VRC.UI.Elements;
9+
using VRC.UI.Elements.Controls;
10+
using ListBinding = MonoBehaviourPublicTrGaBoObAcObDi2GaBoUnique; //DataModel.dll
11+
using Object = Il2CppSystem.Object;
12+
13+
namespace ReMod.Core.UI.QuickMenu
14+
{
15+
public class ReRadioTogglePage : UiElement
16+
{
17+
private static GameObject _menuPrefab;
18+
19+
private static GameObject MenuPrefab
20+
{
21+
get
22+
{
23+
if (_menuPrefab == null)
24+
{
25+
_menuPrefab = QuickMenuEx.Instance.field_Public_Transform_0.Find("Window/QMParent/Menu_ChangeAudioInputDevice").gameObject;
26+
}
27+
return _menuPrefab;
28+
}
29+
}
30+
31+
private static int SiblingIndex => QuickMenuEx.Instance.field_Public_Transform_0.Find("Window/QMParent/Modal_AddMessage").GetSiblingIndex();
32+
33+
public event Action OnOpen;
34+
public event Action OnClose;
35+
public event Action<Object> OnSelect;
36+
37+
public string TitleText
38+
{
39+
set => _titleText.text = value;
40+
}
41+
42+
private TextMeshProUGUI _titleText;
43+
private ListBinding ListBinding;
44+
private GameObject RadioButtonPrefab;
45+
private RadioButtonSelectorGroup RadioButtonSelectorGroup;
46+
private Dictionary<string, Tuple<string, Object, Action>> _radioElementSource = new();
47+
private bool _isUpdated;
48+
49+
private readonly bool _isRoot;
50+
51+
private readonly Transform _container;
52+
53+
public UIPage UiPage { get; }
54+
55+
public ReRadioTogglePage(string name) : base(MenuPrefab, QuickMenuEx.MenuParent, $"Menu_{name}", false)
56+
{
57+
var headerTransform = RectTransform.GetChild(0);
58+
59+
_titleText = headerTransform.GetComponentInChildren<TextMeshProUGUI>();
60+
_titleText.text = name;
61+
_titleText.richText = true;
62+
63+
_container = RectTransform.GetComponentInChildren<ScrollRect>().content;
64+
65+
var inputMenu = RectTransform.GetComponent<AudioInputDeviceMenu>();
66+
RadioButtonPrefab = inputMenu.field_Public_GameObject_0;
67+
ListBinding = inputMenu.field_Public_MonoBehaviourPublicTrGaBoObAcObDi2GaBoUnique_0;
68+
RadioButtonSelectorGroup = ListBinding.gameObject.GetComponent<RadioButtonSelectorGroup>();
69+
70+
ListBinding.field_Private_Dictionary_2_Object_GameObject_0 = new Il2CppSystem.Collections.Generic.Dictionary<Object, GameObject>();
71+
72+
//Get rid of the AudioInputDeviceMenu component
73+
UnityEngine.Object.DestroyImmediate(inputMenu);
74+
75+
// Set up UIPage
76+
UiPage = GameObject.GetComponent<UIPage>();
77+
UiPage.field_Public_String_0 = $"QuickMenuReMod{GetCleanName(name)}";
78+
UiPage.field_Private_Boolean_1 = true;
79+
UiPage.field_Protected_MenuStateController_0 = QuickMenuEx.MenuStateCtrl;
80+
UiPage.field_Private_List_1_UIPage_0 = new Il2CppSystem.Collections.Generic.List<UIPage>();
81+
UiPage.field_Private_List_1_UIPage_0.Add(UiPage);
82+
83+
QuickMenuEx.MenuStateCtrl.field_Private_Dictionary_2_String_UIPage_0.Add(UiPage.field_Public_String_0, UiPage);
84+
85+
EnableDisableListener.RegisterSafe();
86+
var listener = GameObject.AddComponent<EnableDisableListener>();
87+
listener.OnEnableEvent += () => OnOpen?.Invoke();
88+
listener.OnDisableEvent += () => OnClose?.Invoke();
89+
}
90+
91+
public void Open()
92+
{
93+
QuickMenuEx.MenuStateCtrl.PushPage(UiPage.field_Public_String_0);
94+
95+
if (!_isUpdated)
96+
return;
97+
98+
_isUpdated = false;
99+
100+
foreach(var element in ListBinding.field_Private_Dictionary_2_Object_GameObject_0)
101+
UnityEngine.Object.DestroyImmediate(element.value);
102+
ListBinding.field_Private_Dictionary_2_Object_GameObject_0.Clear();
103+
104+
foreach (var newElement in _radioElementSource)
105+
{
106+
var radioButton = UnityEngine.Object.Instantiate(RadioButtonPrefab, ListBinding.gameObject.transform);
107+
radioButton.active = true;
108+
var radioButtonSelector = radioButton.GetComponent<RadioButtonSelector>();
109+
radioButtonSelector.field_Public_TextMeshProUGUI_0 = radioButtonSelector.GetComponentInChildren<TextMeshProUGUI>();
110+
radioButtonSelector.field_Private_Button_0 = radioButtonSelector.GetComponent<Button>();
111+
UnityEngine.Object.DestroyImmediate(radioButtonSelector.GetComponent<AudioDeviceButton>());
112+
radioButtonSelector.field_Private_Button_0.onClick.AddListener(new Action(() => OnSelect?.Invoke(newElement.Value.Item2)));
113+
if(newElement.Value.Item3 != null)
114+
radioButtonSelector.field_Private_Button_0.onClick.AddListener(newElement.Value.Item3);
115+
radioButtonSelector.field_Public_String_0 = newElement.Value.Item1;
116+
radioButtonSelector.SetTitle(newElement.Key, newElement.Value.Item1);
117+
radioButtonSelector.prop_RadioButtonSelectorGroup_0 = RadioButtonSelectorGroup;
118+
ListBinding.field_Private_Dictionary_2_Object_GameObject_0.Add(newElement.Key, radioButton);
119+
}
120+
}
121+
122+
/// <summary>
123+
/// Adds a item to the radio element source
124+
/// </summary>
125+
/// <param name="name">Name that will appear on radio toggle</param>
126+
/// <param name="obj">Object to be send in OnSelect event</param>
127+
/// <param name="onClick">OnClick when the toggle is selected in menu</param>
128+
public void AddItem(string name, Object obj, Action onClick = null)
129+
{
130+
_radioElementSource.Add($"{_radioElementSource.Count}_{name}", new Tuple<string, Object, Action>(name, obj, onClick));
131+
_isUpdated = true;
132+
}
133+
134+
public void ClearItems()
135+
{
136+
_radioElementSource.Clear();
137+
_isUpdated = true;
138+
}
139+
}
140+
}

VRChat/VrcUiExtensions.cs

+15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Reflection;
5+
using UnhollowerRuntimeLib.XrefScans;
46
using UnityEngine;
57
using VRC.Core;
68
using VRC.UI.Core;
@@ -120,6 +122,19 @@ public static Transform GetScreen(this VRCUiManager uiManager, QuickMenu.MainMen
120122
return uiManager.MenuContent().transform.Find($"Screens/{BigMenuIndexToNameTable[menuIndex]}");
121123
}
122124

125+
private static MethodInfo[] _radioSetTitleMethods;
126+
127+
public static void SetTitle(this RadioButtonSelector selector, string key, string displayName)
128+
{
129+
_radioSetTitleMethods ??= typeof(RadioButtonSelector).GetMethods(BindingFlags.Public | BindingFlags.Instance)
130+
.Where(x => x.Name.Contains("Method_Public_Void_String_String_PDM") && XrefScanner.UsedBy(x).Any()).ToArray();
131+
132+
foreach (var method in _radioSetTitleMethods)
133+
{
134+
method.Invoke(selector, new object[] {key, displayName});
135+
}
136+
}
137+
123138
private static readonly Dictionary<QuickMenu.MainMenuScreenIndex, string> BigMenuIndexToPathTable = new Dictionary<QuickMenu.MainMenuScreenIndex, string>()
124139
{
125140
{ QuickMenu.MainMenuScreenIndex.Unknown, "" },

0 commit comments

Comments
 (0)