Skip to content
This repository was archived by the owner on Jan 24, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ReMod.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<VRCPath Condition="Exists('C:/Program Files (x86)/Steam/steamapps/common/VRChat')">C:/Program Files (x86)/Steam/steamapps/common/VRChat</VRCPath>
<VRCPath Condition="Exists('$(HOME)/.steam/steam/steamapps/common/VRChat')">$(HOME)/.steam/steam/steamapps/common/VRChat</VRCPath>
<VRCPath Condition="Exists('S:\Games\steamapps\common\VRChat')">S:\Games\steamapps\common\VRChat</VRCPath>
<VRCPath Condition="Exists('G:\SteamLibrary\steamapps\common\VRChat')">G:\SteamLibrary\steamapps\common\VRChat</VRCPath>
</PropertyGroup>

<PropertyGroup Condition="'$(MlPath)'==''">
Expand Down Expand Up @@ -109,6 +110,7 @@
<Compile Include="EnumExtensions.cs" />
<Compile Include="Managers\ConfigManager.cs" />
<Compile Include="UI\QuickMenu\IButtonPage.cs" />
<Compile Include="UI\QuickMenu\ReRadioTogglePage.cs" />
<Compile Include="UI\Wings\ReMirroredWingButton.cs" />
<Compile Include="UI\Wings\ReMirroredWingMenu.cs" />
<Compile Include="UI\Wings\ReMirroredWingToggle.cs" />
Expand Down
140 changes: 140 additions & 0 deletions UI/QuickMenu/ReRadioTogglePage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using ReMod.Core.Unity;
using ReMod.Core.VRChat;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using VRC.UI.Elements;
using VRC.UI.Elements.Controls;
using ListBinding = MonoBehaviourPublicTrGaBoObAcObDi2GaBoUnique; //DataModel.dll
using Object = Il2CppSystem.Object;

