AddressableAssetsSettingsGroupEditor.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using UnityEditor.AddressableAssets.Build;
  5. using UnityEditor.AddressableAssets.Diagnostics;
  6. using UnityEditor.AddressableAssets.Settings;
  7. using UnityEditor.Build.Pipeline.Utilities;
  8. using UnityEditor.IMGUI.Controls;
  9. using UnityEngine;
  10. using UnityEngine.AddressableAssets;
  11. using UnityEngine.Serialization;
  12. // ReSharper disable DelegateSubtraction
  13. namespace UnityEditor.AddressableAssets.GUI
  14. {
  15. [Serializable]
  16. class AddressableAssetsSettingsGroupEditor
  17. {
  18. [FormerlySerializedAs("treeState")]
  19. [SerializeField]
  20. TreeViewState m_TreeState;
  21. [FormerlySerializedAs("mchs")]
  22. [SerializeField]
  23. MultiColumnHeaderState m_Mchs;
  24. internal AddressableAssetEntryTreeView m_EntryTree;
  25. public AddressableAssetsWindow window;
  26. SearchField m_SearchField;
  27. const int k_SearchHeight = 20;
  28. AddressableAssetSettings m_Settings;
  29. internal AddressableAssetSettings settings
  30. {
  31. get
  32. {
  33. if (m_Settings == null)
  34. {
  35. m_Settings = AddressableAssetSettingsDefaultObject.Settings;
  36. }
  37. return m_Settings;
  38. }
  39. set => m_Settings = value;
  40. }
  41. bool m_ResizingVerticalSplitter;
  42. Rect m_VerticalSplitterRect = new Rect(0, 0, 10, k_SplitterWidth);
  43. [SerializeField]
  44. float m_VerticalSplitterPercent;
  45. const int k_SplitterWidth = 3;
  46. public AddressableAssetsSettingsGroupEditor(AddressableAssetsWindow w)
  47. {
  48. window = w;
  49. m_VerticalSplitterPercent = 0.8f;
  50. OnEnable();
  51. }
  52. public void SelectEntries(IList<AddressableAssetEntry> entries)
  53. {
  54. List<int> selectedIDs = new List<int>(entries.Count);
  55. Stack<AssetEntryTreeViewItem> items = new Stack<AssetEntryTreeViewItem>();
  56. if (m_EntryTree == null || m_EntryTree.Root == null)
  57. InitialiseEntryTree();
  58. foreach (TreeViewItem item in m_EntryTree.Root.children)
  59. {
  60. if(item is AssetEntryTreeViewItem i)
  61. items.Push(i);
  62. }
  63. while (items.Count > 0)
  64. {
  65. var i = items.Pop();
  66. bool contains = false;
  67. if (i.entry != null)
  68. {
  69. foreach (AddressableAssetEntry entry in entries)
  70. {
  71. // class instances can be different but refer to the same entry, use guid
  72. if (entry.guid == i.entry.guid && i.entry.TargetAsset == entry.TargetAsset)
  73. {
  74. contains = true;
  75. break;
  76. }
  77. }
  78. }
  79. if (!i.IsGroup && contains)
  80. {
  81. selectedIDs.Add(i.id);
  82. }
  83. else if (i.hasChildren)
  84. {
  85. foreach (TreeViewItem child in i.children)
  86. {
  87. if(child is AssetEntryTreeViewItem c)
  88. items.Push(c);
  89. }
  90. }
  91. }
  92. foreach (int i in selectedIDs)
  93. m_EntryTree.FrameItem(i);
  94. m_EntryTree.SetSelection(selectedIDs);
  95. }
  96. void OnSettingsModification(AddressableAssetSettings s, AddressableAssetSettings.ModificationEvent e, object o)
  97. {
  98. if (m_EntryTree == null)
  99. return;
  100. switch (e)
  101. {
  102. case AddressableAssetSettings.ModificationEvent.GroupAdded:
  103. case AddressableAssetSettings.ModificationEvent.GroupRemoved:
  104. case AddressableAssetSettings.ModificationEvent.EntryAdded:
  105. case AddressableAssetSettings.ModificationEvent.EntryMoved:
  106. case AddressableAssetSettings.ModificationEvent.EntryRemoved:
  107. case AddressableAssetSettings.ModificationEvent.GroupRenamed:
  108. case AddressableAssetSettings.ModificationEvent.EntryModified:
  109. case AddressableAssetSettings.ModificationEvent.BatchModification:
  110. m_EntryTree.Reload();
  111. if (window != null)
  112. window.Repaint();
  113. break;
  114. }
  115. }
  116. GUIStyle GetStyle(string styleName)
  117. {
  118. GUIStyle s = UnityEngine.GUI.skin.FindStyle(styleName);
  119. if (s == null)
  120. s = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle(styleName);
  121. if (s == null)
  122. {
  123. Addressables.LogError("Missing built-in guistyle " + styleName);
  124. s = new GUIStyle();
  125. }
  126. return s;
  127. }
  128. [NonSerialized]
  129. List<GUIStyle> m_SearchStyles;
  130. [NonSerialized]
  131. GUIStyle m_ButtonStyle;
  132. [NonSerialized]
  133. Texture2D m_CogIcon;
  134. void TopToolbar(Rect toolbarPos)
  135. {
  136. if (m_SearchStyles == null)
  137. {
  138. m_SearchStyles = new List<GUIStyle>();
  139. m_SearchStyles.Add(GetStyle("ToolbarSeachTextFieldPopup")); //GetStyle("ToolbarSeachTextField");
  140. m_SearchStyles.Add(GetStyle("ToolbarSeachCancelButton"));
  141. m_SearchStyles.Add(GetStyle("ToolbarSeachCancelButtonEmpty"));
  142. }
  143. if (m_ButtonStyle == null)
  144. m_ButtonStyle = GetStyle("ToolbarButton");
  145. if (m_CogIcon == null)
  146. m_CogIcon = EditorGUIUtility.FindTexture("_Popup");
  147. GUILayout.BeginArea(new Rect(0, 0, toolbarPos.width, k_SearchHeight));
  148. GUILayout.BeginHorizontal(EditorStyles.toolbar);
  149. {
  150. float spaceBetween = 4f;
  151. {
  152. var guiMode = new GUIContent("Create");
  153. Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown);
  154. if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown))
  155. {
  156. var menu = new GenericMenu();
  157. foreach (var templateObject in settings.GroupTemplateObjects)
  158. {
  159. if (templateObject != null)
  160. menu.AddItem(new GUIContent("Group/" + templateObject.name), false, m_EntryTree.CreateNewGroup, templateObject);
  161. }
  162. menu.AddSeparator(string.Empty);
  163. menu.AddItem(new GUIContent("Group/Blank (no schema)"), false, m_EntryTree.CreateNewGroup, null);
  164. menu.DropDown(rMode);
  165. }
  166. }
  167. CreateProfileDropdown();
  168. {
  169. var guiMode = new GUIContent("Tools");
  170. Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown);
  171. if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown))
  172. {
  173. var menu = new GenericMenu();
  174. menu.AddItem(new GUIContent("Inspect System Settings"), false, () =>
  175. {
  176. EditorApplication.ExecuteMenuItem("Window/General/Inspector");
  177. EditorGUIUtility.PingObject(AddressableAssetSettingsDefaultObject.Settings);
  178. Selection.activeObject = AddressableAssetSettingsDefaultObject.Settings;
  179. });
  180. menu.AddItem(new GUIContent("Check for Content Update Restrictions"), false, OnPrepareUpdate);
  181. menu.AddItem(new GUIContent("Window/Profiles"), false, () => EditorWindow.GetWindow<ProfileWindow>().Show(true));
  182. menu.AddItem(new GUIContent("Window/Labels"), false, () => EditorWindow.GetWindow<LabelWindow>(true).Intialize(settings));
  183. menu.AddItem(new GUIContent("Window/Analyze"), false, AnalyzeWindow.ShowWindow);
  184. menu.AddItem(new GUIContent("Window/Hosting Services"), false, () => EditorWindow.GetWindow<HostingServicesWindow>().Show(settings));
  185. menu.AddItem(new GUIContent("Window/Event Viewer"), false, ResourceProfilerWindow.ShowWindow);
  186. menu.AddItem(new GUIContent("Groups View/Show Sprite and Subobject Addresses"), ProjectConfigData.ShowSubObjectsInGroupView, () => { ProjectConfigData.ShowSubObjectsInGroupView = !ProjectConfigData.ShowSubObjectsInGroupView; m_EntryTree.Reload(); });
  187. menu.AddItem(new GUIContent("Groups View/Group Hierarchy with Dashes", "If enabled, group names are parsed as if a '-' represented a child in hierarchy. So a group called 'a-b-c' would be displayed as if it were in a folder called 'b' that lived in a folder called 'a'. In this mode, only groups without '-' can be rearranged within the groups window."),
  188. ProjectConfigData.ShowGroupsAsHierarchy, () => { ProjectConfigData.ShowGroupsAsHierarchy = !ProjectConfigData.ShowGroupsAsHierarchy; m_EntryTree.Reload(); });
  189. var bundleList = AssetDatabase.GetAllAssetBundleNames();
  190. if (bundleList != null && bundleList.Length > 0)
  191. menu.AddItem(new GUIContent("Convert Legacy AssetBundles"), false, () => window.OfferToConvert(AddressableAssetSettingsDefaultObject.Settings));
  192. menu.DropDown(rMode);
  193. }
  194. }
  195. GUILayout.FlexibleSpace();
  196. GUILayout.Space(spaceBetween * 2f);
  197. {
  198. GUILayout.Space(8);
  199. var guiMode = new GUIContent("Play Mode Script");
  200. Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown);
  201. if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown))
  202. {
  203. var menu = new GenericMenu();
  204. for (int i = 0; i < settings.DataBuilders.Count; i++)
  205. {
  206. var m = settings.GetDataBuilder(i);
  207. if (m.CanBuildData<AddressablesPlayModeBuildResult>())
  208. menu.AddItem(new GUIContent(m.Name), i == settings.ActivePlayModeDataBuilderIndex, OnSetActivePlayModeScript, i);
  209. }
  210. menu.DropDown(rMode);
  211. }
  212. }
  213. var guiBuild = new GUIContent("Build");
  214. Rect rBuild = GUILayoutUtility.GetRect(guiBuild, EditorStyles.toolbarDropDown);
  215. if (EditorGUI.DropdownButton(rBuild, guiBuild, FocusType.Passive, EditorStyles.toolbarDropDown))
  216. {
  217. //GUIUtility.hotControl = 0;
  218. var menu = new GenericMenu();
  219. var AddressablesPlayerBuildResultBuilderExists = false;
  220. for (int i = 0; i < settings.DataBuilders.Count; i++)
  221. {
  222. var m = settings.GetDataBuilder(i);
  223. if (m.CanBuildData<AddressablesPlayerBuildResult>())
  224. {
  225. AddressablesPlayerBuildResultBuilderExists = true;
  226. menu.AddItem(new GUIContent("New Build/" + m.Name), false, OnBuildScript, i);
  227. }
  228. }
  229. if (!AddressablesPlayerBuildResultBuilderExists)
  230. {
  231. menu.AddDisabledItem(new GUIContent("New Build/No Build Script Available"));
  232. }
  233. menu.AddItem(new GUIContent("Update a Previous Build"), false, OnUpdateBuild);
  234. menu.AddItem(new GUIContent("Clean Build/All"), false, OnCleanAll);
  235. menu.AddItem(new GUIContent("Clean Build/Content Builders/All"), false, OnCleanAddressables, null);
  236. for (int i = 0; i < settings.DataBuilders.Count; i++)
  237. {
  238. var m = settings.GetDataBuilder(i);
  239. menu.AddItem(new GUIContent("Clean Build/Content Builders/" + m.Name), false, OnCleanAddressables, m);
  240. }
  241. menu.AddItem(new GUIContent("Clean Build/Build Pipeline Cache"), false, OnCleanSBP);
  242. menu.DropDown(rBuild);
  243. }
  244. #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
  245. //Build & Release
  246. var guiBuildAndRelease = new GUIContent("Build & Release");
  247. if (GUILayout.Button(guiBuildAndRelease, EditorStyles.toolbarButton))
  248. {
  249. OnBuildAndRelease();
  250. }
  251. #endif
  252. GUILayout.Space(4);
  253. Rect searchRect = GUILayoutUtility.GetRect(0, toolbarPos.width * 0.6f, 16f, 16f, m_SearchStyles[0], GUILayout.MinWidth(65), GUILayout.MaxWidth(300));
  254. Rect popupPosition = searchRect;
  255. popupPosition.width = 20;
  256. if (Event.current.type == EventType.MouseDown && popupPosition.Contains(Event.current.mousePosition))
  257. {
  258. var menu = new GenericMenu();
  259. menu.AddItem(new GUIContent("Hierarchical Search"), ProjectConfigData.HierarchicalSearch, OnHierSearchClick);
  260. menu.DropDown(popupPosition);
  261. }
  262. else
  263. {
  264. var baseSearch = ProjectConfigData.HierarchicalSearch ? m_EntryTree.customSearchString : m_EntryTree.searchString;
  265. var searchString = m_SearchField.OnGUI(searchRect, baseSearch, m_SearchStyles[0], m_SearchStyles[1], m_SearchStyles[2]);
  266. if (baseSearch != searchString)
  267. {
  268. m_EntryTree?.Search(searchString);
  269. }
  270. }
  271. }
  272. GUILayout.EndHorizontal();
  273. GUILayout.EndArea();
  274. }
  275. void OnCleanAll()
  276. {
  277. OnCleanAddressables(null);
  278. OnCleanSBP();
  279. }
  280. void OnCleanAddressables(object builder)
  281. {
  282. AddressableAssetSettings.CleanPlayerContent(builder as IDataBuilder);
  283. }
  284. void OnCleanSBP()
  285. {
  286. BuildCache.PurgeCache(true);
  287. }
  288. void OnPrepareUpdate()
  289. {
  290. var path = ContentUpdateScript.GetContentStateDataPath(true);
  291. if (string.IsNullOrEmpty(path))
  292. Debug.LogWarning("No path specified for Content State Data file.");
  293. else if (!File.Exists(path))
  294. Debug.LogWarningFormat("No Content State Data file exists at path: {0}");
  295. else
  296. ContentUpdatePreviewWindow.PrepareForContentUpdate(AddressableAssetSettingsDefaultObject.Settings, path);
  297. }
  298. #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER)
  299. async void OnBuildAndRelease()
  300. {
  301. await AddressableAssetSettings.BuildAndReleasePlayerContent();
  302. }
  303. #endif
  304. void OnBuildScript(object context)
  305. {
  306. OnSetActiveBuildScript(context);
  307. OnBuildPlayerData();
  308. }
  309. void OnBuildPlayerData()
  310. {
  311. AddressableAssetSettings.BuildPlayerContent();
  312. }
  313. void OnUpdateBuild()
  314. {
  315. var path = ContentUpdateScript.GetContentStateDataPath(true);
  316. if (!string.IsNullOrEmpty(path))
  317. ContentUpdateScript.BuildContentUpdate(AddressableAssetSettingsDefaultObject.Settings, path);
  318. }
  319. void OnSetActiveBuildScript(object context)
  320. {
  321. AddressableAssetSettingsDefaultObject.Settings.ActivePlayerDataBuilderIndex = (int)context;
  322. }
  323. void OnSetActivePlayModeScript(object context)
  324. {
  325. AddressableAssetSettingsDefaultObject.Settings.ActivePlayModeDataBuilderIndex = (int)context;
  326. }
  327. void OnHierSearchClick()
  328. {
  329. ProjectConfigData.HierarchicalSearch = !ProjectConfigData.HierarchicalSearch;
  330. m_EntryTree.SwapSearchType();
  331. m_EntryTree.Reload();
  332. m_EntryTree.Repaint();
  333. }
  334. void CreateProfileDropdown()
  335. {
  336. var activeProfileName = settings.profileSettings.GetProfileName(settings.activeProfileId);
  337. if (string.IsNullOrEmpty(activeProfileName))
  338. {
  339. settings.activeProfileId = null; //this will reset it to default.
  340. activeProfileName = settings.profileSettings.GetProfileName(settings.activeProfileId);
  341. }
  342. var profileButton = new GUIContent("Profile: " + activeProfileName);
  343. Rect r = GUILayoutUtility.GetRect(profileButton, m_ButtonStyle, GUILayout.Width(115f));
  344. if (EditorGUI.DropdownButton(r, profileButton, FocusType.Passive, EditorStyles.toolbarDropDown))
  345. {
  346. //GUIUtility.hotControl = 0;
  347. var menu = new GenericMenu();
  348. var nameList = settings.profileSettings.GetAllProfileNames();
  349. foreach (var name in nameList)
  350. {
  351. menu.AddItem(new GUIContent(name), name == activeProfileName, SetActiveProfile, name);
  352. }
  353. menu.AddSeparator(string.Empty);
  354. menu.AddItem(new GUIContent("Manage Profiles"), false, () => EditorWindow.GetWindow<ProfileWindow>().Show(true));
  355. menu.DropDown(r);
  356. }
  357. }
  358. void SetActiveProfile(object context)
  359. {
  360. var n = context as string;
  361. AddressableAssetSettingsDefaultObject.Settings.activeProfileId = AddressableAssetSettingsDefaultObject.Settings.profileSettings.GetProfileId(n);
  362. AddressableAssetUtility.OpenAssetIfUsingVCIntegration(AddressableAssetSettingsDefaultObject.Settings);
  363. }
  364. bool m_ModificationRegistered;
  365. public void OnEnable()
  366. {
  367. if (AddressableAssetSettingsDefaultObject.Settings == null)
  368. return;
  369. if (!m_ModificationRegistered)
  370. {
  371. AddressableAssetSettingsDefaultObject.Settings.OnModification += OnSettingsModification;
  372. m_ModificationRegistered = true;
  373. }
  374. }
  375. public void OnDisable()
  376. {
  377. if (AddressableAssetSettingsDefaultObject.Settings == null)
  378. return;
  379. if (m_ModificationRegistered)
  380. {
  381. AddressableAssetSettingsDefaultObject.Settings.OnModification -= OnSettingsModification;
  382. m_ModificationRegistered = false;
  383. }
  384. }
  385. public bool OnGUI(Rect pos)
  386. {
  387. if (settings == null)
  388. return false;
  389. if (!m_ModificationRegistered)
  390. {
  391. m_ModificationRegistered = true;
  392. settings.OnModification -= OnSettingsModification; //just in case...
  393. settings.OnModification += OnSettingsModification;
  394. }
  395. if (m_EntryTree == null)
  396. InitialiseEntryTree();
  397. HandleVerticalResize(pos);
  398. var inRectY = pos.height;
  399. var searchRect = new Rect(pos.xMin, pos.yMin, pos.width, k_SearchHeight);
  400. var treeRect = new Rect(pos.xMin, pos.yMin + k_SearchHeight, pos.width, inRectY - k_SearchHeight);
  401. TopToolbar(searchRect);
  402. m_EntryTree.OnGUI(treeRect);
  403. return m_ResizingVerticalSplitter;
  404. }
  405. internal AddressableAssetEntryTreeView InitialiseEntryTree()
  406. {
  407. if (m_TreeState == null)
  408. m_TreeState = new TreeViewState();
  409. var headerState = AddressableAssetEntryTreeView.CreateDefaultMultiColumnHeaderState();
  410. if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_Mchs, headerState))
  411. MultiColumnHeaderState.OverwriteSerializedFields(m_Mchs, headerState);
  412. m_Mchs = headerState;
  413. m_SearchField = new SearchField();
  414. m_EntryTree = new AddressableAssetEntryTreeView(m_TreeState, m_Mchs, this);
  415. m_EntryTree.Reload();
  416. return m_EntryTree;
  417. }
  418. public void Reload()
  419. {
  420. if (m_EntryTree != null)
  421. m_EntryTree.Reload();
  422. }
  423. void HandleVerticalResize(Rect position)
  424. {
  425. m_VerticalSplitterRect.y = (int)(position.yMin + position.height * m_VerticalSplitterPercent);
  426. m_VerticalSplitterRect.width = position.width;
  427. m_VerticalSplitterRect.height = k_SplitterWidth;
  428. EditorGUIUtility.AddCursorRect(m_VerticalSplitterRect, MouseCursor.ResizeVertical);
  429. if (Event.current.type == EventType.MouseDown && m_VerticalSplitterRect.Contains(Event.current.mousePosition))
  430. m_ResizingVerticalSplitter = true;
  431. if (m_ResizingVerticalSplitter)
  432. {
  433. var mousePosInRect = Event.current.mousePosition.y - position.yMin;
  434. m_VerticalSplitterPercent = Mathf.Clamp(mousePosInRect / position.height, 0.20f, 0.90f);
  435. m_VerticalSplitterRect.y = (int)(position.height * m_VerticalSplitterPercent + position.yMin);
  436. if (Event.current.type == EventType.MouseUp)
  437. {
  438. m_ResizingVerticalSplitter = false;
  439. }
  440. }
  441. else
  442. m_VerticalSplitterPercent = Mathf.Clamp(m_VerticalSplitterPercent, 0.20f, 0.90f);
  443. }
  444. }
  445. }