ResolutionPicker.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. using UnityEngine;
  2. using System.Collections;
  3. using UnityEditor;
  4. using System.Reflection;
  5. using System;
  6. using System.Linq;
  7. using System.Collections.Generic;
  8. namespace TheraBytes.BetterUi.Editor
  9. {
  10. public class ResolutionPicker : EditorWindow
  11. {
  12. internal enum TextDisplayMode
  13. {
  14. Size,
  15. Name,
  16. Both,
  17. }
  18. internal class GameViewSize
  19. {
  20. internal int width;
  21. internal int height;
  22. internal string baseText;
  23. internal string displayText;
  24. internal bool isAspectRatio;
  25. internal int index;
  26. internal bool isCustom;
  27. internal string sizeText { get { return (isAspectRatio) ? string.Format("{0}:{1}", width, height) : string.Format("{0}x{1}", width, height); } }
  28. public object ToInternalObject()
  29. {
  30. Assembly ass = typeof(EditorApplication).Assembly;
  31. Type t = ass.GetType("UnityEditor.GameViewSize");
  32. Type sizeType = ass.GetType("UnityEditor.GameViewSizeType");
  33. var constructor = t.GetConstructor(new Type[] { sizeType, typeof(int), typeof(int), typeof(string) });
  34. return constructor.Invoke(new object[] { (isAspectRatio) ? 0 : 1, width, height, baseText });
  35. }
  36. }
  37. static Assembly assembly;
  38. static EditorWindow gameView;
  39. [MenuItem("Tools/Better UI/Pick Resolution", false, 90)]
  40. public static void ShowWindow()
  41. {
  42. assembly = typeof(EditorWindow).Assembly;
  43. var win = EditorWindow.GetWindow<ResolutionPicker>("Pick Resolution") as ResolutionPicker;
  44. win.minSize = new Vector2(20, 40);
  45. win.RefreshSizes();
  46. }
  47. Type gameSizeType;
  48. PropertyInfo selectedIndex;
  49. List<GameViewSize> sizes = new List<GameViewSize>();
  50. StoredEditorBool
  51. displayPortrait = new StoredEditorBool("resolutionPicker.displayPortrait", true),
  52. displayLandscape = new StoredEditorBool("resolutionPicker.displayLandscape", true),
  53. displayFree = new StoredEditorBool("resolutionPicker.displayFree", true),
  54. displayBuiltin = new StoredEditorBool("resolutionPicker.displayBuiltin", true),
  55. displayCustom = new StoredEditorBool("resolutionPicker.displayCustom", true);
  56. StoredEditorBool
  57. showOrientationHint = new StoredEditorBool("resolutionPicker.showOrientationHint", true),
  58. markCustom = new StoredEditorBool("resolutionPicker.markCustom", true);
  59. StoredEditorBool useBigButtons = new StoredEditorBool("resolutionPicker.bigButtons", false);
  60. StoredEditorBool useVerticalLayout = new StoredEditorBool("resolutionPicker.verticalLayout", true);
  61. StoredEditorInt textMode = new StoredEditorInt("reslutionPicker.textMode", (int)TextDisplayMode.Both);
  62. StoredEditorBool
  63. displayScreenConfigs = new StoredEditorBool("resolutionPicker.displayScreenConfigs", true),
  64. applyScreenConfigResolution = new StoredEditorBool("resolutionPicker.applyScreenConfigResolution", true);
  65. int builtinCount;
  66. void RefreshSizes()
  67. {
  68. assembly = typeof(EditorWindow).Assembly;
  69. Type gameViewType = Type.GetType("UnityEditor.GameView,UnityEditor");
  70. gameView = Resources.FindObjectsOfTypeAll(gameViewType)
  71. .FirstOrDefault() as UnityEditor.EditorWindow;
  72. if (gameView == null)
  73. return;
  74. Type gameSizesType = Type.GetType("UnityEditor.GameViewSizes," + assembly);
  75. Type gameSizeGroupType = Type.GetType("UnityEditor.GameViewSizeGroup," + assembly);
  76. gameSizeType = Type.GetType("UnityEditor.GameViewSize," + assembly);
  77. object gameSizesInstance = gameSizesType.BaseType.GetProperty("instance", BindingFlags.Public | BindingFlags.Static).GetValue(null, null);
  78. object gameViewGroup = gameSizesType.GetProperty("currentGroup").GetValue(gameSizesInstance, null);
  79. int count = (int)gameSizeGroupType.InvokeMember("GetTotalCount", BindingFlags.InvokeMethod, null, gameViewGroup, null);
  80. builtinCount = (int)gameSizeGroupType.InvokeMember("GetBuiltinCount", BindingFlags.InvokeMethod, null, gameViewGroup, null);
  81. sizes.Clear();
  82. for (int i = 0; i < count; i++)
  83. {
  84. object gameSize = gameSizeGroupType.InvokeMember("GetGameViewSize", BindingFlags.InvokeMethod, null, gameViewGroup, new object[] { i });
  85. bool isCustom = (i >= builtinCount);
  86. AddSize(gameSizeType, gameSize, i, isCustom);
  87. }
  88. BindingFlags bindingFlags =
  89. #if UNITY_2022_1_OR_NEWER
  90. BindingFlags.Instance | BindingFlags.Public;
  91. #else
  92. BindingFlags.Instance | BindingFlags.NonPublic;
  93. #endif
  94. selectedIndex = gameView.GetType().GetProperty("selectedSizeIndex", bindingFlags);
  95. }
  96. void OnGUI()
  97. {
  98. if (gameView == null || gameSizeType == null)
  99. {
  100. RefreshSizes();
  101. }
  102. if (gameView == null) // still null?
  103. {
  104. EditorGUILayout.BeginHorizontal();
  105. EditorGUILayout.HelpBox("Please open a Game window.", MessageType.Warning);
  106. if (GUILayout.Button("Open Game View Window", GUILayout.Height(38)))
  107. {
  108. gameView = EditorWindow.GetWindow(Type.GetType("UnityEditor.GameView," + assembly));
  109. }
  110. EditorGUILayout.EndHorizontal();
  111. return;
  112. }
  113. Begin(mainSection: false);
  114. // Resolutions
  115. Begin(mainSection: true);
  116. DrawToolStrip(); // settings
  117. EditorGUILayout.Separator();
  118. var style = (useBigButtons) ? GUI.skin.button : EditorStyles.toolbarButton;
  119. var prevAlign = style.alignment;
  120. style.alignment = TextAnchor.MiddleLeft;
  121. style.fontSize = 10;
  122. int currentIndex = (int)selectedIndex.GetValue(gameView, null);
  123. for (int i = 0; i < sizes.Count; i++)
  124. {
  125. if ((displayFree && i == 1) || (displayBuiltin && i == builtinCount))
  126. {
  127. EditorGUILayout.Separator();
  128. }
  129. var size = sizes[i];
  130. if (!(AllowedToShow(size)))
  131. continue;
  132. bool isOptimizedRes = (ResolutionMonitor.IsOptimizedResolution(size.width, size.height));
  133. bool isSelected = currentIndex == size.index;
  134. style.fontStyle = (isOptimizedRes)
  135. ? ((isSelected) ? FontStyle.BoldAndItalic : FontStyle.Italic)
  136. : ((isSelected) ? FontStyle.Bold : FontStyle.Normal);
  137. if (GUILayout.Button(GetText(size), style))
  138. {
  139. SetResolution(size);
  140. }
  141. }
  142. GUILayout.FlexibleSpace();
  143. style.fontStyle = FontStyle.Normal;
  144. End(mainSection: true);
  145. // Screen Configs
  146. if (this.displayScreenConfigs)
  147. {
  148. Begin(mainSection: true);
  149. string title = (useVerticalLayout) ? "♦ Screen Configurations" : "♦";
  150. if (GUILayout.Button(title, EditorStyles.toolbarButton, GUILayout.MinWidth(25)))
  151. {
  152. Selection.activeObject = ResolutionMonitor.Instance;
  153. }
  154. EditorGUILayout.Space();
  155. Action<ScreenTypeConditions, int, int> applyScreenConfig = (config, width, height) =>
  156. {
  157. if (config != null && ResolutionMonitor.SimulatedScreenConfig == config)
  158. {
  159. ResolutionMonitor.SimulatedScreenConfig = null;
  160. }
  161. else
  162. {
  163. ResolutionMonitor.SimulatedScreenConfig = config;
  164. if (this.applyScreenConfigResolution)
  165. {
  166. RefreshSizes();
  167. GameViewSize gvs = sizes.FirstOrDefault((o) =>
  168. o.width == width && o.height == height);
  169. if (gvs == null)
  170. {
  171. string name = (config != null) ? config.Name : ResolutionMonitor.Instance.FallbackName;
  172. AddSizeToUnity(name, width, height);
  173. gvs = sizes.FirstOrDefault((o) =>
  174. o.width == width && o.height == height);
  175. }
  176. if (gvs != null)
  177. {
  178. SetResolution(gvs);
  179. }
  180. }
  181. }
  182. };
  183. if (GUILayout.Button(ResolutionMonitor.Instance.FallbackName + " (Fallback)", style))
  184. {
  185. var resolution = ResolutionMonitor.OptimizedResolutionFallback;
  186. applyScreenConfig(null, (int)resolution.x, (int)resolution.y);
  187. }
  188. EditorGUILayout.Space();
  189. foreach (var config in ResolutionMonitor.Instance.OptimizedScreens)
  190. {
  191. if (GUILayout.Button(ResolutionMonitorEditor.GetButtonText(config), style))
  192. {
  193. applyScreenConfig(config, config.OptimizedWidth, config.OptimizedHeight);
  194. }
  195. }
  196. GUILayout.FlexibleSpace();
  197. End(mainSection: true);
  198. }
  199. End(mainSection: false);
  200. style.alignment = prevAlign;
  201. }
  202. private void AddSizeToUnity(string name, int width, int height)
  203. {
  204. RefreshSizes();
  205. try
  206. {
  207. GameViewSize size = new GameViewSize()
  208. {
  209. baseText = name,
  210. displayText = name,
  211. width = width,
  212. height = height,
  213. index = sizes.Count,
  214. isCustom = true,
  215. isAspectRatio = false,
  216. };
  217. Assembly ass = typeof(EditorApplication).Assembly;
  218. Type gameViewSizesType = ass.GetType("UnityEditor.GameViewSizes");
  219. var singleType = typeof(ScriptableSingleton<>).MakeGenericType(gameViewSizesType);
  220. PropertyInfo gameViewSizesInfo = singleType.GetProperty("instance");
  221. object gameViewSizes = gameViewSizesInfo.GetValue(null, new object[] { });
  222. PropertyInfo gameViewSizeGroupInfo = gameViewSizesType.GetMember("currentGroup")[0] as PropertyInfo;
  223. object gameViewSizeGroup = gameViewSizeGroupInfo.GetValue(gameViewSizes, new object[] { });
  224. MethodInfo addSizeMethod = gameViewSizeGroup.GetType().GetMethod("AddCustomSize");
  225. addSizeMethod.Invoke(gameViewSizeGroup, new object[] { size.ToInternalObject() });
  226. MethodInfo saveToHddMethod = gameViewSizesType.GetMethod("SaveToHDD");
  227. saveToHddMethod.Invoke(gameViewSizes, new object[] { });
  228. RefreshSizes();
  229. }
  230. catch (Exception ex)
  231. {
  232. Debug.LogErrorFormat("Couldn't create resolution: {0}", ex);
  233. }
  234. }
  235. void SetResolution(GameViewSize size)
  236. {
  237. var type = gameView.GetType();
  238. selectedIndex.SetValue(gameView, size.index, null);
  239. if (ResolutionMonitor.IsZoomPossible())
  240. {
  241. var method = type.GetMethod("UpdateZoomAreaAndParent", BindingFlags.Instance | BindingFlags.NonPublic);
  242. method.Invoke(gameView, null);
  243. }
  244. var resizedNotifyMethod = type.GetMethod("OnResized", BindingFlags.Instance | BindingFlags.NonPublic);
  245. resizedNotifyMethod.Invoke(gameView, null);
  246. SceneView.RepaintAll();
  247. }
  248. void Begin(bool mainSection)
  249. {
  250. if (useVerticalLayout == mainSection)
  251. EditorGUILayout.BeginVertical();
  252. else
  253. EditorGUILayout.BeginHorizontal();
  254. }
  255. void End(bool mainSection)
  256. {
  257. if (useVerticalLayout == mainSection)
  258. EditorGUILayout.EndVertical();
  259. else
  260. EditorGUILayout.EndHorizontal();
  261. }
  262. bool AllowedToShow(GameViewSize size)
  263. {
  264. // special treatment for free aspect
  265. if (size.width == 0 && size.height == 0)
  266. return displayFree;
  267. bool allow = (size.width >= size.height && displayLandscape)
  268. || (size.width < size.height && displayPortrait);
  269. allow = allow && ((size.isCustom && displayCustom)
  270. || (!(size.isCustom) && displayBuiltin));
  271. return allow;
  272. }
  273. string GetText(GameViewSize size)
  274. {
  275. string result = "";
  276. if (showOrientationHint)
  277. {
  278. if (size.width > size.height)
  279. {
  280. result += "▬ ";
  281. }
  282. else if (size.width < size.height)
  283. {
  284. result += " ▌";
  285. }
  286. }
  287. if (markCustom && size.isCustom)
  288. {
  289. result += "☺ ";
  290. }
  291. switch ((TextDisplayMode)textMode.Value)
  292. {
  293. case TextDisplayMode.Size: result += (size.width == 0 && size.height == 0) ? "X:Y" : size.sizeText; break;
  294. case TextDisplayMode.Name: result += (string.IsNullOrEmpty(size.baseText)) ? size.sizeText : size.baseText; break;
  295. case TextDisplayMode.Both: result += size.displayText; break;
  296. default:
  297. throw new ArgumentException();
  298. }
  299. return result;
  300. }
  301. void DrawToolStrip()
  302. {
  303. string title = (useVerticalLayout) ? "♠ Settings" : "♠";
  304. if (GUILayout.Button(title, EditorStyles.toolbarDropDown, GUILayout.MinWidth(25)))
  305. {
  306. GenericMenu toolsMenu = new GenericMenu();
  307. toolsMenu.AddSeparator("");
  308. toolsMenu.AddDisabledItem(new GUIContent("♥ Resolution Filters"));
  309. toolsMenu.AddSeparator("");
  310. toolsMenu.AddItem(new GUIContent("Free Aspect"), displayFree, DisplayFree);
  311. toolsMenu.AddItem(new GUIContent("Portrait ( ▌ )"), displayPortrait, DisplayPortrait);
  312. toolsMenu.AddItem(new GUIContent("Landscape ( ▬ )"), displayLandscape, DisplayLandscape);
  313. toolsMenu.AddSeparator("");
  314. toolsMenu.AddItem(new GUIContent("Builtin"), displayBuiltin, DisplayBuiltin);
  315. toolsMenu.AddItem(new GUIContent("Custom ( ☺ )"), displayCustom, DisplayCustom);
  316. toolsMenu.AddSeparator("");
  317. toolsMenu.AddDisabledItem(new GUIContent("♦ Screen Configurations"));
  318. toolsMenu.AddSeparator("");
  319. toolsMenu.AddItem(new GUIContent("Show"), displayScreenConfigs, DisplayScreenConfigs);
  320. toolsMenu.AddItem(new GUIContent("Apply Resolution"), applyScreenConfigResolution, ApplyScreenConfigResolution);
  321. toolsMenu.AddSeparator("");
  322. toolsMenu.AddDisabledItem(new GUIContent("♣ Options"));
  323. toolsMenu.AddSeparator("");
  324. toolsMenu.AddItem(new GUIContent("Text Options/Name and Size"), textMode == (int)TextDisplayMode.Both, TextModeBoth);
  325. toolsMenu.AddItem(new GUIContent("Text Options/Name"), textMode == (int)TextDisplayMode.Name, TextModeName);
  326. toolsMenu.AddItem(new GUIContent("Text Options/Size"), textMode == (int)TextDisplayMode.Size, TextModeSize);
  327. toolsMenu.AddSeparator("Text Options/");
  328. toolsMenu.AddItem(new GUIContent("Text Options/Orientation Hint"), showOrientationHint, ShowOrientationHint);
  329. toolsMenu.AddSeparator("Text Options/");
  330. toolsMenu.AddItem(new GUIContent("Text Options/Mark Custom"), markCustom, MarkCustom);
  331. toolsMenu.AddItem(new GUIContent("Style/Big"), useBigButtons, UseBigButtons);
  332. toolsMenu.AddItem(new GUIContent("Style/Small"), !useBigButtons, UseSmallButtons);
  333. toolsMenu.AddItem(new GUIContent("Layout/Vertical"), useVerticalLayout, UseVerticalLayout);
  334. toolsMenu.AddItem(new GUIContent("Layout/Horizontal"), !useVerticalLayout, UseHorizontalLayout);
  335. toolsMenu.AddSeparator("");
  336. toolsMenu.AddItem(new GUIContent("Refresh List"), false, RefreshSizes);
  337. toolsMenu.DropDown(new Rect(0, 0, 0, 16));
  338. EditorGUIUtility.ExitGUI();
  339. }
  340. }
  341. void DisplayPortrait() { this.displayPortrait.Value = !(this.displayPortrait); }
  342. void DisplayLandscape() { this.displayLandscape.Value = !(this.displayLandscape); }
  343. void DisplayFree() { this.displayFree.Value = !(this.displayFree); }
  344. void DisplayBuiltin() { this.displayBuiltin.Value = !(this.displayBuiltin); }
  345. void DisplayCustom() { this.displayCustom.Value = !(this.displayCustom); }
  346. void ShowOrientationHint() { showOrientationHint.Value = !(this.showOrientationHint); }
  347. void MarkCustom() { markCustom.Value = !(this.markCustom); }
  348. void TextModeBoth() { this.textMode.Value = (int)TextDisplayMode.Both; }
  349. void TextModeSize() { this.textMode.Value = (int)TextDisplayMode.Size; }
  350. void TextModeName() { this.textMode.Value = (int)TextDisplayMode.Name; }
  351. void UseBigButtons() { this.useBigButtons.Value = true; }
  352. void UseSmallButtons() { this.useBigButtons.Value = false; }
  353. void UseVerticalLayout() { this.useVerticalLayout.Value = true; }
  354. void UseHorizontalLayout() { this.useVerticalLayout.Value = false; }
  355. void DisplayScreenConfigs() { this.displayScreenConfigs.Value = !(this.displayScreenConfigs); }
  356. void ApplyScreenConfigResolution() { this.applyScreenConfigResolution.Value = !(this.applyScreenConfigResolution); }
  357. void AddSize(Type gameSizeType, object gameSize, int index, bool isCustom)
  358. {
  359. GameViewSize item = new GameViewSize();
  360. item.index = index;
  361. item.isCustom = isCustom;
  362. item.width = (int)gameSizeType.GetProperty("width").GetValue(gameSize, null);
  363. item.height = (int)gameSizeType.GetProperty("height").GetValue(gameSize, null);
  364. item.baseText = (string)gameSizeType.GetProperty("baseText").GetValue(gameSize, null);
  365. item.displayText = (string)gameSizeType.GetProperty("displayText").GetValue(gameSize, null);
  366. item.isAspectRatio = ((int)gameSizeType.GetProperty("sizeType").GetValue(gameSize, null)) == 0;
  367. sizes.Add(item);
  368. }
  369. }
  370. }