namespace ReMod.Core.UI.QuickMenu
{
public class ReRadioTogglePage : UiElement
{
private static GameObject _menuPrefab;

private static GameObject MenuPrefab
{
get
{
if (_menuPrefab == null)
{
_menuPrefab = QuickMenuEx.Instance.field_Public_Transform_0.Find("Window/QMParent/Menu_ChangeAudioInputDevice").gameObject;
}
return _menuPrefab;
}
}

private static int SiblingIndex => QuickMenuEx.Instance.field_Public_Transform_0.Find("Window/QMParent/Modal_AddMessage").GetSiblingIndex();

public event Action OnOpen;
public event Action OnClose;
public event Action<Object> OnSelect;

public string TitleText
{
set => _titleText.text = value;
}

private TextMeshProUGUI _titleText;
private ListBinding ListBinding;
private GameObject RadioButtonPrefab;
private RadioButtonSelectorGroup RadioButtonSelectorGroup;
private Dictionary<string, Tuple<string, Object, Action>> _radioElementSource = new();
private bool _isUpdated;

private readonly bool _isRoot;

private readonly Transform _container;

public UIPage UiPage { get; }

public ReRadioTogglePage(string name) : base(MenuPrefab, QuickMenuEx.MenuParent, $"Menu_{name}", false)
{
var headerTransform = RectTransform.GetChild(0);

_titleText = headerTransform.GetComponentInChildren<TextMeshProUGUI>();
_titleText.text = name;
_titleText.richText = true;

_container = RectTransform.GetComponentInChildren<ScrollRect>().content;

var inputMenu = RectTransform.GetComponent<AudioInputDeviceMenu>();
RadioButtonPrefab = inputMenu.field_Public_GameObject_0;
ListBinding = inputMenu.field_Public_MonoBehaviourPublicTrGaBoObAcObDi2GaBoUnique_0;
RadioButtonSelectorGroup = ListBinding.gameObject.GetComponent<RadioButtonSelectorGroup>();

ListBinding.field_Private_Dictionary_2_Object_GameObject_0 = new Il2CppSystem.Collections.Generic.Dictionary<Object, GameObject>();

//Get rid of the AudioInputDeviceMenu component
UnityEngine.Object.DestroyImmediate(inputMenu);

// Set up UIPage
UiPage = GameObject.GetComponent<UIPage>();
UiPage.field_Public_String_0 = $"QuickMenuReMod{GetCleanName(name)}";
UiPage.field_Private_Boolean_1 = true;
UiPage.field_Protected_MenuStateController_0 = QuickMenuEx.MenuStateCtrl;
UiPage.field_Private_List_1_UIPage_0 = new Il2CppSystem.Collections.Generic.List<UIPage>();
UiPage.field_Private_List_1_UIPage_0.Add(UiPage);

QuickMenuEx.MenuStateCtrl.field_Private_Dictionary_2_String_UIPage_0.Add(UiPage.field_Public_String_0, UiPage);

EnableDisableListener.RegisterSafe();
var listener = GameObject.AddComponent<EnableDisableListener>();
listener.OnEnableEvent += () => OnOpen?.Invoke();
listener.OnDisableEvent += () => OnClose?.Invoke();
}

public void Open()
{
QuickMenuEx.MenuStateCtrl.PushPage(UiPage.field_Public_String_0);

if (!_isUpdated)
return;

_isUpdated = false;

foreach(var element in ListBinding.field_Private_Dictionary_2_Object_GameObject_0)
UnityEngine.Object.DestroyImmediate(element.value);
ListBinding.field_Private_Dictionary_2_Object_GameObject_0.Clear();

foreach (var newElement in _radioElementSource)
{
var radioButton = UnityEngine.Object.Instantiate(RadioButtonPrefab, ListBinding.gameObject.transform);
radioButton.active = true;
var radioButtonSelector = radioButton.GetComponent<RadioButtonSelector>();
radioButtonSelector.field_Public_TextMeshProUGUI_0 = radioButtonSelector.GetComponentInChildren<TextMeshProUGUI>();
radioButtonSelector.field_Private_Button_0 = radioButtonSelector.GetComponent<Button>();
UnityEngine.Object.DestroyImmediate(radioButtonSelector.GetComponent<AudioDeviceButton>());
radioButtonSelector.field_Private_Button_0.onClick.AddListener(new Action(() => OnSelect?.Invoke(newElement.Value.Item2)));
if(newElement.Value.Item3 != null)
radioButtonSelector.field_Private_Button_0.onClick.AddListener(newElement.Value.Item3);
radioButtonSelector.field_Public_String_0 = newElement.Value.Item1;
radioButtonSelector.SetTitle(newElement.Key, newElement.Value.Item1);
radioButtonSelector.prop_RadioButtonSelectorGroup_0 = RadioButtonSelectorGroup;
ListBinding.field_Private_Dictionary_2_Object_GameObject_0.Add(newElement.Key, radioButton);
}
}

/// <summary>
/// Adds a item to the radio element source
/// </summary>
/// <param name="name">Name that will appear on radio toggle</param>
/// <param name="obj">Object to be send in OnSelect event</param>
/// <param name="onClick">OnClick when the toggle is selected in menu</param>
public void AddItem(string name, Object obj, Action onClick = null)
{
_radioElementSource.Add($"{_radioElementSource.Count}_{name}", new Tuple<string, Object, Action>(name, obj, onClick));
_isUpdated = true;
}

public void ClearItems()
{
_radioElementSource.Clear();
_isUpdated = true;
}
}
}
15 changes: 15 additions & 0 deletions VRChat/VrcUiExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnhollowerRuntimeLib.XrefScans;
using UnityEngine;
using VRC.Core;
using VRC.UI.Core;
Expand Down Expand Up @@ -120,6 +122,19 @@ public static Transform GetScreen(this VRCUiManager uiManager, QuickMenu.MainMen
return uiManager.MenuContent().transform.Find($"Screens/{BigMenuIndexToNameTable[menuIndex]}");
}

private static MethodInfo[] _radioSetTitleMethods;

public static void SetTitle(this RadioButtonSelector selector, string key, string displayName)
{
_radioSetTitleMethods ??= typeof(RadioButtonSelector).GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.Name.Contains("Method_Public_Void_String_String_PDM") && XrefScanner.UsedBy(x).Any()).ToArray();

foreach (var method in _radioSetTitleMethods)
{
method.Invoke(selector, new object[] {key, displayName});
}
}

private static readonly Dictionary<QuickMenu.MainMenuScreenIndex, string> BigMenuIndexToPathTable = new Dictionary<QuickMenu.MainMenuScreenIndex, string>()
{
{ QuickMenu.MainMenuScreenIndex.Unknown, "" },
Expand Down