using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using UnityEditor.AddressableAssets.Build; using UnityEditor.AddressableAssets.Diagnostics; using UnityEditor.AddressableAssets.Settings; using UnityEditor.Build.Pipeline.Utilities; using UnityEditor.IMGUI.Controls; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.Util; using UnityEngine.Serialization; #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER) using static UnityEditor.AddressableAssets.Build.CcdBuildEvents; #endif // ReSharper disable DelegateSubtraction namespace UnityEditor.AddressableAssets.GUI { [Serializable] internal class AddressableAssetsSettingsGroupEditor { [System.AttributeUsage(AttributeTargets.Class)] public class HideBuildMenuInUI : Attribute { } /// /// Interface used for classes that implement Addressables build menu steps. /// public interface IAddressablesBuildMenu { /// /// Path from Build in the Addressables Groups Window. /// string BuildMenuPath { get; } /// /// If returns true, build menu will extend to available Build Scripts. /// bool SelectableBuildScript { get; } /// /// Display order in the menu, lower values are displayed first. /// int Order { get; } /// /// Called before beginning the Addressables content build. /// /// Input used for the Addressables content build /// True for success, else false and fail the build bool OnPrebuild(AddressablesDataBuilderInput input); /// /// Called after the Addressables content build if the build was successful. /// /// Input used for the Addressables content build /// Result of the Addressables content build /// True for success, else false and fail the build bool OnPostbuild(AddressablesDataBuilderInput input, AddressablesPlayerBuildResult result); } internal struct BuildMenuContext { public IAddressablesBuildMenu BuildMenu { get; set; } public int buildScriptIndex; public AddressableAssetSettings Settings { get; set; } } [FormerlySerializedAs("treeState")] [SerializeField] TreeViewState m_TreeState; [FormerlySerializedAs("mchs")] [SerializeField] MultiColumnHeaderState m_Mchs; internal AddressableAssetEntryTreeView m_EntryTree; public AddressableAssetsWindow window; SearchField m_SearchField; const int k_SearchHeight = 20; AddressableAssetSettings m_Settings; internal AddressableAssetSettings settings { get { if (m_Settings == null) { m_Settings = AddressableAssetSettingsDefaultObject.Settings; } return m_Settings; } set => m_Settings = value; } bool m_ResizingVerticalSplitter; Rect m_VerticalSplitterRect = new Rect(0, 0, 10, k_SplitterWidth); [SerializeField] float m_VerticalSplitterPercent; const int k_SplitterWidth = 3; public AddressableAssetsSettingsGroupEditor(AddressableAssetsWindow w) { window = w; m_VerticalSplitterPercent = 0.8f; OnEnable(); } public void SelectEntries(IList entries) { List selectedIDs = new List(entries.Count); Stack items = new Stack(); if (m_EntryTree == null || m_EntryTree.Root == null) InitialiseEntryTree(); foreach (TreeViewItem item in m_EntryTree.Root.children) { if (item is AssetEntryTreeViewItem i) items.Push(i); } while (items.Count > 0) { var i = items.Pop(); bool contains = false; if (i.entry != null) { foreach (AddressableAssetEntry entry in entries) { // class instances can be different but refer to the same entry, use guid if (entry.guid == i.entry.guid && i.entry.TargetAsset == entry.TargetAsset) { contains = true; break; } } } if (!i.IsGroup && contains) { selectedIDs.Add(i.id); } else if (i.hasChildren) { foreach (TreeViewItem child in i.children) { if (child is AssetEntryTreeViewItem c) items.Push(c); } } } foreach (int i in selectedIDs) m_EntryTree.FrameItem(i); m_EntryTree.SetSelection(selectedIDs); } public void SelectGroup(AddressableAssetGroup group, bool fireSelectionChanged) { Stack items = new Stack(); if (m_EntryTree == null || m_EntryTree.Root == null) InitialiseEntryTree(); foreach (TreeViewItem item in m_EntryTree.Root.children) { if (item is AssetEntryTreeViewItem i) items.Push(i); } while (items.Count > 0) { AssetEntryTreeViewItem item = items.Pop(); if (item.IsGroup && item.group.Guid == group.Guid) { m_EntryTree.FrameItem(item.id); var selectedIds = new List(){ item.id }; if (fireSelectionChanged) m_EntryTree.SetSelection(selectedIds, TreeViewSelectionOptions.FireSelectionChanged); else m_EntryTree.SetSelection(selectedIds); return; } else if (!string.IsNullOrEmpty(item.folderPath) && item.hasChildren) { foreach (TreeViewItem child in item.children) { if (child is AssetEntryTreeViewItem c) items.Push(c); } } } } void OnSettingsModification(AddressableAssetSettings s, AddressableAssetSettings.ModificationEvent e, object o) { if (m_EntryTree == null) return; switch (e) { case AddressableAssetSettings.ModificationEvent.GroupAdded: case AddressableAssetSettings.ModificationEvent.GroupRemoved: case AddressableAssetSettings.ModificationEvent.EntryAdded: case AddressableAssetSettings.ModificationEvent.EntryMoved: case AddressableAssetSettings.ModificationEvent.EntryRemoved: case AddressableAssetSettings.ModificationEvent.GroupRenamed: case AddressableAssetSettings.ModificationEvent.EntryModified: case AddressableAssetSettings.ModificationEvent.BatchModification: m_EntryTree.Reload(); if (window != null) window.Repaint(); break; } } GUIStyle GetStyle(string styleName) { GUIStyle s = UnityEngine.GUI.skin.FindStyle(styleName); if (s == null) s = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).FindStyle(styleName); if (s == null) { Addressables.LogError("Missing built-in guistyle " + styleName); s = new GUIStyle(); } return s; } [NonSerialized] List m_SearchStyles; [NonSerialized] GUIStyle m_ButtonStyle; [NonSerialized] Texture2D m_CogIcon; void TopToolbar(Rect toolbarPos) { if (m_SearchStyles == null) { m_SearchStyles = new List(); m_SearchStyles.Add(GetStyle("ToolbarSeachTextFieldPopup")); //GetStyle("ToolbarSeachTextField"); m_SearchStyles.Add(GetStyle("ToolbarSeachCancelButton")); m_SearchStyles.Add(GetStyle("ToolbarSeachCancelButtonEmpty")); } if (m_ButtonStyle == null) m_ButtonStyle = GetStyle("ToolbarButton"); if (m_CogIcon == null) m_CogIcon = EditorGUIUtility.FindTexture("_Popup"); GUILayout.BeginArea(new Rect(0, 0, toolbarPos.width, k_SearchHeight)); GUILayout.BeginHorizontal(EditorStyles.toolbar); { float spaceBetween = 4f; { var guiMode = new GUIContent("New", "Create a new group"); Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown); if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown)) { var menu = new GenericMenu(); foreach (var templateObject in settings.GroupTemplateObjects) { if (templateObject != null) menu.AddItem(new GUIContent(templateObject.name), false, m_EntryTree.CreateNewGroup, templateObject); } menu.AddSeparator(string.Empty); menu.AddItem(new GUIContent("Blank (no schema)"), false, m_EntryTree.CreateNewGroup, null); menu.DropDown(rMode); } } if (toolbarPos.width > 430) CreateProfileDropdown(); { var guiMode = new GUIContent("Tools", "Tools used to configure or analyze Addressable Assets"); Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown); if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown)) { var menu = new GenericMenu(); menu.AddItem(new GUIContent("Inspect System Settings"), false, () => { EditorApplication.ExecuteMenuItem("Window/General/Inspector"); EditorGUIUtility.PingObject(AddressableAssetSettingsDefaultObject.Settings); Selection.activeObject = AddressableAssetSettingsDefaultObject.Settings; }); menu.AddItem(new GUIContent("Check for Content Update Restrictions"), false, OnPrepareUpdate); menu.AddItem(new GUIContent("Window/Profiles"), false, () => EditorWindow.GetWindow().Show(true)); menu.AddItem(new GUIContent("Window/Labels"), false, () => EditorWindow.GetWindow(true).Intialize(settings)); menu.AddItem(new GUIContent("Window/Analyze"), false, AnalyzeWindow.ShowWindow); menu.AddItem(new GUIContent("Window/Hosting Services"), false, () => EditorWindow.GetWindow().Show(settings)); menu.AddItem(new GUIContent("Window/Event Viewer"), false, ResourceProfilerWindow.ShowWindow); #if UNITY_2022_2_OR_NEWER menu.AddItem(new GUIContent("Window/Addressables Report"), false, BuildReportVisualizer.BuildReportWindow.ShowWindow); #endif menu.AddItem(new GUIContent("Groups View/Show Sprite and Subobject Addresses"), ProjectConfigData.ShowSubObjectsInGroupView, () => { ProjectConfigData.ShowSubObjectsInGroupView = !ProjectConfigData.ShowSubObjectsInGroupView; m_EntryTree.Reload(); }); 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."), ProjectConfigData.ShowGroupsAsHierarchy, () => { ProjectConfigData.ShowGroupsAsHierarchy = !ProjectConfigData.ShowGroupsAsHierarchy; m_EntryTree.Reload(); }); var bundleList = AssetDatabase.GetAllAssetBundleNames(); if (bundleList != null && bundleList.Length > 0) menu.AddItem(new GUIContent("Convert Legacy AssetBundles"), false, () => window.OfferToConvert(AddressableAssetSettingsDefaultObject.Settings)); menu.DropDown(rMode); } } GUILayout.FlexibleSpace(); if (toolbarPos.width > 300) GUILayout.Space((spaceBetween * 2f) + 8); { string playmodeButtonName = toolbarPos.width < 300 ? "Play Mode" : "Play Mode Script"; var guiMode = new GUIContent(playmodeButtonName, "Determines how the Addressables system loads assets in Play Mode"); Rect rMode = GUILayoutUtility.GetRect(guiMode, EditorStyles.toolbarDropDown); if (EditorGUI.DropdownButton(rMode, guiMode, FocusType.Passive, EditorStyles.toolbarDropDown)) { var menu = new GenericMenu(); for (int i = 0; i < settings.DataBuilders.Count; i++) { var m = settings.GetDataBuilder(i); if (m.CanBuildData()) { string text = m is Build.DataBuilders.BuildScriptPackedPlayMode ? $"Use Existing Build ({PlatformMappingService.GetAddressablesPlatformPathInternal(EditorUserBuildSettings.activeBuildTarget)})" : m.Name; menu.AddItem(new GUIContent(text), i == settings.ActivePlayModeDataBuilderIndex, OnSetActivePlayModeScript, i); } } menu.DropDown(rMode); } } var guiBuild = new GUIContent("Build", "Options for building Addressable Assets"); Rect rBuild = GUILayoutUtility.GetRect(guiBuild, EditorStyles.toolbarDropDown); if (EditorGUI.DropdownButton(rBuild, guiBuild, FocusType.Passive, EditorStyles.toolbarDropDown)) { var types = AddressableAssetUtility.GetTypes(); var genericDropdownMenu = new GenericMenu(); var displayMenus = CreateBuildMenus(types); foreach (IAddressablesBuildMenu buildOption in displayMenus) { if (buildOption.SelectableBuildScript) { bool addressablesPlayerBuildResultBuilderExists = false; for (int i = 0; i < settings.DataBuilders.Count; i++) { var dataBuilder = settings.GetDataBuilder(i); if (dataBuilder.CanBuildData()) { addressablesPlayerBuildResultBuilderExists = true; BuildMenuContext context = new BuildMenuContext() { buildScriptIndex = i, BuildMenu = buildOption, Settings = settings }; genericDropdownMenu.AddItem(new GUIContent(buildOption.BuildMenuPath + "/" + dataBuilder.Name), false, OnBuildAddressables, context); } } if (!addressablesPlayerBuildResultBuilderExists) genericDropdownMenu.AddDisabledItem(new GUIContent(buildOption.BuildMenuPath + "/No Build Script Available")); } else { BuildMenuContext context = new BuildMenuContext() { buildScriptIndex = -1, BuildMenu = buildOption, Settings = settings }; genericDropdownMenu.AddItem(new GUIContent(buildOption.BuildMenuPath), false, OnBuildAddressables, context); } } genericDropdownMenu.AddSeparator(""); genericDropdownMenu.AddItem(new GUIContent("Clear Build Cache/All"), false, OnCleanAll); genericDropdownMenu.AddItem(new GUIContent("Clear Build Cache/Content Builders/All"), false, OnCleanAddressables, null); for (int i = 0; i < settings.DataBuilders.Count; i++) { var m = settings.GetDataBuilder(i); genericDropdownMenu.AddItem(new GUIContent("Clear Build Cache/Content Builders/" + m.Name), false, OnCleanAddressables, m); } genericDropdownMenu.AddItem(new GUIContent("Clear Build Cache/Build Pipeline Cache"), false, OnCleanSBP); genericDropdownMenu.DropDown(rBuild); } #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER) var guiBuildToCcd = new GUIContent("Build to CCD", "Options for building Addressable Assets"); Rect rBuildToCcd = GUILayoutUtility.GetRect(guiBuildToCcd, EditorStyles.toolbarDropDown); if (EditorGUI.DropdownButton(rBuildToCcd, guiBuildToCcd, FocusType.Passive, EditorStyles.toolbarDropDown)) { var types = AddressableAssetUtility.GetTypes(); var genericDropdownMenu = new GenericMenu(); var displayMenus = CreateBuildMenus(types); foreach (IAddressablesBuildMenu buildOption in displayMenus) { if (buildOption.SelectableBuildScript) { bool addressablesPlayerBuildResultBuilderExists = false; for (int i = 0; i < settings.DataBuilders.Count; i++) { var dataBuilder = settings.GetDataBuilder(i); if (dataBuilder.CanBuildData()) { addressablesPlayerBuildResultBuilderExists = true; BuildMenuContext context = new BuildMenuContext() { buildScriptIndex = i, BuildMenu = buildOption, Settings = settings }; genericDropdownMenu.AddItem(new GUIContent(dataBuilder.Name), false, OnBuildCcd, context); } } if (!addressablesPlayerBuildResultBuilderExists) genericDropdownMenu.AddDisabledItem(new GUIContent("No Build Script Available")); } else { BuildMenuContext context = new BuildMenuContext() { buildScriptIndex = -1, BuildMenu = buildOption, Settings = settings }; genericDropdownMenu.AddItem(new GUIContent(buildOption.BuildMenuPath), false, OnBuildCcd, context); } } genericDropdownMenu.DropDown(rBuildToCcd); } #endif GUILayout.Space(4); Rect searchRect = GUILayoutUtility.GetRect(0, toolbarPos.width * 0.6f, 16f, 16f, m_SearchStyles[0], GUILayout.MinWidth(65), GUILayout.MaxWidth(300)); Rect popupPosition = searchRect; popupPosition.width = 20; if (Event.current.type == EventType.MouseDown && popupPosition.Contains(Event.current.mousePosition)) { var menu = new GenericMenu(); menu.AddItem(new GUIContent("Hierarchical Search"), ProjectConfigData.HierarchicalSearch, OnHierSearchClick); menu.DropDown(popupPosition); } else { var baseSearch = ProjectConfigData.HierarchicalSearch ? m_EntryTree.customSearchString : m_EntryTree.searchString; var searchString = m_SearchField.OnGUI(searchRect, baseSearch, m_SearchStyles[0], m_SearchStyles[1], m_SearchStyles[2]); if (baseSearch != searchString) { m_EntryTree?.Search(searchString); } } } GUILayout.EndHorizontal(); GUILayout.EndArea(); } internal static List CreateBuildMenus(IList types, bool includeMenusHiddenFromUI = false) { List displayMenus = new List(types.Count); foreach (Type menuType in types) { if (Attribute.GetCustomAttribute(menuType, typeof(HideBuildMenuInUI)) != null && !includeMenusHiddenFromUI) continue; var menuInst = Activator.CreateInstance(menuType) as IAddressablesBuildMenu; if (string.IsNullOrEmpty(menuInst.BuildMenuPath)) continue; var existing = displayMenus.Find(b => b.BuildMenuPath == menuInst.BuildMenuPath); if (existing == null) displayMenus.Add(menuInst); else { var existingType = existing.GetType(); if (menuType.IsSubclassOf(existingType)) { // override existing with our current displayMenus.Remove(existing); displayMenus.Add(menuInst); } else if (!existingType.IsSubclassOf(menuType)) { // both are same level, issue Addressables.LogWarning( $"Trying to new build menu [{menuType}] with path \"{menuInst.BuildMenuPath}\". But an existing type already exists with that path, [{existingType}]."); } } } displayMenus.Sort((a, b) => a.Order.CompareTo(b.Order)); return displayMenus; } private static void OnBuildAddressables(object ctx) { BuildMenuContext buildAddressablesContext = (BuildMenuContext)ctx; OnBuildAddressables(buildAddressablesContext); } internal static void OnBuildAddressables(BuildMenuContext context) { if (context.BuildMenu == null) { Addressables.LogError("Addressable content build failure : null build menu context"); return; } if (context.buildScriptIndex >= 0) context.Settings.ActivePlayerDataBuilderIndex = context.buildScriptIndex; var builderInput = new AddressablesDataBuilderInput(context.Settings); if (!HandlePreBuild(context, builderInput)) return; AddressableAssetSettings.BuildPlayerContent(out AddressablesPlayerBuildResult rst, builderInput); HandlePostBuild(context, builderInput, rst); } #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER) private static void OnBuildCcd(object ctx) { BuildMenuContext buildAddressableContext = (BuildMenuContext)ctx; OnBuildCcd(buildAddressableContext); } private static async void OnBuildCcd(BuildMenuContext context) { bool isUpdate = false; PreEvent preEvent = null; PostEvent postEvent = null; try { if (context.BuildMenu == null) { Addressables.LogError("Addressable content build failure : null build menu context"); return; } if (context.buildScriptIndex >= 0) context.Settings.ActivePlayerDataBuilderIndex = context.buildScriptIndex; isUpdate = context.BuildMenu is AddressablesBuildMenuUpdateAPreviousBuild; RegisterBuildMenuEvents(context, isUpdate, out preEvent, out postEvent); await AddressableAssetSettings.BuildAndReleasePlayerContent(isUpdate); } finally { UnregisterBuildMenuEvents(isUpdate, preEvent, postEvent); EditorUtility.ClearProgressBar(); } } static void RegisterBuildMenuEvents(BuildMenuContext context, bool isUpdate, out PreEvent preEvent, out PostEvent postEvent) { preEvent = GetHandlePreBuildDelegate(context); postEvent = GetHandlePostBuildDelegate(context); if (isUpdate) { CcdBuildEvents.OnPreUpdateEvents += preEvent; CcdBuildEvents.PrependPostUpdateEvent(postEvent); return; } CcdBuildEvents.OnPreBuildEvents += preEvent; CcdBuildEvents.PrependPostBuildEvent(postEvent); } static void UnregisterBuildMenuEvents(bool isUpdate, PreEvent preEvent, PostEvent postEvent) { if (preEvent == null || postEvent == null) { return; } if (isUpdate) { CcdBuildEvents.OnPreUpdateEvents -= preEvent; CcdBuildEvents.OnPostUpdateEvents -= postEvent; return; } CcdBuildEvents.OnPreBuildEvents -= preEvent; CcdBuildEvents.OnPostBuildEvents -= postEvent; } static PreEvent GetHandlePreBuildDelegate(BuildMenuContext context) { return input => { return Task.FromResult(HandlePreBuild(context, input)); }; } static PostEvent GetHandlePostBuildDelegate(BuildMenuContext context) { return (input, result) => { HandlePostBuild(context, input, result); return Task.FromResult(true); }; } #endif static bool HandlePreBuild(BuildMenuContext context, AddressablesDataBuilderInput builderInput) { if (!context.BuildMenu.OnPrebuild(builderInput)) { Addressables.LogError($"Addressable content pre-build failure : {context.BuildMenu.BuildMenuPath}"); return false; } return true; } static void HandlePostBuild(BuildMenuContext context, AddressablesDataBuilderInput builderInput, AddressablesPlayerBuildResult rst) { if (string.IsNullOrEmpty(rst.Error) && !context.BuildMenu.OnPostbuild(builderInput, rst)) Addressables.LogError($"Addressable content post-build failure : {context.BuildMenu.BuildMenuPath}"); } void OnCleanAll() { OnCleanAddressables(null); OnCleanSBP(); } void OnCleanAddressables(object builder) { AddressableAssetSettings.CleanPlayerContent(builder as IDataBuilder); } void OnCleanSBP() { BuildCache.PurgeCache(true); } void OnPrepareUpdate() { var path = ContentUpdateScript.GetContentStateDataPath(false); if (string.IsNullOrEmpty(path)) { Debug.LogWarning("No path specified for Content State Data file."); return; } if (ResourceManagerConfig.ShouldPathUseWebRequest(path)) path = ContentUpdateScript.DownloadBinFileToTempLocation(path); if (!File.Exists(path)) { if (UnityEditorInternal.InternalEditorUtility.inBatchMode) { Debug.LogWarningFormat("No Content State Data file exists at path: {0}", path); return; } else { bool selectFileManually = EditorUtility.DisplayDialog("Unable to Check for Update Restrictions", $"The addressable_content_state.bin file could " + $"not be found at {path}", "Select .bin file", "Cancel content update"); if (selectFileManually) path = ContentUpdateScript.GetContentStateDataPath(true); else { Debug.LogWarningFormat("No Content State Data file exists at path: {0}. Content update has been cancelled.", path); return; } } } ContentUpdatePreviewWindow.PrepareForContentUpdate(AddressableAssetSettingsDefaultObject.Settings, path); } #if (ENABLE_CCD && UNITY_2019_4_OR_NEWER) async void OnBuildAndRelease() { await AddressableAssetSettings.BuildAndReleasePlayerContent(); } #endif void OnBuildScript(object context) { OnSetActiveBuildScript(context); OnBuildPlayerData(); } void OnBuildPlayerData() { AddressableAssetSettings.BuildPlayerContent(); } void OnSetActiveBuildScript(object context) { AddressableAssetSettingsDefaultObject.Settings.ActivePlayerDataBuilderIndex = (int)context; } void OnSetActivePlayModeScript(object context) { AddressableAssetSettingsDefaultObject.Settings.ActivePlayModeDataBuilderIndex = (int)context; } void OnHierSearchClick() { ProjectConfigData.HierarchicalSearch = !ProjectConfigData.HierarchicalSearch; m_EntryTree.SwapSearchType(); m_EntryTree.Reload(); m_EntryTree.Repaint(); } void CreateProfileDropdown() { var activeProfileName = settings.profileSettings.GetProfileName(settings.activeProfileId); if (string.IsNullOrEmpty(activeProfileName)) { settings.activeProfileId = null; //this will reset it to default. activeProfileName = settings.profileSettings.GetProfileName(settings.activeProfileId); } var profileButton = new GUIContent("Profile: " + activeProfileName, "The active collection of build path settings"); Rect r = GUILayoutUtility.GetRect(profileButton, m_ButtonStyle, GUILayout.Width(115f)); if (EditorGUI.DropdownButton(r, profileButton, FocusType.Passive, EditorStyles.toolbarDropDown)) { //GUIUtility.hotControl = 0; var menu = new GenericMenu(); var nameList = settings.profileSettings.GetAllProfileNames(); foreach (var name in nameList) { menu.AddItem(new GUIContent(name), name == activeProfileName, SetActiveProfile, name); } menu.AddSeparator(string.Empty); menu.AddItem(new GUIContent("Manage Profiles"), false, () => EditorWindow.GetWindow().Show(true)); menu.DropDown(r); } } void SetActiveProfile(object context) { var n = context as string; AddressableAssetSettingsDefaultObject.Settings.activeProfileId = AddressableAssetSettingsDefaultObject.Settings.profileSettings.GetProfileId(n); AddressableAssetUtility.OpenAssetIfUsingVCIntegration(AddressableAssetSettingsDefaultObject.Settings); } bool m_ModificationRegistered; public void OnEnable() { if (AddressableAssetSettingsDefaultObject.Settings == null) return; if (!m_ModificationRegistered) { AddressableAssetSettingsDefaultObject.Settings.OnModification += OnSettingsModification; m_ModificationRegistered = true; } } public void OnDisable() { if (AddressableAssetSettingsDefaultObject.Settings == null) return; if (m_ModificationRegistered) { AddressableAssetSettingsDefaultObject.Settings.OnModification -= OnSettingsModification; m_ModificationRegistered = false; } } public bool OnGUI(Rect pos) { if (settings == null) return false; if (!m_ModificationRegistered) { m_ModificationRegistered = true; settings.OnModification -= OnSettingsModification; //just in case... settings.OnModification += OnSettingsModification; } if (m_EntryTree == null) InitialiseEntryTree(); HandleVerticalResize(pos); var inRectY = pos.height; var searchRect = new Rect(pos.xMin, pos.yMin, pos.width, k_SearchHeight); var treeRect = new Rect(pos.xMin, pos.yMin + k_SearchHeight, pos.width, inRectY - k_SearchHeight); TopToolbar(searchRect); m_EntryTree.OnGUI(treeRect); return m_ResizingVerticalSplitter; } internal AddressableAssetEntryTreeView InitialiseEntryTree() { if (m_TreeState == null) m_TreeState = new TreeViewState(); var headerState = AddressableAssetEntryTreeView.CreateDefaultMultiColumnHeaderState(); if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_Mchs, headerState)) MultiColumnHeaderState.OverwriteSerializedFields(m_Mchs, headerState); m_Mchs = headerState; m_SearchField = new SearchField(); m_EntryTree = new AddressableAssetEntryTreeView(m_TreeState, m_Mchs, this); m_EntryTree.Reload(); return m_EntryTree; } public void Reload() { if (m_EntryTree != null) m_EntryTree.Reload(); } void HandleVerticalResize(Rect position) { m_VerticalSplitterRect.y = (int)(position.yMin + position.height * m_VerticalSplitterPercent); m_VerticalSplitterRect.width = position.width; m_VerticalSplitterRect.height = k_SplitterWidth; EditorGUIUtility.AddCursorRect(m_VerticalSplitterRect, MouseCursor.ResizeVertical); if (Event.current.type == EventType.MouseDown && m_VerticalSplitterRect.Contains(Event.current.mousePosition)) m_ResizingVerticalSplitter = true; if (m_ResizingVerticalSplitter) { var mousePosInRect = Event.current.mousePosition.y - position.yMin; m_VerticalSplitterPercent = Mathf.Clamp(mousePosInRect / position.height, 0.20f, 0.90f); m_VerticalSplitterRect.y = (int)(position.height * m_VerticalSplitterPercent + position.yMin); if (Event.current.type == EventType.MouseUp) { m_ResizingVerticalSplitter = false; } } else m_VerticalSplitterPercent = Mathf.Clamp(m_VerticalSplitterPercent, 0.20f, 0.90f); } } }