SongBrowserUI.cs 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737
  1. using UnityEngine;
  2. using System.Linq;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using UnityEngine.UI;
  7. using HMUI;
  8. using VRUI;
  9. using SongBrowserPlugin.DataAccess;
  10. using System.IO;
  11. using SongLoaderPlugin;
  12. using System.Security.Cryptography;
  13. using System.Text;
  14. using TMPro;
  15. using Logger = SongBrowserPlugin.Logging.Logger;
  16. using SongBrowserPlugin.DataAccess.BeatSaverApi;
  17. using System.Collections;
  18. namespace SongBrowserPlugin.UI
  19. {
  20. /// <summary>
  21. /// Hijack the flow coordinator. Have access to all StandardLevel easily.
  22. /// </summary>
  23. public class SongBrowserUI : MonoBehaviour
  24. {
  25. // Logging
  26. public const String Name = "SongBrowserUI";
  27. private const float SEGMENT_PERCENT = 0.1f;
  28. private const int LIST_ITEMS_VISIBLE_AT_ONCE = 6;
  29. // Beat Saber UI Elements
  30. private FlowCoordinator _levelSelectionFlowCoordinator;
  31. private LevelPacksViewController _levelPackViewController;
  32. private LevelPacksTableView _levelPacksTableView;
  33. private LevelPackDetailViewController _levelPackDetailViewController;
  34. private LevelPackLevelsViewController _levelPackLevelsViewController;
  35. private LevelPackLevelsTableView _levelPackLevelsTableView;
  36. private StandardLevelDetailViewController _levelDetailViewController;
  37. private StandardLevelDetailView _standardLevelDetailView;
  38. private BeatmapDifficultySegmentedControlController _levelDifficultyViewController;
  39. private BeatmapCharacteristicSegmentedControlController _beatmapCharacteristicSelectionViewController;
  40. private DismissableNavigationController _levelSelectionNavigationController;
  41. private RectTransform _tableViewRectTransform;
  42. private Button _tableViewPageUpButton;
  43. private Button _tableViewPageDownButton;
  44. private Button _playButton;
  45. // New UI Elements
  46. private List<SongSortButton> _sortButtonGroup;
  47. private List<SongFilterButton> _filterButtonGroup;
  48. private Button _clearSortFilterButton;
  49. private Button _addFavoriteButton;
  50. private SimpleDialogPromptViewController _simpleDialogPromptViewControllerPrefab;
  51. private SimpleDialogPromptViewController _deleteDialog;
  52. private Button _deleteButton;
  53. private Button _pageUpFastButton;
  54. private Button _pageDownFastButton;
  55. private SearchKeyboardViewController _searchViewController;
  56. private PlaylistFlowCoordinator _playListFlowCoordinator;
  57. private RectTransform _ppStatButton;
  58. private RectTransform _starStatButton;
  59. private RectTransform _njsStatButton;
  60. // Cached items
  61. private Sprite _addFavoriteSprite;
  62. private Sprite _removeFavoriteSprite;
  63. private Sprite _currentAddFavoriteButtonSprite;
  64. // Plugin Compat checks
  65. private bool _detectedTwitchPluginQueue = false;
  66. private bool _checkedForTwitchPlugin = false;
  67. // Debug
  68. private int _sortButtonLastPushedIndex = 0;
  69. private int _lastRow = 0;
  70. // Model
  71. private SongBrowserModel _model;
  72. // UI Created
  73. private bool _uiCreated = false;
  74. public SongBrowserModel Model
  75. {
  76. get
  77. {
  78. return _model;
  79. }
  80. }
  81. /// <summary>
  82. /// Constructor
  83. /// </summary>
  84. public SongBrowserUI() : base()
  85. {
  86. if (_model == null)
  87. {
  88. _model = new SongBrowserModel();
  89. }
  90. _model.Init();
  91. _sortButtonLastPushedIndex = (int)(_model.Settings.sortMode);
  92. }
  93. /// <summary>
  94. /// Builds the UI for this plugin.
  95. /// </summary>
  96. public void CreateUI(MainMenuViewController.MenuButton mode)
  97. {
  98. Logger.Trace("CreateUI()");
  99. // Determine the flow controller to use
  100. if (mode == MainMenuViewController.MenuButton.SoloFreePlay)
  101. {
  102. Logger.Debug("Entering SOLO mode...");
  103. _levelSelectionFlowCoordinator = Resources.FindObjectsOfTypeAll<SoloFreePlayFlowCoordinator>().First();
  104. }
  105. else if (mode == MainMenuViewController.MenuButton.Party)
  106. {
  107. Logger.Debug("Entering PARTY mode...");
  108. _levelSelectionFlowCoordinator = Resources.FindObjectsOfTypeAll<PartyFreePlayFlowCoordinator>().First();
  109. }
  110. else
  111. {
  112. Logger.Debug("Entering SOLO CAMPAIGN mode...");
  113. _levelSelectionFlowCoordinator = Resources.FindObjectsOfTypeAll<CampaignFlowCoordinator>().First();
  114. }
  115. // returning to the menu and switching modes could trigger this.
  116. if (_uiCreated)
  117. {
  118. return;
  119. }
  120. try
  121. {
  122. // gather controllers and ui elements.
  123. _levelPackViewController = _levelSelectionFlowCoordinator.GetPrivateField<LevelPacksViewController>("_levelPacksViewController");
  124. Logger.Debug("Acquired LevelPacksViewController [{0}]", _levelPackViewController.GetInstanceID());
  125. _levelPackDetailViewController = _levelSelectionFlowCoordinator.GetPrivateField<LevelPackDetailViewController>("_levelPackDetailViewController");
  126. Logger.Debug("Acquired LevelPackDetailViewController [{0}]", _levelPackDetailViewController.GetInstanceID());
  127. _levelPacksTableView = _levelPackViewController.GetPrivateField<LevelPacksTableView>("_levelPacksTableView");
  128. Logger.Debug("Acquired LevelPacksTableView [{0}]", _levelPacksTableView.GetInstanceID());
  129. _levelPackLevelsViewController = _levelSelectionFlowCoordinator.GetPrivateField<LevelPackLevelsViewController>("_levelPackLevelsViewController");
  130. Logger.Debug("Acquired LevelPackLevelsViewController [{0}]", _levelPackLevelsViewController.GetInstanceID());
  131. _levelPackLevelsTableView = this._levelPackLevelsViewController.GetPrivateField<LevelPackLevelsTableView>("_levelPackLevelsTableView");
  132. Logger.Debug("Acquired LevelPackLevelsTableView [{0}]", _levelPackLevelsTableView.GetInstanceID());
  133. _levelDetailViewController = _levelSelectionFlowCoordinator.GetPrivateField<StandardLevelDetailViewController>("_levelDetailViewController");
  134. Logger.Debug("Acquired StandardLevelDetailViewController [{0}]", _levelDetailViewController.GetInstanceID());
  135. _standardLevelDetailView = _levelDetailViewController.GetPrivateField<StandardLevelDetailView>("_standardLevelDetailView");
  136. Logger.Debug("Acquired StandardLevelDetailView [{0}]", _standardLevelDetailView.GetInstanceID());
  137. _beatmapCharacteristicSelectionViewController = Resources.FindObjectsOfTypeAll<BeatmapCharacteristicSegmentedControlController>().First();
  138. Logger.Debug("Acquired BeatmapCharacteristicSegmentedControlController [{0}]", _beatmapCharacteristicSelectionViewController.GetInstanceID());
  139. _levelSelectionNavigationController = _levelSelectionFlowCoordinator.GetPrivateField<DismissableNavigationController>("_navigationController");
  140. Logger.Debug("Acquired DismissableNavigationController [{0}]", _levelSelectionNavigationController.GetInstanceID());
  141. _levelDifficultyViewController = _standardLevelDetailView.GetPrivateField<BeatmapDifficultySegmentedControlController>("_beatmapDifficultySegmentedControlController");
  142. Logger.Debug("Acquired BeatmapDifficultySegmentedControlController [{0}]", _levelDifficultyViewController.GetInstanceID());
  143. _tableViewRectTransform = _levelPackLevelsTableView.transform as RectTransform;
  144. Logger.Debug("Acquired TableViewRectTransform from LevelPackLevelsTableView [{0}]", _tableViewRectTransform.GetInstanceID());
  145. _tableViewPageUpButton = _tableViewRectTransform.GetComponentsInChildren<Button>().First(x => x.name == "PageUpButton");
  146. _tableViewPageDownButton = _tableViewRectTransform.GetComponentsInChildren<Button>().First(x => x.name == "PageDownButton");
  147. Logger.Debug("Acquired Page Up and Down buttons...");
  148. _playButton = _standardLevelDetailView.GetComponentsInChildren<Button>().FirstOrDefault(x => x.name == "PlayButton");
  149. Logger.Debug("Acquired PlayButton [{0}]", _playButton.GetInstanceID());
  150. _simpleDialogPromptViewControllerPrefab = Resources.FindObjectsOfTypeAll<SimpleDialogPromptViewController>().First();
  151. if (_playListFlowCoordinator == null)
  152. {
  153. _playListFlowCoordinator = UIBuilder.CreateFlowCoordinator<PlaylistFlowCoordinator>("PlaylistFlowCoordinator");
  154. _playListFlowCoordinator.didFinishEvent += HandleDidSelectPlaylist;
  155. }
  156. // delete dialog
  157. this._deleteDialog = UnityEngine.Object.Instantiate<SimpleDialogPromptViewController>(this._simpleDialogPromptViewControllerPrefab);
  158. this._deleteDialog.name = "DeleteDialogPromptViewController";
  159. this._deleteDialog.gameObject.SetActive(false);
  160. // sprites
  161. this._addFavoriteSprite = Base64Sprites.AddToFavoritesIcon;
  162. this._removeFavoriteSprite = Base64Sprites.RemoveFromFavoritesIcon;
  163. // create song browser main ui
  164. this.CreateUIElements();
  165. this.InstallHandlers();
  166. this.ResizeStatsPanel();
  167. _uiCreated = true;
  168. Logger.Debug("Done Creating UI...");
  169. }
  170. catch (Exception e)
  171. {
  172. Logger.Exception("Exception during CreateUI: ", e);
  173. }
  174. }
  175. /// <summary>
  176. /// Builds the SongBrowser UI
  177. /// </summary>
  178. private void CreateUIElements()
  179. {
  180. Logger.Trace("CreateUIElements");
  181. try
  182. {
  183. // Gather some transforms and templates to use.
  184. RectTransform sortButtonTransform = this._levelSelectionNavigationController.transform as RectTransform;
  185. RectTransform otherButtonTransform = this._levelDetailViewController.transform as RectTransform;
  186. Button sortButtonTemplate = Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "SettingsButton"));
  187. Button otherButtonTemplate = Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "SettingsButton"));
  188. RectTransform playContainerRect = _standardLevelDetailView.GetComponentsInChildren<RectTransform>().First(x => x.name == "PlayContainer");
  189. RectTransform playButtonsRect = playContainerRect.GetComponentsInChildren<RectTransform>().First(x => x.name == "PlayButtons");
  190. Button practiceButton = playButtonsRect.GetComponentsInChildren<Button>().First(x => x.name == "PracticeButton");
  191. RectTransform practiceButtonRect = (practiceButton.transform as RectTransform);
  192. Button playButton = Resources.FindObjectsOfTypeAll<Button>().First(x => x.name == "PlayButton");
  193. RectTransform playButtonRect = (playButton.transform as RectTransform);
  194. Sprite arrowIcon = SongBrowserApplication.Instance.CachedIcons["ArrowIcon"];
  195. Sprite borderSprite = SongBrowserApplication.Instance.CachedIcons["RoundRectBigStroke"];
  196. // Resize some of the UI
  197. _tableViewRectTransform.sizeDelta = new Vector2(0f, -20f);
  198. _tableViewRectTransform.anchoredPosition = new Vector2(0f, -2.5f);
  199. // Create Sorting Songs By-Buttons
  200. Logger.Debug("Creating sort by buttons...");
  201. float buttonSpacing = 0.5f;
  202. float fontSize = 2.0f;
  203. float buttonWidth = 12.25f;
  204. float buttonHeight = 5.5f;
  205. float startButtonX = 24.50f;
  206. float curButtonX = 0.0f;
  207. float buttonY = -6.0f;
  208. Vector2 iconButtonSize = new Vector2(buttonHeight, buttonHeight);
  209. // Create cancel button
  210. Logger.Debug("Creating cancel button...");
  211. _clearSortFilterButton = UIBuilder.CreateIconButton(
  212. sortButtonTransform,
  213. otherButtonTemplate,
  214. Base64Sprites.XIcon,
  215. new Vector2(startButtonX - buttonHeight - (buttonSpacing * 2.0f), buttonY),
  216. new Vector2(iconButtonSize.x, iconButtonSize.y),
  217. new Vector2(3.5f, 3.5f),
  218. new Vector2(1.0f, 1.0f),
  219. 0);
  220. _clearSortFilterButton.onClick.RemoveAllListeners();
  221. _clearSortFilterButton.onClick.AddListener(delegate () {
  222. OnClearButtonClickEvent();
  223. });
  224. startButtonX += (buttonHeight + buttonSpacing);
  225. // define sort buttons
  226. string[] sortButtonNames = new string[]
  227. {
  228. "Song", "Author", "Newest", "Plays", "PP", "Difficult", "Random"
  229. };
  230. SongSortMode[] sortModes = new SongSortMode[]
  231. {
  232. SongSortMode.Default, SongSortMode.Author, SongSortMode.Newest, SongSortMode.PlayCount, SongSortMode.PP, SongSortMode.Difficulty, SongSortMode.Random
  233. };
  234. _sortButtonGroup = new List<SongSortButton>();
  235. for (int i = 0; i < sortButtonNames.Length; i++)
  236. {
  237. curButtonX = startButtonX + (buttonWidth*i) + (buttonSpacing*i);
  238. SongSortButton newButton = UIBuilder.CreateSortButton(sortButtonTransform, sortButtonTemplate, arrowIcon, borderSprite,
  239. sortButtonNames[i],
  240. fontSize,
  241. curButtonX,
  242. buttonY,
  243. buttonWidth,
  244. buttonHeight,
  245. sortModes[i],
  246. OnSortButtonClickEvent);
  247. _sortButtonGroup.Add(newButton);
  248. newButton.Button.name = "Sort" + sortModes[i].ToString() + "Button";
  249. }
  250. // Create filter buttons
  251. Logger.Debug("Creating filter buttons...");
  252. float filterButtonX = curButtonX + (buttonWidth / 2.0f);
  253. List<Tuple<SongFilterMode, UnityEngine.Events.UnityAction, Sprite>> filterButtonSetup = new List<Tuple<SongFilterMode, UnityEngine.Events.UnityAction, Sprite>>()
  254. {
  255. Tuple.Create<SongFilterMode, UnityEngine.Events.UnityAction, Sprite>(SongFilterMode.Favorites, OnFavoriteFilterButtonClickEvent, Base64Sprites.StarFullIcon),
  256. Tuple.Create<SongFilterMode, UnityEngine.Events.UnityAction, Sprite>(SongFilterMode.Playlist, OnPlaylistButtonClickEvent, Base64Sprites.PlaylistIcon),
  257. Tuple.Create<SongFilterMode, UnityEngine.Events.UnityAction, Sprite>(SongFilterMode.Search, OnSearchButtonClickEvent, Base64Sprites.SearchIcon),
  258. };
  259. _filterButtonGroup = new List<SongFilterButton>();
  260. for (int i = 0; i < filterButtonSetup.Count; i++)
  261. {
  262. Tuple<SongFilterMode, UnityEngine.Events.UnityAction, Sprite> t = filterButtonSetup[i];
  263. Button b = UIBuilder.CreateIconButton(sortButtonTransform, otherButtonTemplate,
  264. t.Item3,
  265. new Vector2(filterButtonX + (iconButtonSize.x * i) + (buttonSpacing * i), buttonY),
  266. new Vector2(iconButtonSize.x, iconButtonSize.y),
  267. new Vector2(3.5f, 3.5f),
  268. new Vector2(1.0f, 1.0f),
  269. 0);
  270. SongFilterButton filterButton = new SongFilterButton
  271. {
  272. Button = b,
  273. FilterMode = t.Item1
  274. };
  275. b.onClick.AddListener(t.Item2);
  276. filterButton.Button.name = "Filter" + t.Item1.ToString() + "Button";
  277. _filterButtonGroup.Add(filterButton);
  278. }
  279. // Create add favorite button
  280. Logger.Debug("Creating Add to favorites button...");
  281. _addFavoriteButton = UIBuilder.CreateIconButton(playButtonsRect,
  282. practiceButton,
  283. Base64Sprites.AddToFavoritesIcon
  284. );
  285. _addFavoriteButton.onClick.RemoveAllListeners();
  286. _addFavoriteButton.onClick.AddListener(delegate () {
  287. ToggleSongInPlaylist();
  288. });
  289. // Create delete button
  290. Logger.Debug("Creating delete button...");
  291. _deleteButton = UIBuilder.CreateIconButton(playButtonsRect,
  292. practiceButton,
  293. Base64Sprites.DeleteIcon
  294. );
  295. _deleteButton.onClick.RemoveAllListeners();
  296. _deleteButton.onClick.AddListener(delegate () {
  297. HandleDeleteSelectedLevel();
  298. });
  299. // Create fast scroll buttons
  300. int pageFastButtonX = 25;
  301. Vector2 pageFastSize = new Vector2(12.5f, 7.75f);
  302. Vector2 pageFastIconSize = new Vector2(0.1f, 0.1f);
  303. Vector2 pageFastIconScale = new Vector2(0.4f, 0.4f);
  304. Logger.Debug("Creating fast scroll button...");
  305. _pageUpFastButton = UIBuilder.CreateIconButton(sortButtonTransform, otherButtonTemplate, arrowIcon,
  306. new Vector2(pageFastButtonX, -13f),
  307. pageFastSize,
  308. pageFastIconSize,
  309. pageFastIconScale,
  310. 180);
  311. UnityEngine.GameObject.Destroy(_pageUpFastButton.GetComponentsInChildren<UnityEngine.UI.Image>().First(btn => btn.name == "Stroke"));
  312. _pageUpFastButton.onClick.AddListener(delegate ()
  313. {
  314. this.JumpSongList(-1, SEGMENT_PERCENT);
  315. });
  316. _pageDownFastButton = UIBuilder.CreateIconButton(sortButtonTransform, otherButtonTemplate, arrowIcon,
  317. new Vector2(pageFastButtonX, -80f),
  318. pageFastSize,
  319. pageFastIconSize,
  320. pageFastIconScale,
  321. 0);
  322. UnityEngine.GameObject.Destroy(_pageDownFastButton.GetComponentsInChildren<UnityEngine.UI.Image>().First(btn => btn.name == "Stroke"));
  323. _pageDownFastButton.onClick.AddListener(delegate ()
  324. {
  325. this.JumpSongList(1, SEGMENT_PERCENT);
  326. });
  327. RefreshSortButtonUI();
  328. Logger.Debug("Done Creating UIElements");
  329. }
  330. catch (Exception e)
  331. {
  332. Logger.Exception("Exception CreateUIElements:", e);
  333. }
  334. }
  335. /// <summary>
  336. /// Resize the stats panel to fit more stats.
  337. /// </summary>
  338. private void ResizeStatsPanel()
  339. {
  340. // modify details view
  341. Logger.Debug("Resizing Stats Panel...");
  342. var statsPanel = _standardLevelDetailView.GetPrivateField<LevelParamsPanel>("_levelParamsPanel");
  343. var statTransforms = statsPanel.GetComponentsInChildren<RectTransform>();
  344. var valueTexts = statsPanel.GetComponentsInChildren<TextMeshProUGUI>().Where(x => x.name == "ValueText").ToList();
  345. RectTransform panelRect = (statsPanel.transform as RectTransform);
  346. panelRect.sizeDelta = new Vector2(panelRect.sizeDelta.x * 1.2f, panelRect.sizeDelta.y * 1.2f);
  347. for (int i = 0; i < statTransforms.Length; i++)
  348. {
  349. var r = statTransforms[i];
  350. if (r.name == "Separator")
  351. {
  352. continue;
  353. }
  354. r.sizeDelta = new Vector2(r.sizeDelta.x * 0.75f, r.sizeDelta.y * 0.75f);
  355. }
  356. for (int i = 0; i < valueTexts.Count; i++)
  357. {
  358. var text = valueTexts[i];
  359. text.fontSize = 3.25f;
  360. }
  361. _ppStatButton = UnityEngine.Object.Instantiate(statTransforms[1], statsPanel.transform, false);
  362. UIBuilder.SetStatButtonIcon(_ppStatButton, Base64Sprites.GraphIcon);
  363. _starStatButton = UnityEngine.Object.Instantiate(statTransforms[1], statsPanel.transform, false);
  364. UIBuilder.SetStatButtonIcon(_starStatButton, Base64Sprites.StarIcon);
  365. _njsStatButton = UnityEngine.Object.Instantiate(statTransforms[1], statsPanel.transform, false);
  366. UIBuilder.SetStatButtonIcon(_njsStatButton, Base64Sprites.SpeedIcon);
  367. // shrink title
  368. var titleText = this._levelDetailViewController.GetComponentsInChildren<TextMeshProUGUI>(true).First(x => x.name == "SongNameText");
  369. titleText.fontSize = 5.0f;
  370. }
  371. /// <summary>
  372. /// Show the UI.
  373. /// </summary>
  374. public void Show()
  375. {
  376. Logger.Trace("Show SongBrowserUI()");
  377. this.SetVisibility(true);
  378. }
  379. /// <summary>
  380. /// Hide the UI.
  381. /// </summary>
  382. public void Hide()
  383. {
  384. Logger.Trace("Hide SongBrowserUI()");
  385. this.SetVisibility(false);
  386. }
  387. /// <summary>
  388. /// Handle showing or hiding UI logic.
  389. /// </summary>
  390. /// <param name="visible"></param>
  391. private void SetVisibility(bool visible)
  392. {
  393. // UI not created, nothing visible to hide...
  394. if (!_uiCreated)
  395. {
  396. return;
  397. }
  398. _ppStatButton.gameObject.SetActive(visible);
  399. _starStatButton.gameObject.SetActive(visible);
  400. _njsStatButton.gameObject.SetActive(visible);
  401. _clearSortFilterButton.gameObject.SetActive(visible);
  402. _sortButtonGroup.ForEach(x => x.Button.gameObject.SetActive(visible));
  403. _filterButtonGroup.ForEach(x => x.Button.gameObject.SetActive(visible));
  404. _addFavoriteButton.gameObject.SetActive(visible);
  405. _deleteButton.gameObject.SetActive(visible);
  406. _pageUpFastButton.gameObject.SetActive(visible);
  407. _pageDownFastButton.gameObject.SetActive(visible);
  408. }
  409. /// <summary>
  410. /// Add our handlers into BeatSaber.
  411. /// </summary>
  412. private void InstallHandlers()
  413. {
  414. // level pack, level, difficulty handlers, characteristics
  415. TableView tableView = ReflectionUtil.GetPrivateField<TableView>(_levelPackLevelsTableView, "_tableView");
  416. tableView.didSelectCellWithIdxEvent -= HandleDidSelectTableViewRow;
  417. tableView.didSelectCellWithIdxEvent += HandleDidSelectTableViewRow;
  418. _levelPackLevelsViewController.didSelectLevelEvent -= OnDidSelectLevelEvent;
  419. _levelPackLevelsViewController.didSelectLevelEvent += OnDidSelectLevelEvent;
  420. _levelDifficultyViewController.didSelectDifficultyEvent -= OnDidSelectDifficultyEvent;
  421. _levelDifficultyViewController.didSelectDifficultyEvent += OnDidSelectDifficultyEvent;
  422. _levelPacksTableView.didSelectPackEvent -= _levelPacksTableView_didSelectPackEvent;
  423. _levelPacksTableView.didSelectPackEvent += _levelPacksTableView_didSelectPackEvent;
  424. _levelPackViewController.didSelectPackEvent -= _levelPackViewController_didSelectPackEvent;
  425. _levelPackViewController.didSelectPackEvent += _levelPackViewController_didSelectPackEvent;
  426. _beatmapCharacteristicSelectionViewController.didSelectBeatmapCharacteristicEvent -= OnDidSelectBeatmapCharacteristic;
  427. _beatmapCharacteristicSelectionViewController.didSelectBeatmapCharacteristicEvent += OnDidSelectBeatmapCharacteristic;
  428. // make sure the quick scroll buttons don't desync with regular scrolling
  429. _tableViewPageDownButton.onClick.AddListener(delegate ()
  430. {
  431. this.RefreshQuickScrollButtons();
  432. });
  433. _tableViewPageUpButton.onClick.AddListener(delegate ()
  434. {
  435. this.RefreshQuickScrollButtons();
  436. });
  437. // finished level
  438. ResultsViewController resultsViewController = _levelSelectionFlowCoordinator.GetPrivateField<ResultsViewController>("_resultsViewController");
  439. resultsViewController.continueButtonPressedEvent += ResultsViewController_continueButtonPressedEvent;
  440. }
  441. /// <summary>
  442. /// Handle updating the level pack selection after returning from a song.
  443. /// </summary>
  444. /// <param name="obj"></param>
  445. private void ResultsViewController_continueButtonPressedEvent(ResultsViewController obj)
  446. {
  447. StartCoroutine(this.UpdateLevelPackSelectionEndOfFrame());
  448. }
  449. public IEnumerator UpdateLevelPackSelectionEndOfFrame()
  450. {
  451. yield return new WaitForEndOfFrame();
  452. this.UpdateLevelPackSelection();
  453. SelectAndScrollToLevel(_levelPackLevelsTableView, _model.LastSelectedLevelId);
  454. }
  455. /// <summary>
  456. /// Handler for level pack selection.
  457. /// </summary>
  458. /// <param name="arg1"></param>
  459. /// <param name="arg2"></param>
  460. private void _levelPacksTableView_didSelectPackEvent(LevelPacksTableView arg1, IBeatmapLevelPack arg2)
  461. {
  462. Logger.Trace("_levelPacksTableView_didSelectPackEvent(arg2={0})", arg2);
  463. try
  464. {
  465. // reset filter mode always here
  466. this._model.Settings.filterMode = SongFilterMode.None;
  467. this._model.Settings.Save();
  468. this._model.SetCurrentLevelPack(arg2);
  469. this._model.ProcessSongList();
  470. RefreshSongList();
  471. RefreshSortButtonUI();
  472. RefreshQuickScrollButtons();
  473. }
  474. catch (Exception e)
  475. {
  476. Logger.Exception("Exception handling didSelectPackEvent...", e);
  477. }
  478. }
  479. /// <summary>
  480. /// Handler for level pack selection, controller.
  481. /// Sets the current level pack into the model and updates.
  482. /// </summary>
  483. /// <param name="arg1"></param>
  484. /// <param name="arg2"></param>
  485. private void _levelPackViewController_didSelectPackEvent(LevelPacksViewController arg1, IBeatmapLevelPack arg2)
  486. {
  487. Logger.Trace("_levelPackViewController_didSelectPackEvent(arg2={0})", arg2);
  488. try
  489. {
  490. RefreshSongList();
  491. RefreshSortButtonUI();
  492. RefreshQuickScrollButtons();
  493. }
  494. catch (Exception e)
  495. {
  496. Logger.Exception("Exception handling didSelectPackEvent...", e);
  497. }
  498. }
  499. private void OnClearButtonClickEvent()
  500. {
  501. Logger.Debug("Clearing all sorts and filters.");
  502. _model.Settings.sortMode = SongSortMode.Original;
  503. _model.Settings.filterMode = SongFilterMode.None;
  504. _model.Settings.invertSortResults = false;
  505. _model.Settings.Save();
  506. this._model.ProcessSongList();
  507. RefreshSongList();
  508. RefreshSortButtonUI();
  509. }
  510. /// <summary>
  511. /// Sort button clicked.
  512. /// </summary>
  513. private void OnSortButtonClickEvent(SongSortMode sortMode)
  514. {
  515. Logger.Debug("Sort button - {0} - pressed.", sortMode.ToString());
  516. _model.LastSelectedLevelId = null;
  517. if (_model.Settings.sortMode == sortMode)
  518. {
  519. _model.ToggleInverting();
  520. }
  521. _model.Settings.sortMode = sortMode;
  522. // update the seed
  523. if (_model.Settings.sortMode == SongSortMode.Random)
  524. {
  525. this.Model.Settings.randomSongSeed = Guid.NewGuid().GetHashCode();
  526. }
  527. _model.Settings.Save();
  528. this._model.ProcessSongList();
  529. RefreshSongList();
  530. RefreshSortButtonUI();
  531. RefreshQuickScrollButtons();
  532. // Handle instant queue logic
  533. if (_model.Settings.sortMode == SongSortMode.Random && _model.Settings.randomInstantQueue)
  534. {
  535. int index = 0;
  536. if (_model.SortedSongList.Count > index)
  537. {
  538. this.SelectAndScrollToLevel(_levelPackLevelsTableView, _model.SortedSongList[index].levelID);
  539. var beatMapDifficulties = _model.SortedSongList[index].difficultyBeatmapSets
  540. .Where(x => x.beatmapCharacteristic == _model.CurrentBeatmapCharacteristicSO)
  541. .SelectMany(x => x.difficultyBeatmaps);
  542. this._levelDifficultyViewController.HandleDifficultySegmentedControlDidSelectCell(null, beatMapDifficulties.Count() - 1);
  543. _playButton.onClick.Invoke();
  544. }
  545. }
  546. //Scroll to start of the list
  547. TableView listTableView = _levelPackLevelsTableView.GetPrivateField<TableView>("_tableView");
  548. listTableView.ScrollToCellWithIdx(0, TableView.ScrollPositionType.Beginning, false);
  549. }
  550. /// <summary>
  551. /// Filter by favorites.
  552. /// </summary>
  553. private void OnFavoriteFilterButtonClickEvent()
  554. {
  555. Logger.Debug("Filter button - {0} - pressed.", SongFilterMode.Favorites.ToString());
  556. if (_model.Settings.filterMode != SongFilterMode.Favorites)
  557. {
  558. _model.Settings.filterMode = SongFilterMode.Favorites;
  559. }
  560. else
  561. {
  562. _model.Settings.filterMode = SongFilterMode.None;
  563. }
  564. _model.Settings.Save();
  565. _model.ProcessSongList();
  566. RefreshSongList();
  567. RefreshSortButtonUI();
  568. RefreshQuickScrollButtons();
  569. }
  570. /// <summary>
  571. /// Filter button clicked.
  572. /// </summary>
  573. /// <param name="sortMode"></param>
  574. private void OnSearchButtonClickEvent()
  575. {
  576. Logger.Debug("Filter button - {0} - pressed.", SongFilterMode.Search.ToString());
  577. if (_model.Settings.filterMode != SongFilterMode.Search)
  578. {
  579. this.ShowSearchKeyboard();
  580. }
  581. else
  582. {
  583. _model.Settings.filterMode = SongFilterMode.None;
  584. _model.ProcessSongList();
  585. RefreshSongList();
  586. RefreshSortButtonUI();
  587. RefreshQuickScrollButtons();
  588. }
  589. _model.Settings.Save();
  590. }
  591. /// <summary>
  592. /// Display the playlist selector.
  593. /// </summary>
  594. /// <param name="sortMode"></param>
  595. private void OnPlaylistButtonClickEvent()
  596. {
  597. Logger.Debug("Filter button - {0} - pressed.", SongFilterMode.Playlist.ToString());
  598. _model.LastSelectedLevelId = null;
  599. if (_model.Settings.filterMode != SongFilterMode.Playlist)
  600. {
  601. _playListFlowCoordinator.parentFlowCoordinator = _levelSelectionFlowCoordinator;
  602. _levelSelectionFlowCoordinator.InvokePrivateMethod("PresentFlowCoordinator", new object[] { _playListFlowCoordinator, null, false, false });
  603. }
  604. else
  605. {
  606. _model.Settings.filterMode = SongFilterMode.None;
  607. _model.Settings.Save();
  608. _model.ProcessSongList();
  609. RefreshSongList();
  610. RefreshSortButtonUI();
  611. RefreshQuickScrollButtons();
  612. }
  613. }
  614. /// <summary>
  615. /// Adjust UI based on level selected.
  616. /// Various ways of detecting if a level is not properly selected. Seems most hit the first one.
  617. /// </summary>
  618. private void OnDidSelectLevelEvent(LevelPackLevelsViewController view, IPreviewBeatmapLevel level)
  619. {
  620. try
  621. {
  622. Logger.Trace("OnDidSelectLevelEvent()");
  623. if (level == null)
  624. {
  625. Logger.Debug("No level selected?");
  626. return;
  627. }
  628. if (_model.Settings == null)
  629. {
  630. Logger.Debug("Settings not instantiated yet?");
  631. return;
  632. }
  633. _model.LastSelectedLevelId = level.levelID;
  634. HandleDidSelectLevelRow(level);
  635. }
  636. catch (Exception e)
  637. {
  638. Logger.Exception("Exception selecting song:", e);
  639. }
  640. }
  641. /// <summary>
  642. /// Switching one-saber modes for example.
  643. /// </summary>
  644. /// <param name="view"></param>
  645. /// <param name="bc"></param>
  646. private void OnDidSelectBeatmapCharacteristic(BeatmapCharacteristicSegmentedControlController view, BeatmapCharacteristicSO bc)
  647. {
  648. Logger.Trace("OnDidSelectBeatmapCharacteristic({0}", bc.name);
  649. _model.CurrentBeatmapCharacteristicSO = bc;
  650. _model.UpdateLevelRecords();
  651. this.RefreshSongList();
  652. }
  653. /// <summary>
  654. /// Handle difficulty level selection.
  655. /// </summary>
  656. private void OnDidSelectDifficultyEvent(BeatmapDifficultySegmentedControlController view, BeatmapDifficulty beatmap)
  657. {
  658. Logger.Trace("OnDidSelectDifficultyEvent({0})", beatmap);
  659. _deleteButton.interactable = (_levelDetailViewController.selectedDifficultyBeatmap.level.levelID.Length >= 32);
  660. this.RefreshScoreSaberData(_levelDetailViewController.selectedDifficultyBeatmap.level);
  661. }
  662. /// <summary>
  663. /// Refresh stats panel.
  664. /// </summary>
  665. /// <param name="level"></param>
  666. private void HandleDidSelectLevelRow(IPreviewBeatmapLevel level)
  667. {
  668. Logger.Trace("HandleDidSelectLevelRow({0})", level);
  669. _deleteButton.interactable = (level.levelID.Length >= 32);
  670. RefreshScoreSaberData(level);
  671. RefreshQuickScrollButtons();
  672. RefreshAddFavoriteButton(level.levelID);
  673. }
  674. /// <summary>
  675. /// Track the current row.
  676. /// </summary>
  677. /// <param name="tableView"></param>
  678. /// <param name="row"></param>
  679. private void HandleDidSelectTableViewRow(TableView tableView, int row)
  680. {
  681. Logger.Trace("HandleDidSelectTableViewRow({0})", row);
  682. _lastRow = row;
  683. }
  684. /// <summary>
  685. /// Pop up a delete dialog.
  686. /// </summary>
  687. private void HandleDeleteSelectedLevel()
  688. {
  689. IBeatmapLevel level = _levelDetailViewController.selectedDifficultyBeatmap.level;
  690. _deleteDialog.Init("Delete song", $"Do you really want to delete \"{ level.songName} {level.songSubName}\"?", "Delete", "Cancel",
  691. (selectedButton) =>
  692. {
  693. _levelSelectionFlowCoordinator.InvokePrivateMethod("DismissViewController", new object[] { _deleteDialog, null, false });
  694. if (selectedButton == 0)
  695. {
  696. try
  697. {
  698. // determine the index we are deleting so we can keep the cursor near the same spot after
  699. List<IPreviewBeatmapLevel> levels = _levelPackLevelsTableView.GetPrivateField<IBeatmapLevelPack>("_pack").beatmapLevelCollection.beatmapLevels.ToList();
  700. int selectedIndex = levels.FindIndex(x => x.levelID == _levelDetailViewController.selectedDifficultyBeatmap.level.levelID);
  701. // we are only deleting custom levels, find the song, delete it
  702. var song = new Song(SongLoader.CustomLevels.First(x => x.levelID == _levelDetailViewController.selectedDifficultyBeatmap.level.levelID));
  703. SongDownloader.Instance.DeleteSong(song);
  704. if (selectedIndex > -1)
  705. {
  706. this._model.RemoveSongFromLevelPack(this._model.CurrentLevelPack, _levelDetailViewController.selectedDifficultyBeatmap.level.levelID);
  707. Logger.Log("Removed {0} from custom song list!", song.songName);
  708. this.UpdateLevelDataModel();
  709. this.RefreshSongList();
  710. TableView listTableView = _levelPackLevelsTableView.GetPrivateField<TableView>("_tableView");
  711. listTableView.ScrollToCellWithIdx(selectedIndex, TableView.ScrollPositionType.Beginning, false);
  712. _levelPackLevelsTableView.SetPrivateField("_selectedRow", selectedIndex);
  713. listTableView.SelectCellWithIdx(selectedIndex, true);
  714. }
  715. }
  716. catch (Exception e)
  717. {
  718. Logger.Error("Unable to delete song! Exception: " + e);
  719. }
  720. }
  721. });
  722. _levelSelectionFlowCoordinator.InvokePrivateMethod("PresentViewController", new object[] { _deleteDialog, null, false });
  723. }
  724. /// <summary>
  725. /// Create MD5 of a file.
  726. /// </summary>
  727. /// <param name="path"></param>
  728. /// <returns></returns>
  729. public static string CreateMD5FromFile(string path)
  730. {
  731. string hash = "";
  732. if (!File.Exists(path)) return null;
  733. using (MD5 md5 = MD5.Create())
  734. {
  735. using (var stream = File.OpenRead(path))
  736. {
  737. byte[] hashBytes = md5.ComputeHash(stream);
  738. StringBuilder sb = new StringBuilder();
  739. foreach (byte hashByte in hashBytes)
  740. {
  741. sb.Append(hashByte.ToString("X2"));
  742. }
  743. hash = sb.ToString();
  744. return hash;
  745. }
  746. }
  747. }
  748. /// <summary>
  749. /// Handle selection of a playlist. Show just the songs in the playlist.
  750. /// </summary>
  751. /// <param name="p"></param>
  752. private void HandleDidSelectPlaylist(Playlist p)
  753. {
  754. if (p != null)
  755. {
  756. Logger.Debug("Showing songs for playlist: {0}", p.playlistTitle);
  757. _model.Settings.filterMode = SongFilterMode.Playlist;
  758. _model.CurrentPlaylist = p;
  759. _model.Settings.Save();
  760. _model.ProcessSongList();
  761. this.RefreshSongList();
  762. this.RefreshSortButtonUI();
  763. }
  764. else
  765. {
  766. Logger.Debug("No playlist selected");
  767. }
  768. }
  769. /// <summary>
  770. /// Display the search keyboard
  771. /// </summary>
  772. void ShowSearchKeyboard()
  773. {
  774. if (_searchViewController == null)
  775. {
  776. _searchViewController = UIBuilder.CreateViewController<SearchKeyboardViewController>("SearchKeyboardViewController");
  777. _searchViewController.searchButtonPressed += SearchViewControllerSearchButtonPressed;
  778. _searchViewController.backButtonPressed += SearchViewControllerbackButtonPressed;
  779. }
  780. Logger.Debug("Presenting search keyboard");
  781. _levelSelectionFlowCoordinator.InvokePrivateMethod("PresentViewController", new object[] { _searchViewController, null, false });
  782. }
  783. /// <summary>
  784. /// Handle back button event from search keyboard.
  785. /// </summary>
  786. private void SearchViewControllerbackButtonPressed()
  787. {
  788. _levelSelectionFlowCoordinator.InvokePrivateMethod("DismissViewController", new object[] { _searchViewController, null, false });
  789. // force disable search filter.
  790. this._model.Settings.filterMode = SongFilterMode.None;
  791. this._model.Settings.Save();
  792. RefreshSortButtonUI();
  793. RefreshQuickScrollButtons();
  794. }
  795. /// <summary>
  796. /// Handle search.
  797. /// </summary>
  798. /// <param name="searchFor"></param>
  799. private void SearchViewControllerSearchButtonPressed(string searchFor)
  800. {
  801. _levelSelectionFlowCoordinator.InvokePrivateMethod("DismissViewController", new object[] { _searchViewController, null, false });
  802. Logger.Debug("Searching for \"{0}\"...", searchFor);
  803. _model.Settings.filterMode = SongFilterMode.Search;
  804. _model.Settings.searchTerms.Insert(0, searchFor);
  805. _model.Settings.Save();
  806. _model.LastSelectedLevelId = null;
  807. _model.ProcessSongList();
  808. RefreshSongList();
  809. RefreshSortButtonUI();
  810. RefreshQuickScrollButtons();
  811. }
  812. /// <summary>
  813. /// Make big jumps in the song list.
  814. /// </summary>
  815. /// <param name="numJumps"></param>
  816. private void JumpSongList(int numJumps, float segmentPercent)
  817. {
  818. int totalSize = _model.SortedSongList.Count;
  819. int segmentSize = (int)(totalSize * segmentPercent);
  820. // Jump at least one scree size.
  821. if (segmentSize < LIST_ITEMS_VISIBLE_AT_ONCE)
  822. {
  823. segmentSize = LIST_ITEMS_VISIBLE_AT_ONCE;
  824. }
  825. TableView tableView = ReflectionUtil.GetPrivateField<TableView>(_levelPackLevelsTableView, "_tableView");
  826. int jumpDirection = Math.Sign(numJumps);
  827. int newRow = _lastRow + (jumpDirection * segmentSize);
  828. if (newRow <= 0)
  829. {
  830. newRow = 0;
  831. }
  832. else if (newRow >= totalSize)
  833. {
  834. newRow = totalSize - 1;
  835. }
  836. Logger.Debug("jumpDirection: {0}, newRow: {1}", jumpDirection, newRow);
  837. _lastRow = newRow;
  838. this.SelectAndScrollToLevel(_levelPackLevelsTableView, _model.SortedSongList[newRow].levelID);
  839. }
  840. /// <summary>
  841. /// Add/Remove song from favorites depending on if it already exists.
  842. /// </summary>
  843. private void ToggleSongInPlaylist()
  844. {
  845. IBeatmapLevel songInfo = _levelDetailViewController.selectedDifficultyBeatmap.level;
  846. if (_model.CurrentEditingPlaylist != null)
  847. {
  848. if (_model.CurrentEditingPlaylistLevelIds.Contains(songInfo.levelID))
  849. {
  850. Logger.Info("Remove {0} from editing playlist", songInfo.songName);
  851. _model.RemoveSongFromEditingPlaylist(songInfo);
  852. if (_model.Settings.filterMode == SongFilterMode.Favorites)
  853. {
  854. this._model.ProcessSongList();
  855. this.RefreshSongList();
  856. }
  857. }
  858. else
  859. {
  860. Logger.Info("Add {0} to editing playlist", songInfo.songName);
  861. _model.AddSongToEditingPlaylist(songInfo);
  862. }
  863. }
  864. RefreshAddFavoriteButton(songInfo.levelID);
  865. _model.Settings.Save();
  866. }
  867. /// <summary>
  868. /// Update GUI elements that show score saber data.
  869. /// </summary>
  870. public void RefreshScoreSaberData(IPreviewBeatmapLevel level)
  871. {
  872. Logger.Trace("RefreshScoreSaberData({0})", level.levelID);
  873. // use controllers level...
  874. if (level == null)
  875. {
  876. level = _levelDetailViewController.selectedDifficultyBeatmap.level;
  877. }
  878. // abort!
  879. if (level == null)
  880. {
  881. Logger.Debug("Aborting RefreshScoreSaberData()");
  882. return;
  883. }
  884. BeatmapDifficulty difficulty = this._levelDifficultyViewController.selectedDifficulty;
  885. string njsText;
  886. string difficultyString = difficulty.ToString();
  887. Logger.Debug(difficultyString);
  888. //Grab NJS for difficulty
  889. //Default to 10 if a standard level
  890. float njs = 0;
  891. if (!_model.LevelIdToCustomSongInfos.ContainsKey(level.levelID))
  892. {
  893. njsText = "OST";
  894. }
  895. else
  896. {
  897. //Grab the matching difficulty level
  898. SongLoaderPlugin.OverrideClasses.CustomLevel customLevel = _model.LevelIdToCustomSongInfos[level.levelID];
  899. CustomSongInfo.DifficultyLevel difficultyLevel = null;
  900. foreach (var diffLevel in customLevel.customSongInfo.difficultyLevels)
  901. {
  902. if (diffLevel.difficulty == difficultyString)
  903. {
  904. difficultyLevel = diffLevel;
  905. break;
  906. }
  907. }
  908. // set njs text
  909. if (difficultyLevel == null || String.IsNullOrEmpty(difficultyLevel.json))
  910. {
  911. njsText = "NA";
  912. }
  913. else
  914. {
  915. njs = GetNoteJump(difficultyLevel.json);
  916. njsText = njs.ToString();
  917. }
  918. }
  919. UIBuilder.SetStatButtonText(_njsStatButton, njsText);
  920. // check if we have score saber data
  921. if (this._model.LevelIdToScoreSaberData != null)
  922. {
  923. // Check for PP
  924. Logger.Debug("Checking if have info for song {0}", level.songName);
  925. if (this._model.LevelIdToScoreSaberData.ContainsKey(level.levelID))
  926. {
  927. Logger.Debug("Checking if have difficulty for song {0} difficulty {1}", level.songName, difficultyString);
  928. ScoreSaberData ppData = this._model.LevelIdToScoreSaberData[level.levelID];
  929. if (ppData.difficultyToSaberDifficulty.ContainsKey(difficultyString))
  930. {
  931. Logger.Debug("Display pp for song.");
  932. float pp = ppData.difficultyToSaberDifficulty[difficultyString].pp;
  933. float star = ppData.difficultyToSaberDifficulty[difficultyString].star;
  934. UIBuilder.SetStatButtonText(_ppStatButton, String.Format("{0:0.#}", pp));
  935. UIBuilder.SetStatButtonText(_starStatButton, String.Format("{0:0.#}", star));
  936. }
  937. else
  938. {
  939. UIBuilder.SetStatButtonText(_ppStatButton, "NA");
  940. UIBuilder.SetStatButtonText(_starStatButton, "NA");
  941. }
  942. }
  943. else
  944. {
  945. UIBuilder.SetStatButtonText(_ppStatButton, "?");
  946. UIBuilder.SetStatButtonText(_starStatButton, "?");
  947. }
  948. }
  949. else
  950. {
  951. Logger.Debug("No ScoreSaberData available... Cannot display pp/star stats...");
  952. }
  953. Logger.Debug("Done refreshing score saber stats.");
  954. }
  955. /// <summary>
  956. /// Update interactive state of the quick scroll buttons.
  957. /// </summary>
  958. private void RefreshQuickScrollButtons()
  959. {
  960. // if you are ever viewing the song list with less than 5 songs the up/down buttons do not exist.
  961. // just try and fetch them and ignore the exception.
  962. if (_tableViewPageUpButton == null)
  963. {
  964. try
  965. {
  966. _tableViewPageUpButton = _tableViewRectTransform.GetComponentsInChildren<Button>().First(x => x.name == "PageUpButton");
  967. (_tableViewPageUpButton.transform as RectTransform).anchoredPosition = new Vector2(0f, -1f);
  968. }
  969. catch (Exception)
  970. {
  971. // We don't care if this fails.
  972. return;
  973. }
  974. }
  975. if (_tableViewPageDownButton == null)
  976. {
  977. try
  978. {
  979. _tableViewPageDownButton = _tableViewRectTransform.GetComponentsInChildren<Button>().First(x => x.name == "PageDownButton");
  980. (_tableViewPageDownButton.transform as RectTransform).anchoredPosition = new Vector2(0f, 1f);
  981. }
  982. catch (Exception)
  983. {
  984. // We don't care if this fails.
  985. return;
  986. }
  987. }
  988. // Refresh the fast scroll buttons
  989. if (_tableViewPageUpButton != null && _pageUpFastButton != null)
  990. {
  991. _pageUpFastButton.interactable = _tableViewPageUpButton.interactable;
  992. _pageUpFastButton.gameObject.SetActive(_tableViewPageUpButton.IsActive());
  993. }
  994. if (_tableViewPageDownButton != null && _pageUpFastButton != null)
  995. {
  996. _pageDownFastButton.interactable = _tableViewPageDownButton.interactable;
  997. _pageDownFastButton.gameObject.SetActive(_tableViewPageDownButton.IsActive());
  998. }
  999. }
  1000. /// <summary>
  1001. /// Helper to quickly refresh add to favorites button
  1002. /// </summary>
  1003. /// <param name="levelId"></param>
  1004. private void RefreshAddFavoriteButton(String levelId)
  1005. {
  1006. if (levelId == null)
  1007. {
  1008. _currentAddFavoriteButtonSprite = null;
  1009. }
  1010. else
  1011. {
  1012. if (_model.CurrentEditingPlaylistLevelIds.Contains(levelId))
  1013. {
  1014. _currentAddFavoriteButtonSprite = _removeFavoriteSprite;
  1015. }
  1016. else
  1017. {
  1018. _currentAddFavoriteButtonSprite = _addFavoriteSprite;
  1019. }
  1020. }
  1021. UIBuilder.SetButtonIcon(_addFavoriteButton, _currentAddFavoriteButtonSprite);
  1022. }
  1023. /// <summary>
  1024. /// Adjust the UI colors.
  1025. /// </summary>
  1026. public void RefreshSortButtonUI()
  1027. {
  1028. // So far all we need to refresh is the sort buttons.
  1029. foreach (SongSortButton sortButton in _sortButtonGroup)
  1030. {
  1031. //UIBuilder.SetButtonTextColor(sortButton.Button, Color.white);
  1032. UIBuilder.SetButtonBorder(sortButton.Button, Color.white);
  1033. if (sortButton.SortMode == _model.Settings.sortMode)
  1034. {
  1035. if (this._model.Settings.invertSortResults)
  1036. {
  1037. //UIBuilder.SetButtonTextColor(sortButton.Button, Color.red);
  1038. UIBuilder.SetButtonBorder(sortButton.Button, Color.red);
  1039. }
  1040. else
  1041. {
  1042. //UIBuilder.SetButtonTextColor(sortButton.Button, Color.green);
  1043. UIBuilder.SetButtonBorder(sortButton.Button, Color.green);
  1044. }
  1045. }
  1046. }
  1047. // refresh filter buttons
  1048. foreach (SongFilterButton filterButton in _filterButtonGroup)
  1049. {
  1050. UIBuilder.SetButtonBorder(filterButton.Button, Color.white);
  1051. if (filterButton.FilterMode == _model.Settings.filterMode)
  1052. {
  1053. UIBuilder.SetButtonBorder(filterButton.Button, Color.green);
  1054. }
  1055. }
  1056. }
  1057. /// <summary>
  1058. /// Try to refresh the song list. Broken for now.
  1059. /// </summary>
  1060. public void RefreshSongList()
  1061. {
  1062. Logger.Info("Refreshing the song list view.");
  1063. try
  1064. {
  1065. // TODO - remove as part of unifying the we handle the song lists
  1066. if (_model.IsCurrentLevelPackPreview)
  1067. {
  1068. return;
  1069. }
  1070. if (_model.SortedSongList == null)
  1071. {
  1072. Logger.Debug("Songs are not sorted yet, nothing to refresh.");
  1073. return;
  1074. }
  1075. var levels = _model.SortedSongList.ToArray();
  1076. Logger.Debug("Checking if TableView is initialized...");
  1077. TableView tableView = ReflectionUtil.GetPrivateField<TableView>(_levelPackLevelsTableView, "_tableView");
  1078. bool tableViewInit = ReflectionUtil.GetPrivateField<bool>(tableView, "_isInitialized");
  1079. if (!tableViewInit)
  1080. {
  1081. Logger.Debug("LevelPackLevelListTableView.TableView is not initialized... nothing to reload...");
  1082. return;
  1083. }
  1084. Logger.Debug("Reloading SongList TableView");
  1085. tableView.ReloadData();
  1086. Logger.Debug("Attempting to scroll to level...");
  1087. String selectedLevelID = null;
  1088. if (_model.LastSelectedLevelId != null)
  1089. {
  1090. selectedLevelID = _model.LastSelectedLevelId;
  1091. Logger.Debug("Scrolling to row for level ID: {0}", selectedLevelID);
  1092. }
  1093. else
  1094. {
  1095. if (levels.Length > 0)
  1096. {
  1097. selectedLevelID = levels.FirstOrDefault().levelID;
  1098. }
  1099. }
  1100. // HACK, seems like if 6 or less items scrolling to row causes the song list to disappear.
  1101. //if (levels.Length > 6 && !String.IsNullOrEmpty(selectedLevelID) && levels.Any(x => x.levelID == selectedLevelID))
  1102. {
  1103. SelectAndScrollToLevel(_levelPackLevelsTableView, selectedLevelID);
  1104. }
  1105. }
  1106. catch (Exception e)
  1107. {
  1108. Logger.Exception("Exception refreshing song list:", e);
  1109. }
  1110. }
  1111. /// <summary>
  1112. /// Acquire the level pack collection.
  1113. /// </summary>
  1114. /// <returns></returns>
  1115. private IBeatmapLevelPackCollection GetLevelPackCollection()
  1116. {
  1117. if (_levelPackViewController == null)
  1118. {
  1119. return null;
  1120. }
  1121. IBeatmapLevelPackCollection levelPackCollection = _levelPackViewController.GetPrivateField<IBeatmapLevelPackCollection>("_levelPackCollection");
  1122. return levelPackCollection;
  1123. }
  1124. /// <summary>
  1125. /// Get the currently selected level pack within the LevelPackLevelViewController hierarchy.
  1126. /// </summary>
  1127. /// <returns></returns>
  1128. private IBeatmapLevelPack GetCurrentSelectedLevelPackFromBeatSaber()
  1129. {
  1130. if (_levelPackLevelsTableView == null)
  1131. {
  1132. return null;
  1133. }
  1134. var pack = _levelPackLevelsTableView.GetPrivateField<IBeatmapLevelPack>("_pack");
  1135. return pack;
  1136. }
  1137. /// <summary>
  1138. /// Get level pack by level pack id.
  1139. /// </summary>
  1140. /// <param name="levelPackId"></param>
  1141. /// <returns></returns>
  1142. private IBeatmapLevelPack GetLevelPackByPackId(String levelPackId)
  1143. {
  1144. IBeatmapLevelPackCollection levelPackCollection = GetLevelPackCollection();
  1145. if (levelPackCollection == null)
  1146. {
  1147. return null;
  1148. }
  1149. IBeatmapLevelPack levelPack = levelPackCollection.beatmapLevelPacks.ToList().First(x => x.packID == levelPackId);
  1150. return levelPack;
  1151. }
  1152. /// <summary>
  1153. /// Get level pack index by level pack id.
  1154. /// </summary>
  1155. /// <param name="levelPackId"></param>
  1156. /// <returns></returns>
  1157. private int GetLevelPackIndexByPackId(String levelPackId)
  1158. {
  1159. IBeatmapLevelPackCollection levelPackCollection = GetLevelPackCollection();
  1160. if (levelPackCollection == null)
  1161. {
  1162. return -1;
  1163. }
  1164. int index = levelPackCollection.beatmapLevelPacks.ToList().FindIndex(x => x.packID == levelPackId);
  1165. return index;
  1166. }
  1167. /// <summary>
  1168. /// Select a level pack.
  1169. /// </summary>
  1170. /// <param name="levelPackId"></param>
  1171. public void SelectLevelPack(String levelPackId)
  1172. {
  1173. Logger.Trace("SelectLevelPack({0})", levelPackId);
  1174. try
  1175. {
  1176. var levelPacks = GetLevelPackCollection();
  1177. var index = GetLevelPackIndexByPackId(levelPackId);
  1178. var pack = GetLevelPackByPackId(levelPackId);
  1179. if (index < 0)
  1180. {
  1181. Logger.Debug("Cannot select level packs yet...");
  1182. return;
  1183. }
  1184. Logger.Info("Selecting level pack index: {0}", pack.packName);
  1185. var tableView = _levelPacksTableView.GetPrivateField<TableView>("_tableView");
  1186. _levelPacksTableView.SelectCellWithIdx(index);
  1187. tableView.SelectCellWithIdx(index, true);
  1188. tableView.ScrollToCellWithIdx(0, TableView.ScrollPositionType.Beginning, false);
  1189. for (int i = 0; i < index; i++)
  1190. {
  1191. tableView.PageScrollDown();
  1192. }
  1193. Logger.Debug("Done selecting level pack!");
  1194. }
  1195. catch (Exception e)
  1196. {
  1197. Logger.Exception(e);
  1198. }
  1199. }
  1200. /// <summary>
  1201. /// Scroll TableView to proper row, fire events.
  1202. /// </summary>
  1203. /// <param name="table"></param>
  1204. /// <param name="levelID"></param>
  1205. private void SelectAndScrollToLevel(LevelPackLevelsTableView table, string levelID)
  1206. {
  1207. Logger.Debug("Scrolling to LevelID: {0}", levelID);
  1208. // Check once per load
  1209. if (!_checkedForTwitchPlugin)
  1210. {
  1211. Logger.Info("Checking for BeatSaber Twitch Integration Plugin...");
  1212. // Try to detect BeatSaber Twitch Integration Plugin
  1213. _detectedTwitchPluginQueue = Resources.FindObjectsOfTypeAll<VRUIViewController>().Any(x => x.name == "RequestInfo");
  1214. Logger.Info("BeatSaber Twitch Integration plugin detected: " + _detectedTwitchPluginQueue);
  1215. _checkedForTwitchPlugin = true;
  1216. }
  1217. // Skip scrolling to level if twitch plugin has queue active.
  1218. if (_detectedTwitchPluginQueue)
  1219. {
  1220. Logger.Debug("Skipping SelectAndScrollToLevel() because we detected Twitch Integrtion Plugin has a Queue active...");
  1221. return;
  1222. }
  1223. // try to find the index and scroll to it
  1224. int selectedIndex = 0;
  1225. List<IPreviewBeatmapLevel> levels = table.GetPrivateField<IBeatmapLevelPack>("_pack").beatmapLevelCollection.beatmapLevels.ToList();
  1226. // check if we have any levels
  1227. if (levels.Count <= 0)
  1228. {
  1229. return;
  1230. }
  1231. // acquire the index or try the last row
  1232. selectedIndex = levels.FindIndex(x => x.levelID == levelID);
  1233. if (selectedIndex < 0)
  1234. {
  1235. // this might look like an off by one error but the _level list we keep is missing the header entry BeatSaber.
  1236. // so the last row is +1 the max index, the count.
  1237. int maxCount = _model.SortedSongList.Count;
  1238. Logger.Debug("Song is not in the level pack, cannot scroll to it... Using last known row");
  1239. selectedIndex = Math.Min(maxCount, _lastRow);
  1240. }
  1241. else
  1242. {
  1243. // the header counts as an index, so if the index came from the level array we have to add 1.
  1244. selectedIndex += 1;
  1245. }
  1246. Logger.Debug("Scrolling level list to idx: {0}", selectedIndex);
  1247. TableView tableView = _levelPackLevelsTableView.GetPrivateField<TableView>("_tableView");
  1248. var scrollPosType = TableView.ScrollPositionType.Center;
  1249. if (selectedIndex == 0)
  1250. {
  1251. scrollPosType = TableView.ScrollPositionType.Beginning;
  1252. }
  1253. if (selectedIndex == _model.SortedSongList.Count - 1)
  1254. {
  1255. scrollPosType = TableView.ScrollPositionType.End;
  1256. }
  1257. _levelPackLevelsTableView.HandleDidSelectRowEvent(tableView, selectedIndex);
  1258. tableView.ScrollToCellWithIdx(selectedIndex, TableView.ScrollPositionType.Beginning, true);
  1259. tableView.SelectCellWithIdx(selectedIndex);
  1260. RefreshQuickScrollButtons();
  1261. _lastRow = selectedIndex;
  1262. }
  1263. /// <summary>
  1264. /// Helper for updating the model (which updates the song list)
  1265. /// </summary>
  1266. public void UpdateLevelDataModel()
  1267. {
  1268. try
  1269. {
  1270. Logger.Trace("UpdateLevelDataModel()");
  1271. // get a current beatmap characteristic...
  1272. if (_model.CurrentBeatmapCharacteristicSO == null && _beatmapCharacteristicSelectionViewController != null)
  1273. {
  1274. _model.CurrentBeatmapCharacteristicSO = _beatmapCharacteristicSelectionViewController.GetPrivateField<BeatmapCharacteristicSO>("_selectedBeatmapCharacteristic");
  1275. }
  1276. _model.UpdateLevelRecords();
  1277. UpdateLevelPackSelection();
  1278. }
  1279. catch (Exception e)
  1280. {
  1281. Logger.Exception("SongBrowser UI crashed trying to update the internal song lists: ", e);
  1282. }
  1283. }
  1284. /// <summary>
  1285. /// Update the level pack model.
  1286. /// </summary>
  1287. public void UpdateLevelPackModel()
  1288. {
  1289. _model.UpdateLevelPackOriginalLists();
  1290. }
  1291. /// <summary>
  1292. /// Logic for fixing BeatSaber's level pack selection bugs.
  1293. /// </summary>
  1294. public void UpdateLevelPackSelection()
  1295. {
  1296. if (_levelPackViewController != null)
  1297. {
  1298. IBeatmapLevelPack currentSelected = GetCurrentSelectedLevelPackFromBeatSaber();
  1299. Logger.Debug("Current selected level pack: {0}", currentSelected);
  1300. if (String.IsNullOrEmpty(_model.Settings.currentLevelPackId))
  1301. {
  1302. if (currentSelected == null)
  1303. {
  1304. Logger.Debug("No level pack selected, acquiring the first available...");
  1305. var levelPackCollection = _levelPackViewController.GetPrivateField<IBeatmapLevelPackCollection>("_levelPackCollection");
  1306. currentSelected = levelPackCollection.beatmapLevelPacks[0];
  1307. }
  1308. this._model.SetCurrentLevelPack(currentSelected);
  1309. }
  1310. else if (currentSelected == null || (currentSelected.packID != _model.Settings.currentLevelId))
  1311. {
  1312. Logger.Debug("Automatically selecting level pack: {0}", _model.Settings.currentLevelPackId);
  1313. // HACK - BeatSaber seems to always go back to OST1 internally.
  1314. // - Lets force it to the last pack id but not have SongBrowser functions fire.
  1315. // Turn off our event processing
  1316. _levelPackViewController.didSelectPackEvent -= _levelPackViewController_didSelectPackEvent;
  1317. _levelPacksTableView.didSelectPackEvent -= _levelPacksTableView_didSelectPackEvent;
  1318. var levelPack = GetLevelPackByPackId(_model.Settings.currentLevelPackId);
  1319. this.SelectLevelPack(_model.Settings.currentLevelPackId);
  1320. this._model.SetCurrentLevelPack(levelPack);
  1321. this._model.ProcessSongList();
  1322. _levelPacksTableView.didSelectPackEvent += _levelPacksTableView_didSelectPackEvent;
  1323. _levelPackViewController.didSelectPackEvent += _levelPackViewController_didSelectPackEvent;
  1324. }
  1325. }
  1326. }
  1327. //Pull njs from a difficulty, based on private function from SongLoader
  1328. public float GetNoteJump(string json)
  1329. {
  1330. float noteJumpSpeed = 0;
  1331. var split = json.Split(':');
  1332. for (var i = 0; i < split.Length; i++)
  1333. {
  1334. if (split[i].Contains("_noteJumpSpeed"))
  1335. {
  1336. noteJumpSpeed = Convert.ToSingle(split[i + 1].Split(',')[0], CultureInfo.InvariantCulture);
  1337. }
  1338. }
  1339. return noteJumpSpeed;
  1340. }
  1341. #if DEBUG
  1342. /// <summary>
  1343. /// Not normally called by the game-engine. Dependent on SongBrowserApplication to call it.
  1344. /// </summary>
  1345. public void LateUpdate()
  1346. {
  1347. CheckDebugUserInput();
  1348. }
  1349. /// <summary>
  1350. /// Map some key presses directly to UI interactions to make testing easier.
  1351. /// </summary>
  1352. private void CheckDebugUserInput()
  1353. {
  1354. try
  1355. {
  1356. if (this._levelPackLevelsViewController != null && this._levelPackLevelsViewController.isActiveAndEnabled)
  1357. {
  1358. bool isShiftKeyDown = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
  1359. if (isShiftKeyDown && Input.GetKeyDown(KeyCode.X))
  1360. {
  1361. this._beatmapCharacteristicSelectionViewController.HandleDifficultySegmentedControlDidSelectCell(null, 1);
  1362. }
  1363. else if (Input.GetKeyDown(KeyCode.X))
  1364. {
  1365. this._beatmapCharacteristicSelectionViewController.HandleDifficultySegmentedControlDidSelectCell(null, 0);
  1366. }
  1367. // back
  1368. if (Input.GetKeyDown(KeyCode.Escape))
  1369. {
  1370. this._levelSelectionNavigationController.GoBackButtonPressed();
  1371. }
  1372. // select current sort mode again (toggle inverting)
  1373. if (isShiftKeyDown && Input.GetKeyDown(KeyCode.BackQuote))
  1374. {
  1375. _sortButtonGroup[_sortButtonLastPushedIndex].Button.onClick.Invoke();
  1376. }
  1377. // cycle sort modes
  1378. else if (Input.GetKeyDown(KeyCode.BackQuote))
  1379. {
  1380. _sortButtonLastPushedIndex = (_sortButtonLastPushedIndex + 1) % _sortButtonGroup.Count;
  1381. _sortButtonGroup[_sortButtonLastPushedIndex].Button.onClick.Invoke();
  1382. }
  1383. // filter favorites
  1384. if (Input.GetKeyDown(KeyCode.F1))
  1385. {
  1386. OnFavoriteFilterButtonClickEvent();
  1387. }
  1388. // filter playlists
  1389. if (Input.GetKeyDown(KeyCode.F2))
  1390. {
  1391. OnPlaylistButtonClickEvent();
  1392. }
  1393. // filter search
  1394. if (Input.GetKeyDown(KeyCode.F3))
  1395. {
  1396. OnSearchButtonClickEvent();
  1397. }
  1398. // delete
  1399. if (Input.GetKeyDown(KeyCode.Delete))
  1400. {
  1401. _deleteButton.onClick.Invoke();
  1402. }
  1403. // c - select difficulty for top song
  1404. if (Input.GetKeyDown(KeyCode.C))
  1405. {
  1406. _levelPacksTableView.SelectCellWithIdx(5);
  1407. _levelPacksTableView.HandleDidSelectColumnEvent(null, 2);
  1408. TableView listTableView = this._levelPackLevelsTableView.GetPrivateField<TableView>("_tableView");
  1409. this._levelPackLevelsTableView.HandleDidSelectRowEvent(listTableView, 2);
  1410. listTableView.ScrollToCellWithIdx(2, TableView.ScrollPositionType.Beginning, false);
  1411. //this._levelDifficultyViewController.HandleDifficultySegmentedControlDidSelectCell(null, 0);
  1412. }
  1413. // v - select difficulty for top song
  1414. if (Input.GetKeyDown(KeyCode.V))
  1415. {
  1416. this.SelectAndScrollToLevel(_levelPackLevelsTableView, _model.SortedSongList[0].levelID);
  1417. this._levelDifficultyViewController.HandleDifficultySegmentedControlDidSelectCell(null, 0);
  1418. }
  1419. // return - start a song
  1420. if (Input.GetKeyDown(KeyCode.Return))
  1421. {
  1422. if (_playButton.isActiveAndEnabled)
  1423. {
  1424. _playButton.onClick.Invoke();
  1425. }
  1426. }
  1427. // change song index
  1428. if (isShiftKeyDown && Input.GetKeyDown(KeyCode.N))
  1429. {
  1430. _pageUpFastButton.onClick.Invoke();
  1431. }
  1432. else if (Input.GetKeyDown(KeyCode.N))
  1433. {
  1434. _lastRow = (_lastRow - 1) != -1 ? (_lastRow - 1) % this._model.SortedSongList.Count : 0;
  1435. this.SelectAndScrollToLevel(_levelPackLevelsTableView, _model.SortedSongList[_lastRow].levelID);
  1436. }
  1437. if (isShiftKeyDown && Input.GetKeyDown(KeyCode.M))
  1438. {
  1439. _pageDownFastButton.onClick.Invoke();
  1440. }
  1441. else if (Input.GetKeyDown(KeyCode.M))
  1442. {
  1443. _lastRow = (_lastRow + 1) % this._model.SortedSongList.Count;
  1444. this.SelectAndScrollToLevel(_levelPackLevelsTableView, _model.SortedSongList[_lastRow].levelID);
  1445. }
  1446. // add to favorites
  1447. if (Input.GetKeyDown(KeyCode.KeypadPlus))
  1448. {
  1449. ToggleSongInPlaylist();
  1450. }
  1451. }
  1452. else if (_deleteDialog != null && _deleteDialog.isInViewControllerHierarchy)
  1453. {
  1454. // accept delete
  1455. if (Input.GetKeyDown(KeyCode.Return))
  1456. {
  1457. _deleteDialog.GetPrivateField<Button>("_okButton").onClick.Invoke();
  1458. }
  1459. if (Input.GetKeyDown(KeyCode.Escape))
  1460. {
  1461. _deleteDialog.GetPrivateField<Button>("_cancelButton").onClick.Invoke();
  1462. }
  1463. }
  1464. else
  1465. {
  1466. if (Input.GetKeyDown(KeyCode.B))
  1467. {
  1468. Logger.Debug("Invoking OK Button");
  1469. VRUIViewController view = Resources.FindObjectsOfTypeAll<VRUIViewController>().First(x => x.name == "StandardLevelResultsViewController");
  1470. view.GetComponentsInChildren<Button>().First(x => x.name == "Ok").onClick.Invoke();
  1471. }
  1472. }
  1473. }
  1474. catch (Exception e)
  1475. {
  1476. Logger.Exception("Debug Input caused Exception: ", e);
  1477. }
  1478. }
  1479. #endif
  1480. }
  1481. }