ScreenConfigConnectionHelper.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using UnityEditor;
  6. using UnityEngine;
  7. namespace TheraBytes.BetterUi.Editor
  8. {
  9. public delegate void ScreenConfigConnectionCallback(string configName, SerializedProperty property);
  10. public static class ScreenConfigConnectionHelper
  11. {
  12. static HashSet<int> foldoutHashes = new HashSet<int>();
  13. public static void DrawSizerGui(string title, SerializedProperty collection, ref SerializedProperty fallback)
  14. {
  15. int indent = EditorGUI.indentLevel;
  16. EditorGUI.indentLevel = 0;
  17. DrawGui(title, collection, ref fallback, null, (name, o) =>
  18. {
  19. var obj = o.GetValue<ScreenDependentSize>();
  20. obj.DynamicInitialization();
  21. });
  22. EditorGUI.indentLevel = indent;
  23. }
  24. public static void DrawGui(string title, SerializedProperty collection, ref SerializedProperty fallback,
  25. ScreenConfigConnectionCallback drawContent = null,
  26. ScreenConfigConnectionCallback newElementInitCallback = null,
  27. ScreenConfigConnectionCallback elementDeleteCallback = null)
  28. {
  29. int baseHash = title.GetHashCode();
  30. GUILayout.Space(2);
  31. Rect bgRect = EditorGUILayout.BeginVertical("box");
  32. EditorGUILayout.LabelField(title, EditorStyles.boldLabel);
  33. var configs = collection.GetValue<ISizeConfigCollection>();
  34. if (configs.IsDirty)
  35. {
  36. configs.Sort();
  37. return;
  38. }
  39. string currentConfig = configs.GetCurrentConfigName();
  40. string currentScreen = ResolutionMonitor.CurrentScreenConfiguration?.Name;
  41. SerializedProperty items = collection.FindPropertyRelative("items");
  42. // LIST
  43. bool configFound = currentConfig != null;
  44. HashSet<string> names = new HashSet<string>();
  45. for (int i = 0; i < items.arraySize; i++)
  46. {
  47. SerializedProperty item = items.GetArrayElementAtIndex(i);
  48. var nameProp = item.FindPropertyRelative("screenConfigName");
  49. string name = "?";
  50. if (nameProp != null)
  51. {
  52. name = nameProp.stringValue;
  53. names.Add(name);
  54. }
  55. else
  56. {
  57. Debug.LogError("no serialized property named 'screenConfigName' found.");
  58. }
  59. bool foldout = false;
  60. // DELETE
  61. DrawItemHeader(name, baseHash, false, currentConfig, out foldout, () =>
  62. {
  63. if (elementDeleteCallback != null)
  64. elementDeleteCallback(name, item);
  65. items.DeleteArrayElementAtIndex(i);
  66. i--;
  67. foldout = false;
  68. });
  69. if (foldout)
  70. {
  71. if (drawContent != null)
  72. drawContent(name, item);
  73. else
  74. EditorGUILayout.PropertyField(item);
  75. }
  76. }
  77. // FALLBACK
  78. bool fallbackFoldout;
  79. string fallbackName = string.Format("{0} (Fallback)", ResolutionMonitor.Instance.FallbackName);
  80. DrawItemHeader(fallbackName, baseHash, !configFound, currentConfig ?? fallbackName, out fallbackFoldout);
  81. if (fallbackFoldout)
  82. {
  83. if (drawContent != null)
  84. drawContent(fallbackName, fallback);
  85. else
  86. EditorGUILayout.PropertyField(fallback);
  87. }
  88. // ADD NEW
  89. string[] options = ResolutionMonitor.Instance.OptimizedScreens
  90. .Where(o => !(names.Contains(o.Name)))
  91. .Select(o => o.Name)
  92. .ToArray();
  93. if (options.Length > 0)
  94. {
  95. EditorGUILayout.BeginHorizontal();
  96. GUILayout.FlexibleSpace();
  97. // Quick Action: Add current config
  98. if (currentConfig != currentScreen)
  99. {
  100. GUIStyle style = "minibutton";
  101. var content = EditorGUIUtility.IconContent("OL Plus");
  102. content.text = $" {currentScreen}";
  103. var size = style.CalcSize(content);
  104. Rect rect = new Rect(bgRect.xMax - size.x - 20, bgRect.y + 3, size.x, 16);
  105. if (GUI.Button(rect, content, "minibutton"))
  106. {
  107. AddSizerToList(currentScreen, ref fallback, items, newElementInitCallback);
  108. configs.MarkDirty();
  109. }
  110. }
  111. //int idx = EditorGUILayout.Popup(-1, options, "OL Plus", GUILayout.Width(20));
  112. Rect r = new Rect(bgRect.x + bgRect.width - 20, bgRect.y + 3, 20, 20);
  113. int idx = EditorGUI.Popup(r, -1, options, "OL Plus");
  114. if (idx != -1)
  115. {
  116. string name = options[idx];
  117. idx = -1;
  118. AddSizerToList(name, ref fallback, items, newElementInitCallback);
  119. configs.MarkDirty();
  120. }
  121. EditorGUILayout.EndHorizontal();
  122. }
  123. fallback.serializedObject.ApplyModifiedProperties();
  124. // immediately handle the ordering of the configs and update the serialized object
  125. // otherwise this would happen one frame delayed and an error would popup in the console.
  126. if(configs.IsDirty)
  127. {
  128. configs.Sort();
  129. fallback.serializedObject.Update();
  130. }
  131. EditorGUILayout.EndVertical();
  132. }
  133. public static void AddSizerToList(string configName, ref SerializedProperty fallback, SerializedProperty items,
  134. ScreenConfigConnectionCallback callback = null)
  135. {
  136. string fallbackPath = fallback.propertyPath;
  137. items.arraySize += 1;
  138. var newElement = items.GetArrayElementAtIndex(items.arraySize - 1);
  139. SerializedPropertyUtil.Copy(fallback, newElement);
  140. // after the copy action the property pointer points somewhere.
  141. // so, point to the right prop again.
  142. newElement = items.GetArrayElementAtIndex(items.arraySize - 1);
  143. fallback = fallback.serializedObject.FindProperty(fallbackPath);
  144. if (callback != null)
  145. {
  146. callback(configName, newElement);
  147. }
  148. var prop = newElement.FindPropertyRelative("screenConfigName");
  149. if (prop != null)
  150. prop.stringValue = configName;
  151. else
  152. Debug.LogError("no serialized property named 'screenConfigName' found.");
  153. }
  154. private static void DrawItemHeader(string configName, int baseHash, bool setCurrent, string currentConfigName, out bool foldout, Action deleteCallback = null)
  155. {
  156. int hash = GetHash(baseHash, configName);
  157. bool isCurrentConfig = configName == currentConfigName;
  158. bool isSimulatedConfig = (ResolutionMonitor.SimulatedScreenConfig != null) && (configName == ResolutionMonitor.SimulatedScreenConfig.Name);
  159. bool exists = ResolutionMonitor.Instance.FallbackName + " (Fallback)" == configName
  160. || ResolutionMonitor.Instance.OptimizedScreens.Any(o => o.Name == configName);
  161. foldout = foldoutHashes.Contains(hash) || isCurrentConfig;
  162. EditorGUILayout.BeginHorizontal();
  163. string title = string.Format("{0} {1}{2} {3}{4}",
  164. (foldout) ? "▼" : "►",
  165. (isCurrentConfig) ? "♦" : "◊",
  166. (isSimulatedConfig) ? " ⃰" : " ",
  167. configName,
  168. (exists) ? "" : " (‼ not found ‼)");
  169. if (GUILayout.Button(title, "TextField", GUILayout.ExpandWidth(true)))//(foldout) ? "MiniPopup" : "MiniPullDown"))
  170. {
  171. if (!(isCurrentConfig) && !(foldoutHashes.Remove(hash)))
  172. {
  173. foldoutHashes.Add(hash);
  174. foldout = true;
  175. }
  176. }
  177. GUILayout.Space(-6);
  178. if (deleteCallback != null)
  179. {
  180. if (GUILayout.Button("X", "SearchCancelButton", GUILayout.Width(20)))//"MiniButton", GUILayout.Width(20))))
  181. {
  182. if (EditorUtility.DisplayDialog("Delete?",
  183. string.Format("Do you really want to delete the configuration '{0}'?", configName),
  184. "Delete", "Cancel"))
  185. {
  186. deleteCallback();
  187. }
  188. }
  189. }
  190. else
  191. {
  192. GUILayout.Box("", "SearchCancelButtonEmpty", GUILayout.Width(20));
  193. }
  194. EditorGUILayout.EndHorizontal();
  195. }
  196. private static int GetHash(int baseHash, string configName)
  197. {
  198. return baseHash ^ configName.GetHashCode();
  199. }
  200. }
  201. }