You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

439 lines
16 KiB

  1. using BSIPA_ModList.UI.ViewControllers;
  2. using CustomUI.BeatSaber;
  3. using CustomUI.MenuButton;
  4. using IPA.Loader;
  5. using IPA.Updating.BeatMods;
  6. using IPA.Utilities;
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.Diagnostics;
  11. using System.Linq;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using TMPro;
  15. using UnityEngine;
  16. using UnityEngine.UI;
  17. using VRUI;
  18. namespace BSIPA_ModList.UI
  19. {
  20. internal class ModInfoViewController : VRUIViewController
  21. {
  22. internal Sprite Icon;
  23. internal string Name;
  24. internal string Version;
  25. internal string Author;
  26. internal string Description;
  27. internal PluginLoader.PluginMetadata UpdateInfo;
  28. private static RectTransform rowTransformOriginal;
  29. private ModInfoView view;
  30. private RectTransform rowTransform;
  31. private Button linkHomeButton;
  32. private Button linkSourceButton;
  33. private Button linkDonateButton;
  34. private ModListFlowCoordinator flowController;
  35. private bool showEnableDisable = false;
  36. private TextMeshProUGUI restartMessage;
  37. private Button enableDisableButton;
  38. private new bool enabled = false;
  39. public void Init(Sprite icon, string name, string version, string author, string description, PluginLoader.PluginMetadata updateInfo, PluginManifest.LinksObject links = null, bool showEnDis = false, ModListFlowCoordinator mlfc = null)
  40. {
  41. showEnableDisable = showEnDis;
  42. Plugin.OnConfigChaned -= OptHideButton;
  43. Icon = icon;
  44. Name = name;
  45. Version = version;
  46. Author = author;
  47. Description = description;
  48. UpdateInfo = updateInfo;
  49. enabled = !PluginManager.IsDisabled(updateInfo);
  50. flowController = mlfc;
  51. if (rowTransformOriginal == null)
  52. rowTransformOriginal = MenuButtonUI.Instance.GetPrivateField<RectTransform>("menuButtonsOriginal");
  53. // i also have no clue why this is necessary
  54. rectTransform.anchorMin = new Vector2(0f, 0f);
  55. rectTransform.anchorMax = new Vector2(0.5f, 1f);
  56. var go = new GameObject("Info View", typeof(RectTransform));
  57. go.SetActive(false);
  58. go.AddComponent<RectMask2D>();
  59. view = go.AddComponent<ModInfoView>();
  60. var rt = view.transform as RectTransform;
  61. rt.SetParent(transform);
  62. rt.anchorMin = new Vector2(0f, 0f);
  63. rt.anchorMax = new Vector2(1f, 1f);
  64. rt.anchoredPosition = new Vector2(0f, 0);
  65. view.Init(this);
  66. go.SetActive(true);
  67. if (showEnDis)
  68. {
  69. restartMessage = BeatSaberUI.CreateText(rectTransform, "A restart is required to apply", new Vector2(11f, 33.5f));
  70. restartMessage.fontSize = 4f;
  71. restartMessage.gameObject.SetActive(false);
  72. enableDisableButton = BeatSaberUI.CreateUIButton(rectTransform, "CreditsButton", new Vector2(33, 32), new Vector2(25, 10), ToggleEnable);
  73. enableDisableButton.GetComponentInChildren<StartMiddleEndButtonBackgroundController>().SetMiddleSprite();
  74. UpdateButtonText();
  75. Plugin.OnConfigChaned += OptHideButton;
  76. OptHideButton(Plugin.config.Value);
  77. }
  78. SetupLinks(links);
  79. }
  80. protected override void DidDeactivate(DeactivationType deactivationType)
  81. {
  82. base.DidDeactivate(deactivationType);
  83. if (deactivationType == DeactivationType.RemovedFromHierarchy)
  84. Plugin.OnConfigChaned -= OptHideButton;
  85. }
  86. protected override void OnDestroy()
  87. {
  88. Plugin.OnConfigChaned -= OptHideButton;
  89. base.OnDestroy();
  90. }
  91. ~ModInfoViewController()
  92. {
  93. Plugin.OnConfigChaned -= OptHideButton;
  94. }
  95. private void OptHideButton(SelfConfig cfg)
  96. {
  97. enableDisableButton?.gameObject?.SetActive(cfg.ShowEnableDisable);
  98. }
  99. private Action setAction = () => { };
  100. private void ToggleEnable()
  101. {
  102. var info = enabled ? PluginManager.InfoFromMetadata(UpdateInfo) : null;
  103. bool needsRestart;
  104. if (enabled)
  105. needsRestart = PluginManager.DisablePlugin(info);
  106. else
  107. needsRestart = PluginManager.EnablePlugin(UpdateInfo);
  108. UpdateButtonText(!enabled, needsRestart);
  109. if (!needsRestart)
  110. flowController.exitActions.Add(setAction);
  111. }
  112. private void UpdateButtonText(bool? _enabled = null, bool _needsRestart = false)
  113. {
  114. enabled = _enabled ?? enabled;
  115. if (enabled)
  116. enableDisableButton.SetButtonText("Disable");
  117. else
  118. enableDisableButton.SetButtonText("Enable");
  119. restartMessage.gameObject.SetActive(_needsRestart);
  120. }
  121. public void Reload(string name, string version, string author, string description)
  122. {
  123. Name = name;
  124. Version = version;
  125. Author = author;
  126. Description = description;
  127. view.Refresh();
  128. }
  129. private void SetupLinks(PluginManifest.LinksObject links = null, Uri moreInfoLink = null)
  130. {
  131. bool addedLink = false;
  132. if (links != null || moreInfoLink != null)
  133. {
  134. Logger.log.Debug($"Adding links");
  135. rowTransform = Instantiate(rowTransformOriginal, rectTransform);
  136. rowTransform.anchorMin = new Vector2(0f, 0f);
  137. rowTransform.anchorMax = new Vector2(1f, .15f);
  138. rowTransform.anchoredPosition = new Vector2(-3.5f, 4f);
  139. rowTransform.sizeDelta = Vector2.zero;
  140. Destroy(rowTransform.GetComponent<StartMiddleEndButtonsGroup>());
  141. foreach (Transform child in rowTransform)
  142. {
  143. child.name = string.Empty;
  144. Destroy(child.gameObject);
  145. }
  146. if (links?.ProjectHome != null)
  147. {
  148. linkHomeButton = BeatSaberUI.CreateUIButton(rowTransform, "CreditsButton", buttonText: "Home", anchoredPosition: Vector2.zero, sizeDelta: new Vector2(20, 10),
  149. onClick: () => Process.Start(links.ProjectHome.ToString()));
  150. addedLink = true;
  151. }
  152. if (links?.ProjectSource != null)
  153. {
  154. linkSourceButton = BeatSaberUI.CreateUIButton(rowTransform, "CreditsButton", buttonText: "Source", anchoredPosition: Vector2.zero, sizeDelta: new Vector2(20, 10),
  155. onClick: () => Process.Start(links.ProjectSource.ToString()));
  156. addedLink = true;
  157. }
  158. if (links?.Donate != null)
  159. {
  160. linkDonateButton = BeatSaberUI.CreateUIButton(rowTransform, "CreditsButton", buttonText: "Donate", anchoredPosition: Vector2.zero, sizeDelta: new Vector2(20, 10),
  161. onClick: () => Process.Start(links.Donate.ToString()));
  162. addedLink = true;
  163. }
  164. if (moreInfoLink != null)
  165. {
  166. linkDonateButton = BeatSaberUI.CreateUIButton(rowTransform, "CreditsButton", buttonText: "More Info", anchoredPosition: Vector2.zero, sizeDelta: new Vector2(20, 10),
  167. onClick: () => Process.Start(moreInfoLink.ToString()));
  168. addedLink = true;
  169. }
  170. foreach (var cmp in rowTransform.GetComponentsInChildren<StartMiddleEndButtonBackgroundController>())
  171. cmp.SetMiddleSprite();
  172. }
  173. if (UpdateInfo != null && !addedLink)
  174. StartCoroutine(GetMoreInfoLink());
  175. }
  176. private IEnumerator GetMoreInfoLink()
  177. {
  178. Logger.log.Debug($"Getting more info link");
  179. Ref<ApiEndpoint.Mod> mod = new Ref<ApiEndpoint.Mod>(null);
  180. if (UpdateInfo.Id == null) yield break;
  181. yield return Updater.GetModInfo(UpdateInfo.Id, UpdateInfo.Version.ToString(), mod);
  182. try { mod.Verify(); }
  183. catch (Exception e)
  184. {
  185. Logger.log.Warn($"Error getting more info link for mod {UpdateInfo.Id}");
  186. Logger.log.Warn(e);
  187. yield break;
  188. }
  189. SetupLinks(null, mod.Value.Link);
  190. }
  191. #if DEBUG
  192. public void Update()
  193. {
  194. #if ADJUST_INFO_BUTTON_UI_LINKS
  195. RectTransform rt = rowTransform;
  196. if (rt == null) return;
  197. var cpos = rt.anchoredPosition;
  198. if (Input.GetKey(KeyCode.LeftArrow))
  199. {
  200. rt.anchoredPosition = new Vector2(cpos.x - .1f, cpos.y);
  201. }
  202. else if (Input.GetKey(KeyCode.RightArrow))
  203. {
  204. rt.anchoredPosition = new Vector2(cpos.x + .1f, cpos.y);
  205. }
  206. else if (Input.GetKey(KeyCode.UpArrow))
  207. {
  208. rt.anchoredPosition = new Vector2(cpos.x, cpos.y + .1f);
  209. }
  210. else if (Input.GetKey(KeyCode.DownArrow))
  211. {
  212. rt.anchoredPosition = new Vector2(cpos.x, cpos.y - .1f);
  213. }
  214. else
  215. return;
  216. Logger.log.Debug($"Position now at {rt.anchoredPosition}");
  217. #endif
  218. }
  219. #endif
  220. }
  221. internal class ModInfoView : MonoBehaviour
  222. {
  223. private MarkdownView mdv;
  224. private TextMeshProUGUI titleText;
  225. private TextMeshProUGUI authorText;
  226. private Image icon;
  227. private ModInfoViewController controller;
  228. private const string TitleFormat = "{0} <size=60%>{1}";
  229. public void Init(ModInfoViewController controller)
  230. {
  231. this.controller = controller;
  232. var rectTransform = transform as RectTransform;
  233. rectTransform.sizeDelta = new Vector2(60f, 10f);
  234. titleText = BeatSaberUI.CreateText(rectTransform, string.Format(TitleFormat, controller.Name, controller.Version), new Vector2(11f, 27.5f));
  235. titleText.fontSize = 6f;
  236. authorText = BeatSaberUI.CreateText(rectTransform, controller.Author, new Vector2(11f, 22f));
  237. authorText.fontSize = 4.5f;
  238. var mdvgo = new GameObject("MarkDown Desc");
  239. mdvgo.SetActive(false);
  240. mdv = mdvgo.AddComponent<MarkdownView>();
  241. mdv.rectTransform.SetParent(rectTransform);
  242. mdv.rectTransform.anchorMin = new Vector2(.5f, .5f);
  243. mdv.rectTransform.anchorMax = new Vector2(.5f, .5f);
  244. mdv.rectTransform.anchoredPosition = new Vector2(-4f, -3.6f);
  245. mdv.rectTransform.sizeDelta = new Vector2(65f, 40f);
  246. mdvgo.SetActive(true);
  247. mdv.Markdown = controller.Description;
  248. icon = new GameObject("Mod Info View Icon", typeof(RectTransform)).AddComponent<Image>();
  249. icon.gameObject.SetActive(false);
  250. icon.rectTransform.SetParent(rectTransform, false);
  251. icon.rectTransform.anchorMin = new Vector2(0.5f, 0.44f);
  252. icon.rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
  253. icon.rectTransform.sizeDelta = new Vector2(60f, 10f);
  254. icon.rectTransform.anchoredPosition = new Vector2(-27.8f, 27.3f);
  255. icon.sprite = controller.Icon;
  256. icon.preserveAspect = true;
  257. icon.useSpriteMesh = true;
  258. icon.material = CustomUI.Utilities.UIUtilities.NoGlowMaterial;
  259. icon.gameObject.SetActive(true);
  260. }
  261. public void Refresh()
  262. {
  263. titleText.text = string.Format(TitleFormat, controller.Name, controller.Version);
  264. authorText.text = controller.Author;
  265. mdv.Markdown = controller.Description;
  266. icon.sprite = controller.Icon;
  267. }
  268. #if DEBUG
  269. #if ADJUST_INFO_TEXT_UI_KEYS
  270. private int currentItem = 0;
  271. #endif
  272. public void Update()
  273. {
  274. #if ADJUST_INFO_TEXT_UI_KEYS
  275. RectTransform rt;
  276. switch (currentItem)
  277. {
  278. default:
  279. currentItem = 0;
  280. goto case 0; // idk why this is needed tbh
  281. case 0:
  282. rt = titleText.rectTransform;
  283. break;
  284. case 1:
  285. rt = authorText.rectTransform;
  286. break;
  287. case 2:
  288. rt = descText.rectTransform;
  289. break;
  290. }
  291. if (Input.GetKeyDown(KeyCode.N))
  292. currentItem++;
  293. var cpos = rt.anchoredPosition;
  294. if (Input.GetKey(KeyCode.LeftArrow))
  295. {
  296. rt.anchoredPosition = new Vector2(cpos.x - .1f, cpos.y);
  297. }
  298. else if (Input.GetKey(KeyCode.RightArrow))
  299. {
  300. rt.anchoredPosition = new Vector2(cpos.x + .1f, cpos.y);
  301. }
  302. else if (Input.GetKey(KeyCode.UpArrow))
  303. {
  304. rt.anchoredPosition = new Vector2(cpos.x, cpos.y + .1f);
  305. }
  306. else if (Input.GetKey(KeyCode.DownArrow))
  307. {
  308. rt.anchoredPosition = new Vector2(cpos.x, cpos.y - .1f);
  309. }
  310. else
  311. return;
  312. Logger.log.Debug($"Position now at {rt.anchoredPosition}");
  313. #endif
  314. #if ADJUST_INFO_ICON_UI_KEYS
  315. var rt = icon.rectTransform;
  316. if (Input.GetKey(KeyCode.Z))
  317. { // adjust anchormin
  318. var cpos = rt.anchorMin;
  319. if (Input.GetKey(KeyCode.LeftArrow))
  320. {
  321. rt.anchorMin = new Vector2(cpos.x - .001f, cpos.y);
  322. }
  323. else if (Input.GetKey(KeyCode.RightArrow))
  324. {
  325. rt.anchorMin = new Vector2(cpos.x + .001f, cpos.y);
  326. }
  327. else if (Input.GetKey(KeyCode.UpArrow))
  328. {
  329. rt.anchorMin = new Vector2(cpos.x, cpos.y + .001f);
  330. }
  331. else if (Input.GetKey(KeyCode.DownArrow))
  332. {
  333. rt.anchorMin = new Vector2(cpos.x, cpos.y - .001f);
  334. }
  335. else
  336. return;
  337. Logger.log.Debug($"Anchor min now at {rt.anchorMin}");
  338. }
  339. else if(Input.GetKey(KeyCode.X))
  340. { // adjust anchorMax
  341. var cpos = rt.anchorMax;
  342. if (Input.GetKey(KeyCode.LeftArrow))
  343. {
  344. rt.anchorMax = new Vector2(cpos.x - .001f, cpos.y);
  345. }
  346. else if (Input.GetKey(KeyCode.RightArrow))
  347. {
  348. rt.anchorMax = new Vector2(cpos.x + .001f, cpos.y);
  349. }
  350. else if (Input.GetKey(KeyCode.UpArrow))
  351. {
  352. rt.anchorMax = new Vector2(cpos.x, cpos.y + .001f);
  353. }
  354. else if (Input.GetKey(KeyCode.DownArrow))
  355. {
  356. rt.anchorMax = new Vector2(cpos.x, cpos.y - .001f);
  357. }
  358. else
  359. return;
  360. Logger.log.Debug($"Anchor max now at {rt.anchorMax}");
  361. }
  362. else
  363. {
  364. var cpos = rt.anchoredPosition;
  365. if (Input.GetKey(KeyCode.LeftArrow))
  366. {
  367. rt.anchoredPosition = new Vector2(cpos.x - .1f, cpos.y);
  368. }
  369. else if (Input.GetKey(KeyCode.RightArrow))
  370. {
  371. rt.anchoredPosition = new Vector2(cpos.x + .1f, cpos.y);
  372. }
  373. else if (Input.GetKey(KeyCode.UpArrow))
  374. {
  375. rt.anchoredPosition = new Vector2(cpos.x, cpos.y + .1f);
  376. }
  377. else if (Input.GetKey(KeyCode.DownArrow))
  378. {
  379. rt.anchoredPosition = new Vector2(cpos.x, cpos.y - .1f);
  380. }
  381. else
  382. return;
  383. Logger.log.Debug($"Position now at {rt.anchoredPosition}");
  384. }
  385. #endif
  386. }
  387. #endif
  388. }
  389. }