HostingServicesWindow.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.AddressableAssets.HostingServices;
  5. using UnityEditor.AddressableAssets.Settings;
  6. using UnityEditor.IMGUI.Controls;
  7. using UnityEngine;
  8. using UnityEngine.Serialization;
  9. namespace UnityEditor.AddressableAssets.GUI
  10. {
  11. using Object = UnityEngine.Object;
  12. /// <summary>
  13. /// Configuration GUI for <see cref="T:UnityEditor.AddressableAssets.HostingServices.HostingServicesManager" />
  14. /// </summary>
  15. public class HostingServicesWindow : EditorWindow, ISerializationCallbackReceiver, ILogHandler
  16. {
  17. const float k_DefaultVerticalSplitterRatio = 0.67f;
  18. const float k_DefaultHorizontalSplitterRatio = 0.33f;
  19. const int k_SplitterThickness = 2;
  20. const int k_ToolbarHeight = 20;
  21. const int k_ItemRectPadding = 15;
  22. const int k_LogRectPadding = 5;
  23. GUIStyle m_ItemRectPadding;
  24. GUIStyle m_LogRectPadding;
  25. [FormerlySerializedAs("m_logText")]
  26. [SerializeField]
  27. string m_LogText;
  28. [FormerlySerializedAs("m_logScrollPos")]
  29. [SerializeField]
  30. Vector2 m_LogScrollPos;
  31. [FormerlySerializedAs("m_servicesScrollPos")]
  32. [SerializeField]
  33. Vector2 m_ServicesScrollPos;
  34. [FormerlySerializedAs("m_splitterRatio")]
  35. [SerializeField]
  36. float m_VerticalSplitterRatio = k_DefaultVerticalSplitterRatio;
  37. float m_HorizontalSplitterRatio = k_DefaultHorizontalSplitterRatio;
  38. [FormerlySerializedAs("m_settings")]
  39. [SerializeField]
  40. AddressableAssetSettings m_Settings;
  41. ILogger m_Logger;
  42. bool m_NewLogContent;
  43. bool m_IsResizingVerticalSplitter;
  44. bool m_IsResizingHorizontalSplitter;
  45. bool m_Reload = false;
  46. int m_serviceIndex = -1;
  47. /// <summary>
  48. /// Returns the index of the currently selected hosting service.
  49. /// </summary>
  50. public int ServiceIndex { get { return m_serviceIndex; } set { m_serviceIndex = value; } }
  51. readonly Dictionary<object, HostingServicesProfileVarsTreeView> m_ProfileVarTables =
  52. new Dictionary<object, HostingServicesProfileVarsTreeView>();
  53. readonly Dictionary<object, Dictionary<string, string>> m_TablePrevData =
  54. new Dictionary<object, Dictionary<string, string>>();
  55. private readonly Dictionary<object, Dictionary<string, string>> m_TablePrevManagerVariables =
  56. new Dictionary<object, Dictionary<string, string>>();
  57. readonly List<IHostingService> m_RemovalQueue = new List<IHostingService>();
  58. HostingServicesProfileVarsTreeView m_GlobalProfileVarTable;
  59. HostingServicesListTreeView m_ServicesList;
  60. Type[] m_ServiceTypes;
  61. Type[] ServiceTypes
  62. {
  63. get
  64. {
  65. if (m_ServiceTypes == null || m_ServiceTypes.Length == 0)
  66. PopulateServiceTypes();
  67. return m_ServiceTypes;
  68. }
  69. }
  70. string[] m_ServiceTypeNames;
  71. /// <summary>
  72. /// Show the <see cref="HostingServicesWindow"/>, initialized with the given <see cref="AddressableAssetSettings"/>
  73. /// </summary>
  74. /// <param name="settings"></param>
  75. public void Show(AddressableAssetSettings settings)
  76. {
  77. Initialize(settings);
  78. Show();
  79. }
  80. void Initialize(AddressableAssetSettings settings)
  81. {
  82. if (m_Logger == null)
  83. m_Logger = new Logger(this);
  84. if (m_Settings == null)
  85. m_Settings = settings;
  86. if (m_Settings != null)
  87. m_Settings.HostingServicesManager.Logger = m_Logger;
  88. }
  89. void OnEnable()
  90. {
  91. PopulateServiceTypes();
  92. m_ItemRectPadding = new GUIStyle();
  93. m_ItemRectPadding.padding = new RectOffset(k_ItemRectPadding, k_ItemRectPadding, k_ItemRectPadding, k_ItemRectPadding);
  94. m_LogRectPadding = new GUIStyle();
  95. m_LogRectPadding.padding = new RectOffset(k_LogRectPadding, k_LogRectPadding, k_LogRectPadding, k_LogRectPadding);
  96. }
  97. [MenuItem("Window/Asset Management/Addressables/Hosting", priority = 2052)]
  98. static void InitializeWithDefaultSettings()
  99. {
  100. var defaultSettings = AddressableAssetSettingsDefaultObject.Settings;
  101. if (defaultSettings == null)
  102. {
  103. EditorUtility.DisplayDialog("Error", "Attempting to open Addressables Hosting window, but no Addressables Settings file exists. \n\nOpen 'Window/Asset Management/Addressables/Groups' for more info.", "Ok");
  104. return;
  105. }
  106. GetWindow<HostingServicesWindow>().Show(defaultSettings);
  107. }
  108. void PopulateServiceTypes()
  109. {
  110. if (m_Settings == null) return;
  111. m_ServiceTypes = m_Settings.HostingServicesManager.RegisteredServiceTypes;
  112. m_ServiceTypeNames = new string[m_ServiceTypes.Length];
  113. for (var i = 0; i < m_ServiceTypes.Length; i++)
  114. {
  115. m_ServiceTypeNames[i] = m_ServiceTypes[i].Name;
  116. }
  117. }
  118. void Awake()
  119. {
  120. titleContent = new GUIContent("Addressables Hosting");
  121. Initialize(m_Settings);
  122. }
  123. internal static void DrawOutline(Rect rect, float size)
  124. {
  125. Color color = new Color(0.6f, 0.6f, 0.6f, 1.333f);
  126. if (EditorGUIUtility.isProSkin)
  127. {
  128. color.r = 0.12f;
  129. color.g = 0.12f;
  130. color.b = 0.12f;
  131. }
  132. if (Event.current.type != EventType.Repaint)
  133. return;
  134. Color orgColor = UnityEngine.GUI.color;
  135. UnityEngine.GUI.color = UnityEngine.GUI.color * color;
  136. UnityEngine.GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, size), EditorGUIUtility.whiteTexture);
  137. UnityEngine.GUI.DrawTexture(new Rect(rect.x, rect.yMax - size, rect.width, size), EditorGUIUtility.whiteTexture);
  138. UnityEngine.GUI.DrawTexture(new Rect(rect.x, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);
  139. UnityEngine.GUI.DrawTexture(new Rect(rect.xMax - size, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);
  140. UnityEngine.GUI.color = orgColor;
  141. }
  142. void OnGUI()
  143. {
  144. if (m_Settings == null)
  145. {
  146. if (AddressableAssetSettingsDefaultObject.Settings == null)
  147. return;
  148. InitializeWithDefaultSettings();
  149. }
  150. if (m_IsResizingVerticalSplitter)
  151. m_VerticalSplitterRatio = Mathf.Clamp(Event.current.mousePosition.y / position.height, 0.2f, 0.9f);
  152. if (m_IsResizingHorizontalSplitter)
  153. m_HorizontalSplitterRatio = Mathf.Clamp(Event.current.mousePosition.x / position.width, 0.15f, 0.6f);
  154. var toolbarRect = new Rect(0, 0, position.width, position.height);
  155. var servicesRect = new Rect(0, k_ToolbarHeight, (position.width * m_HorizontalSplitterRatio), position.height);
  156. var itemRect = new Rect(servicesRect.width + k_SplitterThickness, k_ToolbarHeight, position.width - servicesRect.width - k_SplitterThickness, (position.height * m_VerticalSplitterRatio) - k_ToolbarHeight);
  157. var logRect = new Rect(servicesRect.width + k_SplitterThickness, k_ToolbarHeight + itemRect.height + k_SplitterThickness, position.width - servicesRect.width - k_SplitterThickness,
  158. position.height - itemRect.height - k_SplitterThickness);
  159. var verticalSplitterRect = new Rect(servicesRect.width + k_SplitterThickness, k_ToolbarHeight + itemRect.height, position.width, k_SplitterThickness);
  160. var horizontalSplitterRect = new Rect(servicesRect.width, k_ToolbarHeight, k_SplitterThickness, position.height - k_ToolbarHeight);
  161. //EditorGUI.LabelField(splitterRect, string.Empty, UnityEngine.GUI.skin.horizontalSlider);
  162. EditorGUIUtility.AddCursorRect(verticalSplitterRect, MouseCursor.ResizeVertical);
  163. EditorGUIUtility.AddCursorRect(horizontalSplitterRect, MouseCursor.ResizeHorizontal);
  164. if (Event.current.type == EventType.MouseDown && verticalSplitterRect.Contains(Event.current.mousePosition))
  165. m_IsResizingVerticalSplitter = true;
  166. else if (Event.current.type == EventType.MouseDown && horizontalSplitterRect.Contains(Event.current.mousePosition))
  167. m_IsResizingHorizontalSplitter = true;
  168. else if (Event.current.type == EventType.MouseUp)
  169. {
  170. m_IsResizingVerticalSplitter = false;
  171. m_IsResizingHorizontalSplitter = false;
  172. }
  173. GUILayout.BeginArea(toolbarRect);
  174. GUILayout.BeginHorizontal(EditorStyles.toolbar);
  175. {
  176. var guiMode = new GUIContent("Create");
  177. Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown);
  178. if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown))
  179. {
  180. var menu = new GenericMenu();
  181. menu.AddItem(new GUIContent("Local Hosting"), false, () => AddService(0, "Local Hosting"));
  182. menu.AddItem(new GUIContent("Custom Service"), false, () => GetWindow<HostingServicesAddServiceWindow>(true, "Custom Service").Initialize(m_Settings));
  183. menu.DropDown(rMode);
  184. }
  185. GUILayout.FlexibleSpace();
  186. }
  187. GUILayout.EndHorizontal();
  188. GUILayout.EndArea();
  189. DrawOutline(servicesRect, 1);
  190. GUILayout.BeginArea(servicesRect);
  191. {
  192. Rect r = new Rect(servicesRect);
  193. r.y = 0;
  194. DrawServicesList(r);
  195. }
  196. GUILayout.EndArea();
  197. DrawOutline(itemRect, 1);
  198. GUILayout.BeginArea(itemRect, m_ItemRectPadding);
  199. {
  200. EditorGUILayout.Space();
  201. DrawServicesArea();
  202. EditorGUILayout.Space();
  203. }
  204. GUILayout.EndArea();
  205. DrawOutline(logRect, 1);
  206. GUILayout.BeginArea(logRect, m_LogRectPadding);
  207. {
  208. DrawLogArea(logRect);
  209. }
  210. GUILayout.EndArea();
  211. if (m_IsResizingVerticalSplitter || m_IsResizingHorizontalSplitter)
  212. Repaint();
  213. }
  214. void DrawServicesList(Rect rect)
  215. {
  216. var manager = m_Settings.HostingServicesManager;
  217. var svcList = manager.HostingServices;
  218. // Do removal queue
  219. if (m_RemovalQueue.Count > 0)
  220. {
  221. foreach (var svc in m_RemovalQueue)
  222. manager.RemoveHostingService(svc);
  223. m_RemovalQueue.Clear();
  224. }
  225. if (svcList.Count == 0)
  226. {
  227. m_Reload = true;
  228. return;
  229. }
  230. if (m_ServicesList == null || m_ServicesList.Names.Count != svcList.Count || m_Reload)
  231. {
  232. m_ServicesList = new HostingServicesListTreeView(new TreeViewState(), manager, this, HostingServicesListTreeView.CreateHeader());
  233. if (m_Reload)
  234. m_Reload = false;
  235. }
  236. m_ServicesList.OnGUI(rect);
  237. }
  238. void DrawServicesArea()
  239. {
  240. var manager = m_Settings.HostingServicesManager;
  241. m_ServicesScrollPos = EditorGUILayout.BeginScrollView(m_ServicesScrollPos);
  242. var svcList = manager.HostingServices;
  243. List<IHostingService> lst = new List<IHostingService>(svcList);
  244. if (lst.Count == 0)
  245. {
  246. EditorGUILayout.Space();
  247. EditorGUILayout.LabelField("No Hosting Services configured.");
  248. GUILayout.EndScrollView();
  249. return;
  250. }
  251. else if (m_serviceIndex >= lst.Count)
  252. {
  253. m_serviceIndex = lst.Count - 1;
  254. }
  255. DrawServiceElement(lst[m_serviceIndex], lst);
  256. GUILayout.EndScrollView();
  257. }
  258. /// <summary>
  259. /// Add a new hosting service to the HostingServicesManager. The service at index <paramref name="typeIndex"/> in ServiceTypes must implement the <see cref="IHostingService"/> interface, or an <see cref="ArgumentException"/> is thrown.
  260. /// </summary>
  261. /// <param name="typeIndex">The index of the service stored in ServiceTypes. The service at this index must implement <see cref="IHostingService"/></param>
  262. /// <param name="serviceName">A descriptive name for the new service instance.</param>
  263. public void AddService(int typeIndex, string serviceName)
  264. {
  265. string hostingName = string.Format("{0} {1}", serviceName, m_Settings.HostingServicesManager.NextInstanceId);
  266. m_Settings.HostingServicesManager.AddHostingService(ServiceTypes[typeIndex], hostingName);
  267. }
  268. /// <summary>
  269. /// Add a hosting service to the removal queue.
  270. /// </summary>
  271. /// <param name="svc">The service type to be removed.</param>
  272. /// <param name="showDialog">Indicates whether or not a warning dialogue box is shown.</param>
  273. public void RemoveService(IHostingService svc, bool showDialog = true)
  274. {
  275. if (!showDialog)
  276. m_RemovalQueue.Add(svc);
  277. else if (EditorUtility.DisplayDialog("Remove Service", "Are you sure you want to remove " + svc.DescriptiveName + "? This action cannot be undone.", "Ok", "Cancel"))
  278. m_RemovalQueue.Add(svc);
  279. }
  280. void DrawServiceElement(IHostingService svc, List<IHostingService> svcList)
  281. {
  282. bool isDirty = false;
  283. string newName = EditorGUILayout.DelayedTextField("Service Name", svc.DescriptiveName);
  284. if (svcList.Find(s => s.DescriptiveName == newName) == default(IHostingService))
  285. {
  286. svc.DescriptiveName = newName;
  287. m_ServicesList.Reload();
  288. isDirty = true;
  289. }
  290. var typeAndId = string.Format("{0} ({1})", svc.GetType().Name, svc.InstanceId.ToString());
  291. EditorGUILayout.LabelField("Service Type (ID)", typeAndId, GUILayout.MinWidth(225f));
  292. // Allow service to provide additional GUI configuration elements
  293. svc.OnGUI();
  294. var newIsServiceEnabled = EditorGUILayout.Toggle("Enable", svc.IsHostingServiceRunning);
  295. if (newIsServiceEnabled != svc.IsHostingServiceRunning)
  296. {
  297. if (newIsServiceEnabled)
  298. svc.StartHostingService();
  299. else
  300. svc.StopHostingService();
  301. isDirty = true;
  302. }
  303. EditorGUILayout.Space();
  304. using (new EditorGUI.DisabledScope(!svc.IsHostingServiceRunning))
  305. {
  306. GUILayout.BeginHorizontal();
  307. EditorGUILayout.LabelField("Hosting Service Variables");
  308. var manager = m_Settings.HostingServicesManager;
  309. if (GUILayout.Button("Refresh", GUILayout.ExpandWidth(false)))
  310. manager.RefreshGlobalProfileVariables();
  311. GUILayout.EndHorizontal();
  312. DrawProfileVarTable(svc);
  313. }
  314. if (isDirty && m_Settings != null)
  315. m_Settings.SetDirty(AddressableAssetSettings.ModificationEvent.HostingServicesManagerModified, this, false, true);
  316. }
  317. void DrawLogArea(Rect rect)
  318. {
  319. if (m_NewLogContent)
  320. {
  321. var height = UnityEngine.GUI.skin.GetStyle("Label").CalcHeight(new GUIContent(m_LogText), rect.width);
  322. m_LogScrollPos = new Vector2(0f, height);
  323. m_NewLogContent = false;
  324. }
  325. m_LogScrollPos = EditorGUILayout.BeginScrollView(m_LogScrollPos);
  326. GUILayout.Label(m_LogText);
  327. EditorGUILayout.EndScrollView();
  328. }
  329. internal static bool DictsAreEqual(Dictionary<string, string> a, Dictionary<string, string> b)
  330. {
  331. return a.Count == b.Count && !a.Except(b).Any();
  332. }
  333. void DrawProfileVarTable(IHostingService tableKey)
  334. {
  335. var manager = m_Settings.HostingServicesManager;
  336. var data = tableKey.ProfileVariables;
  337. HostingServicesProfileVarsTreeView table;
  338. if (!m_ProfileVarTables.TryGetValue(tableKey, out table))
  339. {
  340. table = new HostingServicesProfileVarsTreeView(new TreeViewState(),
  341. HostingServicesProfileVarsTreeView.CreateHeader());
  342. m_ProfileVarTables[tableKey] = table;
  343. m_TablePrevData[tableKey] = new Dictionary<string, string>(data);
  344. m_TablePrevManagerVariables[tableKey] = new Dictionary<string, string>(manager.GlobalProfileVariables);
  345. }
  346. else if (!DictsAreEqual(data, m_TablePrevData[tableKey]) || !DictsAreEqual(manager.GlobalProfileVariables, m_TablePrevManagerVariables[tableKey]))
  347. {
  348. table.ClearItems();
  349. m_TablePrevData[tableKey] = new Dictionary<string, string>(data);
  350. m_TablePrevManagerVariables[tableKey] = new Dictionary<string, string>(manager.GlobalProfileVariables);
  351. }
  352. if (table.Count == 0)
  353. {
  354. foreach (var globalVar in manager.GlobalProfileVariables)
  355. table.AddOrUpdateItem(globalVar.Key, globalVar.Value);
  356. foreach (var kvp in data)
  357. table.AddOrUpdateItem(kvp.Key, kvp.Value);
  358. }
  359. var rowHeight = table.RowHeight;
  360. var tableHeight = table.multiColumnHeader.height + rowHeight + (rowHeight * (data.Count() + manager.GlobalProfileVariables.Count)); // header + 1 extra line
  361. table.OnGUI(EditorGUILayout.GetControlRect(false, tableHeight));
  362. }
  363. /// <inheritdoc/>
  364. public void LogFormat(LogType logType, Object context, string format, params object[] args)
  365. {
  366. IHostingService svc = null;
  367. if (args.Length > 0)
  368. svc = args[args.Length - 1] as IHostingService;
  369. if (svc != null)
  370. {
  371. m_LogText += string.Format("[{0}] ", svc.DescriptiveName) + string.Format(format, args) + "\n";
  372. m_NewLogContent = true;
  373. }
  374. Debug.unityLogger.LogFormat(logType, context, format, args);
  375. }
  376. /// <inheritdoc/>
  377. public void LogException(Exception exception, Object context)
  378. {
  379. Debug.unityLogger.LogException(exception, context);
  380. }
  381. /// <inheritdoc/>
  382. public void OnBeforeSerialize()
  383. {
  384. // No implementation
  385. }
  386. /// <inheritdoc/>
  387. public void OnAfterDeserialize()
  388. {
  389. // No implementation
  390. }
  391. }
  392. }