BeatSaberUI.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. using HMUI;
  2. using IPA.Utilities;
  3. using System;
  4. using System.Linq;
  5. using TMPro;
  6. using UnityEngine;
  7. using UnityEngine.Events;
  8. using UnityEngine.UI;
  9. using VRUIControls;
  10. using Image = UnityEngine.UI.Image;
  11. using Logger = SongBrowser.Logging.Logger;
  12. namespace SongBrowser.Internals
  13. {
  14. public static class BeatSaberUI
  15. {
  16. private static PhysicsRaycasterWithCache _physicsRaycaster;
  17. public static PhysicsRaycasterWithCache PhysicsRaycasterWithCache
  18. {
  19. get
  20. {
  21. if (_physicsRaycaster == null)
  22. _physicsRaycaster = Resources.FindObjectsOfTypeAll<MainMenuViewController>().First().GetComponent<VRGraphicRaycaster>().GetField<PhysicsRaycasterWithCache, VRGraphicRaycaster>("_physicsRaycaster");
  23. return _physicsRaycaster;
  24. }
  25. }
  26. /// <summary>
  27. /// Creates a ViewController of type T, and marks it to not be destroyed.
  28. /// </summary>
  29. /// <typeparam name="T">The variation of ViewController you want to create.</typeparam>
  30. /// <returns>The newly created ViewController of type T.</returns>
  31. public static T CreateViewController<T>(string name) where T : ViewController
  32. {
  33. T vc = new GameObject(typeof(T).Name, typeof(VRGraphicRaycaster), typeof(CanvasGroup), typeof(T)).GetComponent<T>();
  34. vc.GetComponent<VRGraphicRaycaster>().SetField("_physicsRaycaster", PhysicsRaycasterWithCache);
  35. vc.rectTransform.anchorMin = new Vector2(0f, 0f);
  36. vc.rectTransform.anchorMax = new Vector2(1f, 1f);
  37. vc.rectTransform.sizeDelta = new Vector2(0f, 0f);
  38. vc.rectTransform.anchoredPosition = new Vector2(0f, 0f);
  39. vc.gameObject.SetActive(false);
  40. vc.name = name;
  41. return vc;
  42. }
  43. public static T CreateCurvedViewController<T>(string name, float curveRadius) where T : ViewController
  44. {
  45. T vc = new GameObject(typeof(T).Name, typeof(VRGraphicRaycaster), typeof(CurvedCanvasSettings), typeof(CanvasGroup), typeof(T)).GetComponent<T>();
  46. vc.GetComponent<VRGraphicRaycaster>().SetField("_physicsRaycaster", PhysicsRaycasterWithCache);
  47. vc.GetComponent<CurvedCanvasSettings>().SetRadius(curveRadius);
  48. vc.rectTransform.anchorMin = new Vector2(0f, 0f);
  49. vc.rectTransform.anchorMax = new Vector2(1f, 1f);
  50. vc.rectTransform.sizeDelta = new Vector2(0f, 0f);
  51. vc.rectTransform.anchoredPosition = new Vector2(0f, 0f);
  52. vc.gameObject.SetActive(false);
  53. return vc;
  54. }
  55. /// <summary>
  56. /// Create an icon button, simple.
  57. /// </summary>
  58. /// <param name="parent"></param>
  59. /// <param name="buttonTemplate"></param>
  60. /// <param name="iconSprite"></param>
  61. /// <returns></returns>
  62. public static Button CreateIconButton(String name, RectTransform parent, String buttonTemplate, Vector2 anchoredPosition, Vector2 sizeDelta, UnityAction onClick, Sprite icon)
  63. {
  64. Logger.Debug("CreateIconButton({0}, {1}, {2}, {3}, {4}", name, parent, buttonTemplate, anchoredPosition, sizeDelta);
  65. Button btn = UnityEngine.Object.Instantiate(Resources.FindObjectsOfTypeAll<Button>().Last(x => (x.name == buttonTemplate)), parent, false);
  66. btn.name = name;
  67. btn.interactable = true;
  68. UnityEngine.Object.Destroy(btn.GetComponent<HoverHint>());
  69. GameObject.Destroy(btn.GetComponent<LocalizedHoverHint>());
  70. btn.gameObject.AddComponent<BeatSaberMarkupLanguage.Components.ExternalComponents>().components.Add(btn.GetComponentsInChildren<LayoutGroup>().First(x => x.name == "Content"));
  71. Transform contentTransform = btn.transform.Find("Content");
  72. GameObject.Destroy(contentTransform.Find("Text").gameObject);
  73. Image iconImage = new GameObject("Icon").AddComponent<ImageView>();
  74. iconImage.material = BeatSaberMarkupLanguage.Utilities.ImageResources.NoGlowMat;
  75. iconImage.rectTransform.SetParent(contentTransform, false);
  76. iconImage.rectTransform.sizeDelta = new Vector2(10f, 10f);
  77. iconImage.sprite = icon;
  78. iconImage.preserveAspect = true;
  79. if (iconImage != null)
  80. {
  81. BeatSaberMarkupLanguage.Components.ButtonIconImage btnIcon = btn.gameObject.AddComponent<BeatSaberMarkupLanguage.Components.ButtonIconImage>();
  82. btnIcon.image = iconImage;
  83. }
  84. GameObject.Destroy(btn.transform.Find("Content").GetComponent<LayoutElement>());
  85. btn.GetComponentsInChildren<RectTransform>().First(x => x.name == "Underline").gameObject.SetActive(false);
  86. ContentSizeFitter buttonSizeFitter = btn.gameObject.AddComponent<ContentSizeFitter>();
  87. buttonSizeFitter.verticalFit = ContentSizeFitter.FitMode.Unconstrained;
  88. buttonSizeFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
  89. (btn.transform as RectTransform).anchorMin = new Vector2(0.5f, 0.5f);
  90. (btn.transform as RectTransform).anchorMax = new Vector2(0.5f, 0.5f);
  91. (btn.transform as RectTransform).anchoredPosition = anchoredPosition;
  92. (btn.transform as RectTransform).sizeDelta = sizeDelta;
  93. btn.onClick.RemoveAllListeners();
  94. if (onClick != null)
  95. btn.onClick.AddListener(onClick);
  96. return btn;
  97. }
  98. /// <summary>
  99. /// Creates a copy of a template button and returns it.
  100. /// </summary>
  101. /// <param name="parent">The transform to parent the button to.</param>
  102. /// <param name="buttonTemplate">The name of the button to make a copy of. Example: "QuitButton", "PlayButton", etc.</param>
  103. /// <param name="anchoredPosition">The position the button should be anchored to.</param>
  104. /// <param name="sizeDelta">The size of the buttons RectTransform.</param>
  105. /// <param name="onClick">Callback for when the button is pressed.</param>
  106. /// <param name="buttonText">The text that should be shown on the button.</param>
  107. /// <param name="icon">The icon that should be shown on the button.</param>
  108. /// <returns>The newly created button.</returns>
  109. public static Button CreateUIButton(String name, RectTransform parent, string buttonTemplate, Vector2 anchoredPosition, Vector2 sizeDelta, UnityAction onClick = null, string buttonText = "BUTTON")
  110. {
  111. Logger.Debug("CreateUIButton({0}, {1}, {2}, {3}, {4}", name, parent, buttonTemplate, anchoredPosition, sizeDelta);
  112. Button btn = UnityEngine.Object.Instantiate(Resources.FindObjectsOfTypeAll<Button>().Last(x => (x.name == buttonTemplate)), parent, false);
  113. btn.gameObject.SetActive(true);
  114. btn.name = name;
  115. btn.interactable = true;
  116. Polyglot.LocalizedTextMeshProUGUI localizer = btn.GetComponentInChildren<Polyglot.LocalizedTextMeshProUGUI>();
  117. if (localizer != null)
  118. {
  119. GameObject.Destroy(localizer);
  120. }
  121. BeatSaberMarkupLanguage.Components.ExternalComponents externalComponents = btn.gameObject.AddComponent<BeatSaberMarkupLanguage.Components.ExternalComponents>();
  122. TextMeshProUGUI textMesh = btn.GetComponentInChildren<TextMeshProUGUI>();
  123. textMesh.richText = true;
  124. externalComponents.components.Add(textMesh);
  125. var contentTransform = btn.transform.Find("Content").GetComponent<LayoutElement>();
  126. if (contentTransform != null)
  127. {
  128. GameObject.Destroy(contentTransform);
  129. }
  130. ContentSizeFitter buttonSizeFitter = btn.gameObject.AddComponent<ContentSizeFitter>();
  131. buttonSizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
  132. buttonSizeFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
  133. LayoutGroup stackLayoutGroup = btn.GetComponentInChildren<LayoutGroup>();
  134. if (stackLayoutGroup != null)
  135. {
  136. externalComponents.components.Add(stackLayoutGroup);
  137. }
  138. btn.onClick.RemoveAllListeners();
  139. if (onClick != null)
  140. {
  141. btn.onClick.AddListener(onClick);
  142. }
  143. (btn.transform as RectTransform).anchorMin = new Vector2(0.5f, 0.5f);
  144. (btn.transform as RectTransform).anchorMax = new Vector2(0.5f, 0.5f);
  145. (btn.transform as RectTransform).anchoredPosition = anchoredPosition;
  146. (btn.transform as RectTransform).sizeDelta = sizeDelta;
  147. btn.SetButtonText(buttonText);
  148. return btn;
  149. }
  150. public static RectTransform CreateStatIcon(string name, RectTransform template, Transform parent, Sprite icon, String hoverHintText = null)
  151. {
  152. RectTransform statIcon = UnityEngine.Object.Instantiate(template, parent, false);
  153. statIcon.name = name;
  154. (statIcon.transform as RectTransform).Translate(0, -0.1f, 0);
  155. BeatSaberUI.SetStatButtonIcon(statIcon, icon);
  156. BeatSaberUI.DestroyHoverHint(statIcon);
  157. if (!String.IsNullOrEmpty(hoverHintText))
  158. {
  159. BeatSaberUI.SetHoverHint(statIcon, $"{name}_hoverHintText", hoverHintText);
  160. }
  161. return statIcon;
  162. }
  163. public static TextMeshProUGUI CreateText(RectTransform parent, string text, float fontSize, Vector2 anchoredPosition, Vector2 sizeDelta)
  164. {
  165. GameObject gameObj = new GameObject("CustomUIText");
  166. gameObj.SetActive(false);
  167. TextMeshProUGUI textMesh = gameObj.AddComponent<CurvedTextMeshPro>();
  168. textMesh.font = UnityEngine.Object.Instantiate(Resources.FindObjectsOfTypeAll<TMP_FontAsset>().First(t => t.name == "Teko-Medium SDF No Glow"));
  169. textMesh.rectTransform.SetParent(parent, false);
  170. textMesh.text = text;
  171. textMesh.fontSize = fontSize;
  172. textMesh.color = Color.white;
  173. textMesh.rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
  174. textMesh.rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
  175. textMesh.rectTransform.sizeDelta = sizeDelta;
  176. textMesh.rectTransform.anchoredPosition = anchoredPosition;
  177. gameObj.SetActive(true);
  178. return textMesh;
  179. }
  180. /// <summary>
  181. /// Replace existing HoverHint on stat panel icons.
  182. /// </summary>
  183. /// <param name="button"></param>
  184. /// <param name="name"></param>
  185. /// <param name="text"></param>
  186. public static void SetHoverHint(RectTransform button, string name, string text)
  187. {
  188. HoverHint hover = button.gameObject.AddComponent<HoverHint>();
  189. hover.text = text;
  190. hover.name = name;
  191. hover.SetField("_hoverHintController", Resources.FindObjectsOfTypeAll<HoverHintController>().First());
  192. }
  193. /// <summary>
  194. /// Safely destroy existing hoverhint.
  195. /// </summary>
  196. /// <param name="button"></param>
  197. public static void DestroyHoverHint(RectTransform button)
  198. {
  199. HoverHint currentHoverHint = button.GetComponentsInChildren<HMUI.HoverHint>().First();
  200. if (currentHoverHint != null)
  201. {
  202. UnityEngine.GameObject.DestroyImmediate(currentHoverHint);
  203. }
  204. }
  205. /// <summary>
  206. /// Adjust button text size.
  207. /// </summary>
  208. /// <param name="button"></param>
  209. /// <param name="fontSize"></param>
  210. static public void SetButtonTextColor(Button button, Color color)
  211. {
  212. TextMeshProUGUI txt = button.GetComponentsInChildren<TextMeshProUGUI>().FirstOrDefault(x => x.name == "Text");
  213. if (txt != null)
  214. {
  215. txt.color = color;
  216. }
  217. }
  218. /// <summary>
  219. /// Find and adjust a stat panel item text fields.
  220. /// </summary>
  221. /// <param name="rect"></param>
  222. /// <param name="text"></param>
  223. static public void SetStatButtonText(RectTransform rect, String text)
  224. {
  225. TextMeshProUGUI txt = rect.GetComponentsInChildren<TextMeshProUGUI>().FirstOrDefault(x => x.name == "ValueText");
  226. if (txt != null)
  227. {
  228. txt.text = text;
  229. }
  230. }
  231. /// <summary>
  232. /// Find and adjust a stat panel item icon.
  233. /// </summary>
  234. /// <param name="rect"></param>
  235. /// <param name="icon"></param>
  236. static public void SetStatButtonIcon(RectTransform rect, Sprite icon)
  237. {
  238. Image img = rect.GetComponentsInChildren<Image>().FirstOrDefault(x => x.name == "Icon");
  239. if (img != null)
  240. {
  241. img.sprite = icon;
  242. img.color = Color.white;
  243. }
  244. }
  245. }
  246. }