 7502018d20
			
		
	
	
		7502018d20
		
	
	
	
	
		
			
			There is an asset in the store I grabbed. the coding is WAY above my head, I got about half of it and integrated and adapted what I can to it. im going as far as I can with it and ill come back in a few month when I understand t better.
		
			
				
	
	
		
			979 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			979 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections;
 | |
| using System.Collections.Generic;
 | |
| using System.Diagnostics;
 | |
| using System.IO;
 | |
| using System.Linq;
 | |
| using System.Reflection;
 | |
| using System.Threading.Tasks;
 | |
| using UnityEditor.AddressableAssets.Settings;
 | |
| using UnityEditor.IMGUI.Controls;
 | |
| using UnityEditor.SceneManagement;
 | |
| using UnityEngine;
 | |
| using UnityEngine.AddressableAssets;
 | |
| using UnityEngine.Assertions;
 | |
| using UnityEngine.U2D;
 | |
| using Debug = UnityEngine.Debug;
 | |
| 
 | |
| namespace UnityEditor.AddressableAssets.GUI
 | |
| {
 | |
|     using Object = UnityEngine.Object;
 | |
| 
 | |
|     [CustomPropertyDrawer(typeof(AssetReference), true)]
 | |
|     class AssetReferenceDrawer : PropertyDrawer
 | |
|     {
 | |
|         public string newGuid;
 | |
|         public string newGuidPropertyPath;
 | |
|         internal string m_AssetName;
 | |
|         internal Rect assetDropDownRect;
 | |
|         internal const string noAssetString = "None (AddressableAsset)";
 | |
|         internal const string forceAddressableString = "Make Addressable - ";
 | |
|         internal AssetReference m_AssetRefObject;
 | |
|         internal GUIContent m_label;
 | |
|         internal bool m_ReferencesSame = true;
 | |
|         internal List<AssetReferenceUIRestrictionSurrogate> m_Restrictions = null;
 | |
|         SubassetPopup m_SubassetPopup;
 | |
|         private Texture2D m_CaretTexture = null;
 | |
|         internal const string k_FieldControlName = "AssetReferenceField";
 | |
| 
 | |
|         internal List<AssetReferenceUIRestrictionSurrogate> Restrictions => m_Restrictions;
 | |
|         /// <summary>
 | |
|         /// Validates that the referenced asset allowable for this asset reference.
 | |
|         /// </summary>
 | |
|         /// <param name="path">The path to the asset in question.</param>
 | |
|         /// <returns>Whether the referenced asset is valid.</returns>
 | |
|         public bool ValidateAsset(string path)
 | |
|         {
 | |
|             return AssetReferenceDrawerUtilities.ValidateAsset(m_AssetRefObject, Restrictions, path);
 | |
|         }
 | |
| 
 | |
|         internal bool ValidateAsset(IReferenceEntryData entryData)
 | |
|         {
 | |
|             return AssetReferenceDrawerUtilities.ValidateAsset(m_AssetRefObject, Restrictions, entryData);
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|         * The AssetReference class is not a Unity.Object or a base type so a lot of SerializedProperty's
 | |
|         * functionalities doesn't work, because type-checking is done in the API to check whether an operation
 | |
|         * can be executed or not. In the engine, one of the way changes are detected is when a new value is set
 | |
|         * through the class' value setters (see MarkPropertyModified() in SerializedProperty.cpp). So in order to
 | |
|         * trigger a change, we modify a sub-property instead.
 | |
|         */
 | |
|         void TriggerOnValidate(SerializedProperty property)
 | |
|         {
 | |
|             if (property != null)
 | |
|             {
 | |
|                 property.serializedObject.ApplyModifiedProperties();
 | |
|                 property.serializedObject.Update();
 | |
| 
 | |
|                 // This is actually what triggers the OnValidate() method.
 | |
|                 // Since 'm_EditorAssetChanged' is of a recognized type and is a sub-property of AssetReference, both
 | |
|                 // are flagged as changed and OnValidate() is called.
 | |
|                 property.FindPropertyRelative("m_EditorAssetChanged").boolValue = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
 | |
|         {
 | |
|             if (property == null || label == null)
 | |
|             {
 | |
|                 Debug.LogError("Error rendering drawer for AssetReference property.");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             string labelText = label.text;
 | |
|             m_ReferencesSame = true;
 | |
|             m_AssetRefObject = property.GetActualObjectForSerializedProperty<AssetReference>(fieldInfo, ref labelText);
 | |
|             labelText = ObjectNames.NicifyVariableName(labelText);
 | |
|             if (labelText != label.text || string.IsNullOrEmpty(label.text))
 | |
|             {
 | |
|                 label = new GUIContent(labelText, label.tooltip);
 | |
|             }
 | |
|             m_label = label;
 | |
| 
 | |
|             if (m_AssetRefObject == null)
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             EditorGUI.BeginProperty(position, label, property);
 | |
|             if (m_Restrictions == null)
 | |
|                 m_Restrictions = AssetReferenceDrawerUtilities.GatherFilters(property);
 | |
|             string guid = m_AssetRefObject.AssetGUID;
 | |
|             var aaSettings = AddressableAssetSettingsDefaultObject.Settings;
 | |
| 
 | |
|             var isNotAddressable = ApplySelectionChanges(property, aaSettings, ref guid);
 | |
| 
 | |
|             assetDropDownRect = EditorGUI.PrefixLabel(position, label);
 | |
|             var nameToUse = AssetReferenceDrawerUtilities.GetNameForAsset(ref m_ReferencesSame, property, isNotAddressable, fieldInfo, m_label.text);
 | |
| 
 | |
|             DrawSubassets(property);
 | |
| 
 | |
|             bool isDragging = Event.current.type == EventType.DragUpdated && position.Contains(Event.current.mousePosition);
 | |
|             bool isDropping = Event.current.type == EventType.DragPerform && position.Contains(Event.current.mousePosition);
 | |
| 
 | |
|             DrawControl(property, nameToUse, isNotAddressable, guid);
 | |
| 
 | |
|             HandleDragAndDrop(property, isDragging, isDropping, guid);
 | |
| 
 | |
|             EditorGUI.EndProperty();
 | |
|         }
 | |
| 
 | |
|         private void DrawSubassets(SerializedProperty property)
 | |
|         {
 | |
|             if (m_AssetRefObject.editorAsset != null && m_ReferencesSame)
 | |
|             {
 | |
|                 List<Object> subAssets = null;
 | |
|                 bool hasSubAssets = !string.IsNullOrEmpty(m_AssetRefObject.SubObjectName);
 | |
|                 if (!hasSubAssets)
 | |
|                 {
 | |
|                     subAssets = AssetReferenceDrawerUtilities.GetSubAssetsList(m_AssetRefObject);
 | |
|                     hasSubAssets = subAssets.Count > 1;
 | |
|                 }
 | |
| 
 | |
|                 if (hasSubAssets)
 | |
|                 {
 | |
|                     assetDropDownRect = DrawSubAssetsControl(property, subAssets);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         bool ApplySelectionChanges(SerializedProperty property, AddressableAssetSettings aaSettings, ref string guid)
 | |
|         {
 | |
|             var checkToForceAddressable = string.Empty;
 | |
|             if (!string.IsNullOrEmpty(newGuid) && newGuidPropertyPath == property.propertyPath)
 | |
|             {
 | |
|                 if (newGuid == noAssetString)
 | |
|                 {
 | |
|                     if (AssetReferenceDrawerUtilities.SetObject(ref m_AssetRefObject, ref m_ReferencesSame, property, null, fieldInfo, m_label.text, out guid))
 | |
|                         TriggerOnValidate(property);
 | |
|                     newGuid = string.Empty;
 | |
|                 }
 | |
|                 else if (newGuid == forceAddressableString)
 | |
|                 {
 | |
|                     checkToForceAddressable = guid;
 | |
|                     newGuid = string.Empty;
 | |
|                 }
 | |
|                 else if (guid != newGuid)
 | |
|                 {
 | |
|                     if (AssetReferenceDrawerUtilities.SetObject(ref m_AssetRefObject, ref m_ReferencesSame, property, AssetDatabase.LoadAssetAtPath<Object>(AssetDatabase.GUIDToAssetPath(newGuid)), fieldInfo, m_label.text, out guid))
 | |
|                     {
 | |
|                         checkToForceAddressable = newGuid;
 | |
|                         TriggerOnValidate(property);
 | |
|                     }
 | |
|                     newGuid = string.Empty;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             bool isNotAddressable = false;
 | |
|             m_AssetName = noAssetString;
 | |
|             if (aaSettings != null && !string.IsNullOrEmpty(guid))
 | |
|             {
 | |
|                 isNotAddressable = AssetReferenceDrawerUtilities.CheckForNewEntry(ref m_AssetName, aaSettings, guid, checkToForceAddressable);
 | |
|             }
 | |
| 
 | |
|             return isNotAddressable;
 | |
|         }
 | |
| 
 | |
|         private void DrawControl(SerializedProperty property, string nameToUse, bool isNotAddressable, string guid)
 | |
|         {
 | |
|             float pickerWidth = 20f;
 | |
|             Rect pickerRect = assetDropDownRect;
 | |
|             pickerRect.width = pickerWidth;
 | |
|             pickerRect.x = assetDropDownRect.xMax - pickerWidth;
 | |
| 
 | |
|             bool isPickerPressed = Event.current.type == EventType.MouseDown && Event.current.button == 0 && pickerRect.Contains(Event.current.mousePosition);
 | |
|             bool isEnterKeyPressed = Event.current.type == EventType.KeyDown && Event.current.isKey && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return);
 | |
| 
 | |
|             var asset = m_AssetRefObject?.editorAsset;
 | |
|             if (asset != null && m_ReferencesSame)
 | |
|             {
 | |
|                 float iconHeight = EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing * 3;
 | |
|                 Vector2 iconSize = EditorGUIUtility.GetIconSize();
 | |
|                 EditorGUIUtility.SetIconSize(new Vector2(iconHeight, iconHeight));
 | |
|                 string assetPath = AssetDatabase.GUIDToAssetPath(m_AssetRefObject.AssetGUID);
 | |
|                 Texture2D assetIcon = AssetDatabase.GetCachedIcon(assetPath) as Texture2D;
 | |
| 
 | |
|                 UnityEngine.GUI.SetNextControlName(k_FieldControlName);
 | |
|                 if (EditorGUI.DropdownButton(assetDropDownRect, new GUIContent(nameToUse, assetIcon), FocusType.Keyboard, EditorStyles.objectField))
 | |
|                 {
 | |
|                     if (Event.current.clickCount == 1)
 | |
|                     {
 | |
|                         UnityEngine.GUI.FocusControl(k_FieldControlName);
 | |
|                         EditorGUIUtility.PingObject(asset);
 | |
|                     }
 | |
|                     if (Event.current.clickCount == 2)
 | |
|                     {
 | |
|                         AssetDatabase.OpenAsset(asset);
 | |
|                         GUIUtility.ExitGUI();
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 EditorGUIUtility.SetIconSize(iconSize);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 UnityEngine.GUI.SetNextControlName(k_FieldControlName);
 | |
|                 if (EditorGUI.DropdownButton(assetDropDownRect, new GUIContent(nameToUse), FocusType.Keyboard, EditorStyles.objectField))
 | |
|                     UnityEngine.GUI.FocusControl(k_FieldControlName);
 | |
|             }
 | |
| 
 | |
|             DrawCaret(pickerRect);
 | |
| 
 | |
|             bool enterKeyRequestsPopup = isEnterKeyPressed && (k_FieldControlName == UnityEngine.GUI.GetNameOfFocusedControl());
 | |
|             if (isPickerPressed || enterKeyRequestsPopup)
 | |
|             {
 | |
|                 newGuidPropertyPath = property.propertyPath;
 | |
|                 var nonAddressedOption = isNotAddressable ? m_AssetName : string.Empty;
 | |
|                 PopupWindow.Show(assetDropDownRect, new AssetReferencePopup(this, guid, nonAddressedOption, enterKeyRequestsPopup));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void DrawCaret(Rect pickerRect)
 | |
|         {
 | |
|             if (m_CaretTexture == null)
 | |
|             {
 | |
|                 string caretIconPath = EditorGUIUtility.isProSkin
 | |
|                     ? @"Packages\com.unity.addressables\Editor\Icons\PickerDropArrow-Pro.png"
 | |
|                     : @"Packages\com.unity.addressables\Editor\Icons\PickerDropArrow-Personal.png";
 | |
| 
 | |
|                 if (File.Exists(caretIconPath))
 | |
|                 {
 | |
|                     m_CaretTexture = (Texture2D)AssetDatabase.LoadAssetAtPath(caretIconPath, typeof(Texture2D));
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (m_CaretTexture != null)
 | |
|             {
 | |
|                 UnityEngine.GUI.DrawTexture(pickerRect, m_CaretTexture, ScaleMode.ScaleToFit);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void HandleDragAndDrop(SerializedProperty property, bool isDragging, bool isDropping, string guid)
 | |
|         {
 | |
|             var aaSettings = AddressableAssetSettingsDefaultObject.Settings;
 | |
|             //During the drag, doing a light check on asset validity.  The in-depth check happens during a drop, and should include a log if it fails.
 | |
|             var rejectedDrag = false;
 | |
|             if (isDragging)
 | |
|             {
 | |
|                 if (aaSettings == null)
 | |
|                     rejectedDrag = true;
 | |
|                 else
 | |
|                 {
 | |
|                     var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
 | |
|                     rejectedDrag = AssetReferenceDrawerUtilities.ValidateDrag(m_AssetRefObject, Restrictions, aaEntries, DragAndDrop.objectReferences, DragAndDrop.paths);
 | |
|                 }
 | |
|                 DragAndDrop.visualMode = rejectedDrag ? DragAndDropVisualMode.Rejected : DragAndDropVisualMode.Copy;
 | |
|             }
 | |
| 
 | |
|             if (!rejectedDrag && isDropping)
 | |
|             {
 | |
|                 var aaEntries = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
 | |
|                 if (aaEntries != null)
 | |
|                 {
 | |
|                     if (aaEntries.Count == 1)
 | |
|                     {
 | |
|                         var item = aaEntries[0];
 | |
|                         if (item.entry != null)
 | |
|                         {
 | |
|                             if (item.entry.IsInResources)
 | |
|                                 Addressables.LogWarning("Cannot use an AssetReference on an asset in Resources. Move asset out of Resources first.");
 | |
|                             else
 | |
|                             {
 | |
|                                 if (AssetReferenceDrawerUtilities.SetObject(ref m_AssetRefObject, ref m_ReferencesSame, property, item.entry.TargetAsset, fieldInfo, m_label.text, out guid))
 | |
|                                     TriggerOnValidate(property);
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     if (DragAndDrop.paths != null && DragAndDrop.paths.Length == 1)
 | |
|                     {
 | |
|                         var path = DragAndDrop.paths[0];
 | |
|                         DragAndDropNotFromAddressableGroupWindow(path, guid, property, aaSettings);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal void DragAndDropNotFromAddressableGroupWindow(string path, string guid, SerializedProperty property, AddressableAssetSettings aaSettings)
 | |
|         {
 | |
|             if (AddressableAssetUtility.IsInResources(path))
 | |
|                 Addressables.LogWarning("Cannot use an AssetReference on an asset in Resources. Move asset out of Resources first. ");
 | |
|             else if (!AddressableAssetUtility.IsPathValidForEntry(path))
 | |
|                 Addressables.LogWarning("Dragged asset is not valid as an Asset Reference. " + path);
 | |
|             else
 | |
|             {
 | |
|                 Object obj;
 | |
|                 if (DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Length == 1)
 | |
|                     obj = DragAndDrop.objectReferences[0];
 | |
|                 else
 | |
|                     obj = AssetDatabase.LoadAssetAtPath<Object>(path);
 | |
| 
 | |
|                 if (AssetReferenceDrawerUtilities.SetObject(ref m_AssetRefObject, ref m_ReferencesSame, property, obj, fieldInfo, m_label.text, out guid))
 | |
|                 {
 | |
|                     TriggerOnValidate(property);
 | |
|                     aaSettings = AddressableAssetSettingsDefaultObject.GetSettings(true);
 | |
|                     var entry = aaSettings.FindAssetEntry(guid);
 | |
|                     if (entry == null && !string.IsNullOrEmpty(guid))
 | |
|                     {
 | |
|                         string assetName;
 | |
|                         if (!aaSettings.IsAssetPathInAddressableDirectory(path, out assetName))
 | |
|                         {
 | |
|                             aaSettings.CreateOrMoveEntry(guid, aaSettings.DefaultGroup);
 | |
|                             newGuid = guid;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private Rect DrawSubAssetsControl(SerializedProperty property, List<Object> subAssets)
 | |
|         {
 | |
|             assetDropDownRect = new Rect(assetDropDownRect.position, new Vector2(assetDropDownRect.width / 2, assetDropDownRect.height));
 | |
|             var objRect = new Rect(assetDropDownRect.xMax, assetDropDownRect.y, assetDropDownRect.width, assetDropDownRect.height);
 | |
|             float pickerWidth = 20f;
 | |
|             Rect pickerRect = objRect;
 | |
|             pickerRect.width = pickerWidth;
 | |
|             pickerRect.x = objRect.xMax - pickerWidth;
 | |
|             bool multipleSubassets = false;
 | |
| 
 | |
|             // Check if targetObjects have multiple different selected
 | |
|             if (property.serializedObject.targetObjects.Length > 1)
 | |
|                 multipleSubassets = AssetReferenceDrawerUtilities.CheckTargetObjectsSubassetsAreDifferent(property, m_AssetRefObject.SubObjectName, fieldInfo, m_label.text);
 | |
| 
 | |
|             bool isPickerPressed = Event.current.type == EventType.MouseDown && Event.current.button == 0 && pickerRect.Contains(Event.current.mousePosition);
 | |
|             if (isPickerPressed)
 | |
|             {
 | |
|                 // Do custom popup with scroll to pick subasset
 | |
|                 if (m_SubassetPopup == null || m_SubassetPopup.m_property != property)
 | |
|                 {
 | |
|                     m_SubassetPopup = CreateSubAssetPopup(property, subAssets ?? AssetReferenceDrawerUtilities.GetSubAssetsList(m_AssetRefObject));
 | |
|                 }
 | |
| 
 | |
|                 PopupWindow.Show(objRect, m_SubassetPopup);
 | |
|             }
 | |
| 
 | |
|             if (m_SubassetPopup != null && m_SubassetPopup.SelectionChanged)
 | |
|             {
 | |
|                 m_SubassetPopup.UpdateSubAssets();
 | |
|             }
 | |
| 
 | |
|             // Show selected name
 | |
|             GUIContent nameSelected = new GUIContent("--");
 | |
|             if (!multipleSubassets)
 | |
|                 nameSelected.text = AssetReferenceDrawerUtilities.FormatName(m_AssetRefObject.SubObjectName);
 | |
|             UnityEngine.GUI.Box(objRect, nameSelected, EditorStyles.objectField);
 | |
| 
 | |
|             // Draw picker arrow
 | |
|             DrawCaret(pickerRect);
 | |
| 
 | |
|             return assetDropDownRect;
 | |
|         }
 | |
| 
 | |
|         internal void GetSelectedSubassetIndex(List<Object> subAssets, out int selIndex, out string[] objNames)
 | |
|         {
 | |
|             var subAssetNames = subAssets.Select(sa => sa == null ? "<none>" : $"{AssetReferenceDrawerUtilities.FormatName(sa.name)}:{sa.GetType()}").ToList();
 | |
|             objNames = subAssetNames.ToArray();
 | |
| 
 | |
|             selIndex = subAssetNames.IndexOf($"{m_AssetRefObject.SubObjectName}:{m_AssetRefObject.SubOjbectType}");
 | |
|             if (selIndex == -1)
 | |
|                 selIndex = 0;
 | |
|         }
 | |
| 
 | |
|         SubassetPopup CreateSubAssetPopup(SerializedProperty property, List<Object> subAssets)
 | |
|         {
 | |
|             GetSelectedSubassetIndex(subAssets, out int selIndex, out string[] objNames);
 | |
|             return new SubassetPopup(selIndex, objNames, subAssets, property, this);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class SubassetPopup : PopupWindowContent
 | |
|     {
 | |
|         internal int SelectedIndex = 0;
 | |
|         internal SerializedProperty m_property;
 | |
|         private string[] m_objNames;
 | |
|         private List<Object> m_subAssets;
 | |
|         private AssetReferenceDrawer m_drawer;
 | |
|         private Vector2 m_scrollPosition;
 | |
|         bool selectionChanged = false;
 | |
| 
 | |
|         internal SubassetPopup(int selIndex, string[] objNames, List<Object> subAssets, SerializedProperty property, AssetReferenceDrawer drawer)
 | |
|         {
 | |
|             SelectedIndex = selIndex;
 | |
|             m_objNames = objNames;
 | |
|             m_property = property;
 | |
|             m_drawer = drawer;
 | |
|             m_subAssets = subAssets;
 | |
|         }
 | |
| 
 | |
|         public bool SelectionChanged => selectionChanged;
 | |
|         public void UpdateSubAssets()
 | |
|         {
 | |
|             if (selectionChanged)
 | |
|             {
 | |
|                 if (!AssetReferenceDrawerUtilities.SetSubAssets(m_property, m_subAssets[SelectedIndex], m_drawer.fieldInfo, m_drawer.m_label.text))
 | |
|                 {
 | |
|                     Debug.LogError("Unable to set all of the objects selected subassets");
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     m_property.serializedObject.ApplyModifiedProperties();
 | |
|                     m_property.serializedObject.Update();
 | |
|                     m_property.FindPropertyRelative("m_EditorAssetChanged").boolValue = false;
 | |
|                 }
 | |
|                 selectionChanged = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public override void OnGUI(Rect rect)
 | |
|         {
 | |
|             var buttonStyle = new GUIStyle();
 | |
|             buttonStyle.fontStyle = FontStyle.Normal;
 | |
|             buttonStyle.fontSize = 12;
 | |
|             buttonStyle.contentOffset = new Vector2(10, 0);
 | |
|             buttonStyle.normal.textColor = Color.white;
 | |
| 
 | |
|             EditorGUILayout.BeginVertical();
 | |
|             m_scrollPosition = EditorGUILayout.BeginScrollView(m_scrollPosition, GUILayout.Width(rect.width), GUILayout.Height(rect.height));
 | |
|             for (int i = 0; i < m_objNames.Length; i++)
 | |
|             {
 | |
|                 if (GUILayout.Button(m_objNames[i], buttonStyle))
 | |
|                 {
 | |
|                     if (SelectedIndex != i)
 | |
|                     {
 | |
|                         SelectedIndex = i;
 | |
|                         selectionChanged = true;
 | |
|                     }
 | |
|                     PopupWindow.focusedWindow.Close();
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             EditorGUILayout.EndScrollView();
 | |
|             EditorGUILayout.EndVertical();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     class AssetReferencePopup : PopupWindowContent
 | |
|     {
 | |
|         AssetReferenceTreeView m_Tree;
 | |
|         TreeViewState m_TreeState;
 | |
|         bool m_ShouldClose;
 | |
| 
 | |
|         void ForceClose()
 | |
|         {
 | |
|             m_ShouldClose = true;
 | |
|         }
 | |
| 
 | |
|         string m_CurrentName = string.Empty;
 | |
|         AssetReferenceDrawer m_Drawer;
 | |
|         string m_GUID;
 | |
|         string m_NonAddressedAsset;
 | |
| 
 | |
|         SearchField m_SearchField;
 | |
|         bool m_OpenedByEnterKey;
 | |
| 
 | |
|         internal AssetReferencePopup(AssetReferenceDrawer drawer, string guid, string nonAddressedAsset, bool openedByEnterKey)
 | |
|         {
 | |
|             m_Drawer = drawer;
 | |
|             m_GUID = guid;
 | |
|             m_NonAddressedAsset = nonAddressedAsset;
 | |
|             m_SearchField = new SearchField();
 | |
|             m_ShouldClose = false;
 | |
|             m_OpenedByEnterKey = openedByEnterKey;
 | |
|         }
 | |
| 
 | |
|         public override void OnOpen()
 | |
|         {
 | |
|             m_SearchField.SetFocus();
 | |
|             base.OnOpen();
 | |
|         }
 | |
| 
 | |
|         public override Vector2 GetWindowSize()
 | |
|         {
 | |
|             Vector2 result = base.GetWindowSize();
 | |
|             result.x = m_Drawer.assetDropDownRect.width;
 | |
|             return result;
 | |
|         }
 | |
| 
 | |
|         public override void OnGUI(Rect rect)
 | |
|         {
 | |
|             int border = 4;
 | |
|             int topPadding = 12;
 | |
|             int searchHeight = 20;
 | |
|             var searchRect = new Rect(border, topPadding, rect.width - border * 2, searchHeight);
 | |
|             var remainTop = topPadding + searchHeight + border;
 | |
|             var remainingRect = new Rect(border, topPadding + searchHeight + border, rect.width - border * 2, rect.height - remainTop - border);
 | |
| 
 | |
|             bool isEnterKeyPressed = Event.current.type == EventType.KeyDown && Event.current.isKey && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return);
 | |
| 
 | |
|             m_CurrentName = m_SearchField.OnGUI(searchRect, m_CurrentName);
 | |
| 
 | |
|             if (m_Tree == null)
 | |
|             {
 | |
|                 if (m_TreeState == null)
 | |
|                     m_TreeState = new TreeViewState();
 | |
|                 m_Tree = new AssetReferenceTreeView(m_TreeState, m_Drawer, this, m_GUID, m_NonAddressedAsset);
 | |
|                 m_Tree.Reload();
 | |
|             }
 | |
| 
 | |
|             m_Tree.searchString = m_CurrentName;
 | |
|             m_Tree.OnGUI(remainingRect);
 | |
| 
 | |
|             if (m_ShouldClose || (isEnterKeyPressed && m_OpenedByEnterKey))
 | |
|             {
 | |
|                 GUIUtility.hotControl = 0;
 | |
|                 editorWindow.Close();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         sealed class AssetRefTreeViewItem : TreeViewItem
 | |
|         {
 | |
|             public string AssetPath;
 | |
| 
 | |
|             private string m_Guid;
 | |
|             public string Guid
 | |
|             {
 | |
|                 get
 | |
|                 {
 | |
|                     if (string.IsNullOrEmpty(m_Guid))
 | |
|                         m_Guid = AssetDatabase.AssetPathToGUID(AssetPath);
 | |
|                     return m_Guid;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public AssetRefTreeViewItem(int id, int depth, string displayName, string path)
 | |
|                 : base(id, depth, displayName)
 | |
|             {
 | |
|                 AssetPath = path;
 | |
|                 icon = AssetDatabase.GetCachedIcon(path) as Texture2D;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal class AssetReferenceTreeView : TreeView
 | |
|         {
 | |
|             AssetReferenceDrawer m_Drawer;
 | |
|             AssetReferencePopup m_Popup;
 | |
|             string m_GUID;
 | |
|             string m_NonAddressedAsset;
 | |
|             Texture2D m_WarningIcon;
 | |
| 
 | |
|             public AssetReferenceTreeView(TreeViewState state, AssetReferenceDrawer drawer, AssetReferencePopup popup, string guid, string nonAddressedAsset)
 | |
|                 : base(state)
 | |
|             {
 | |
|                 m_Drawer = drawer;
 | |
|                 m_Popup = popup;
 | |
|                 showBorder = true;
 | |
|                 showAlternatingRowBackgrounds = true;
 | |
|                 m_GUID = guid;
 | |
|                 m_NonAddressedAsset = nonAddressedAsset;
 | |
|                 m_WarningIcon = EditorGUIUtility.FindTexture("console.warnicon");
 | |
|             }
 | |
| 
 | |
|             protected override bool CanMultiSelect(TreeViewItem item)
 | |
|             {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             protected override void SelectionChanged(IList<int> selectedIds)
 | |
|             {
 | |
|                 if (selectedIds != null && selectedIds.Count == 1)
 | |
|                 {
 | |
|                     var assetRefItem = FindItem(selectedIds[0], rootItem) as AssetRefTreeViewItem;
 | |
|                     if (assetRefItem != null && !string.IsNullOrEmpty(assetRefItem.AssetPath))
 | |
|                     {
 | |
|                         m_Drawer.newGuid = assetRefItem.Guid;
 | |
|                         if (string.IsNullOrEmpty(m_Drawer.newGuid))
 | |
|                             m_Drawer.newGuid = assetRefItem.AssetPath;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         m_Drawer.newGuid = AssetReferenceDrawer.noAssetString;
 | |
|                     }
 | |
| 
 | |
|                     m_Popup.ForceClose();
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
 | |
|             {
 | |
|                 if (string.IsNullOrEmpty(searchString))
 | |
|                 {
 | |
|                     return base.BuildRows(root);
 | |
|                 }
 | |
| 
 | |
|                 List<TreeViewItem> rows = new List<TreeViewItem>();
 | |
| 
 | |
|                 foreach (var child in rootItem.children)
 | |
|                 {
 | |
|                     if (child.displayName.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0)
 | |
|                         rows.Add(child);
 | |
|                 }
 | |
| 
 | |
|                 return rows;
 | |
|             }
 | |
| 
 | |
|             protected override TreeViewItem BuildRoot()
 | |
|             {
 | |
|                 var root = new TreeViewItem(-1, -1);
 | |
| 
 | |
|                 var aaSettings = AddressableAssetSettingsDefaultObject.Settings;
 | |
|                 if (aaSettings == null)
 | |
|                 {
 | |
|                     var message = "Use 'Window->Addressables' to initialize.";
 | |
|                     root.AddChild(new AssetRefTreeViewItem(message.GetHashCode(), 0, message, string.Empty));
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     if (!string.IsNullOrEmpty(m_NonAddressedAsset))
 | |
|                     {
 | |
|                         var item = new AssetRefTreeViewItem(m_NonAddressedAsset.GetHashCode(), 0,
 | |
|                             AssetReferenceDrawer.forceAddressableString + m_NonAddressedAsset, AssetReferenceDrawer.forceAddressableString);
 | |
|                         item.icon = m_WarningIcon;
 | |
|                         root.AddChild(item);
 | |
|                     }
 | |
| 
 | |
|                     root.AddChild(new AssetRefTreeViewItem(AssetReferenceDrawer.noAssetString.GetHashCode(), 0, AssetReferenceDrawer.noAssetString, string.Empty));
 | |
|                     var allAssets = new List<IReferenceEntryData>();
 | |
|                     aaSettings.GatherAllAssetReferenceDrawableEntries(allAssets);
 | |
|                     foreach (var entry in allAssets)
 | |
|                     {
 | |
|                         if (!entry.IsInResources &&
 | |
|                             m_Drawer.ValidateAsset(entry))
 | |
|                         {
 | |
|                             var child = new AssetRefTreeViewItem(entry.AssetPath.GetHashCode(), 0, entry.address, entry.AssetPath);
 | |
|                             root.AddChild(child);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 return root;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Used to manipulate data from a serialized property.
 | |
|     /// </summary>
 | |
|     public static class SerializedPropertyExtensions
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Used to extract the target object from a serialized property.
 | |
|         /// </summary>
 | |
|         /// <typeparam name="T">The type of the object to extract.</typeparam>
 | |
|         /// <param name="property">The property containing the object.</param>
 | |
|         /// <param name="field">The field data.</param>
 | |
|         /// <param name="label">The label name.</param>
 | |
|         /// <returns>Returns the target object type.</returns>
 | |
|         public static T GetActualObjectForSerializedProperty<T>(this SerializedProperty property, FieldInfo field, ref string label)
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 if (property == null || field == null)
 | |
|                     return default(T);
 | |
|                 var serializedObject = property.serializedObject;
 | |
|                 if (serializedObject == null)
 | |
|                 {
 | |
|                     return default(T);
 | |
|                 }
 | |
| 
 | |
|                 var targetObject = serializedObject.targetObject;
 | |
| 
 | |
|                 if (property.depth > 0)
 | |
|                 {
 | |
|                     var slicedName = property.propertyPath.Split('.').ToList();
 | |
|                     List<int> arrayCounts = new List<int>();
 | |
|                     for (int index = 0; index < slicedName.Count; index++)
 | |
|                     {
 | |
|                         arrayCounts.Add(-1);
 | |
|                         var currName = slicedName[index];
 | |
|                         if (currName.EndsWith("]"))
 | |
|                         {
 | |
|                             var arraySlice = currName.Split('[', ']');
 | |
|                             if (arraySlice.Length >= 2)
 | |
|                             {
 | |
|                                 arrayCounts[index - 2] = Convert.ToInt32(arraySlice[1]);
 | |
|                                 slicedName[index] = string.Empty;
 | |
|                                 slicedName[index - 1] = string.Empty;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     while (string.IsNullOrEmpty(slicedName.Last()))
 | |
|                     {
 | |
|                         int i = slicedName.Count - 1;
 | |
|                         slicedName.RemoveAt(i);
 | |
|                         arrayCounts.RemoveAt(i);
 | |
|                     }
 | |
| 
 | |
|                     if (property.propertyPath.EndsWith("]"))
 | |
|                     {
 | |
|                         var slice = property.propertyPath.Split('[', ']');
 | |
|                         if (slice.Length >= 2)
 | |
|                             label = "Element " + slice[slice.Length - 2];
 | |
|                     }
 | |
| 
 | |
|                     return DescendHierarchy<T>(targetObject, slicedName, arrayCounts, 0);
 | |
|                 }
 | |
| 
 | |
|                 var obj = field.GetValue(targetObject);
 | |
|                 return (T)obj;
 | |
|             }
 | |
|             catch
 | |
|             {
 | |
|                 return default(T);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static T DescendHierarchy<T>(object targetObject, List<string> splitName, List<int> splitCounts, int depth)
 | |
|         {
 | |
|             if (depth >= splitName.Count)
 | |
|                 return default(T);
 | |
| 
 | |
|             var currName = splitName[depth];
 | |
| 
 | |
|             if (string.IsNullOrEmpty(currName))
 | |
|                 return DescendHierarchy<T>(targetObject, splitName, splitCounts, depth + 1);
 | |
| 
 | |
|             int arrayIndex = splitCounts[depth];
 | |
| 
 | |
|             var newField = targetObject.GetType().GetField(currName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
 | |
| 
 | |
|             if (newField == null)
 | |
|             {
 | |
|                 Type baseType = targetObject.GetType().BaseType;
 | |
|                 while (baseType != null && newField == null)
 | |
|                 {
 | |
|                     newField = baseType.GetField(currName,
 | |
|                         BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
 | |
|                     baseType = baseType.BaseType;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             var newObj = newField.GetValue(targetObject);
 | |
|             if (depth == splitName.Count - 1)
 | |
|             {
 | |
|                 T actualObject = default(T);
 | |
|                 if (arrayIndex >= 0)
 | |
|                 {
 | |
|                     if (newObj.GetType().IsArray && ((System.Array)newObj).Length > arrayIndex)
 | |
|                         actualObject = (T)((System.Array)newObj).GetValue(arrayIndex);
 | |
| 
 | |
|                     var newObjList = newObj as IList;
 | |
|                     if (newObjList != null && newObjList.Count > arrayIndex)
 | |
|                     {
 | |
|                         actualObject = (T)newObjList[arrayIndex];
 | |
| 
 | |
|                         //if (actualObject == null)
 | |
|                         //    actualObject = new T();
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     actualObject = (T)newObj;
 | |
|                 }
 | |
| 
 | |
|                 return actualObject;
 | |
|             }
 | |
|             else if (arrayIndex >= 0)
 | |
|             {
 | |
|                 if (newObj is IList)
 | |
|                 {
 | |
|                     IList list = (IList)newObj;
 | |
|                     newObj = list[arrayIndex];
 | |
|                 }
 | |
|                 else if (newObj is System.Array)
 | |
|                 {
 | |
|                     System.Array a = (System.Array)newObj;
 | |
|                     newObj = a.GetValue(arrayIndex);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return DescendHierarchy<T>(newObj, splitName, splitCounts, depth + 1);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Used to restrict a class to only allow items with specific labels.
 | |
|     /// </summary>
 | |
|     [AttributeUsage(AttributeTargets.Class)]
 | |
|     public class AssetReferenceSurrogateAttribute : Attribute
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// The type of the attribute.
 | |
|         /// </summary>
 | |
|         public Type TargetType;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Construct a new AssetReferenceSurrogateAttribute.
 | |
|         /// </summary>
 | |
|         /// <param name="type">The Type of the class in question.</param>
 | |
|         public AssetReferenceSurrogateAttribute(Type type)
 | |
|         {
 | |
|             TargetType = type;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Surrogate to AssetReferenceUIRestriction.
 | |
|     /// This surrogate class provides the editor-side implementation of AssetReferenceUIRestriction attribute
 | |
|     /// Used to restrict an AssetReference field or property to only allow items with specific labels. This is only enforced through the UI.
 | |
|     /// </summary>
 | |
|     [AssetReferenceSurrogate(typeof(AssetReferenceUIRestriction))]
 | |
|     public class AssetReferenceUIRestrictionSurrogate : AssetReferenceUIRestriction
 | |
|     {
 | |
|         AssetReferenceUIRestriction data;
 | |
|         /// <summary>
 | |
|         /// Sets the AssetReferenceUIRestriction for this surrogate
 | |
|         /// </summary>
 | |
|         /// <param name="initData">To initialize AssetReferenceUIRestriction for surrogate</param>
 | |
|         public virtual void Init(AssetReferenceUIRestriction initData)
 | |
|         {
 | |
|             data = initData;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Validates the referenced asset allowable for this asset reference.
 | |
|         /// </summary>
 | |
|         /// <param name="obj">The Object to validate.</param>
 | |
|         /// <returns>Whether the referenced asset is valid.</returns>
 | |
|         public override bool ValidateAsset(Object obj)
 | |
|         {
 | |
|             return data.ValidateAsset(obj);
 | |
|         }
 | |
| 
 | |
|         internal virtual bool ValidateAsset(IReferenceEntryData entryData)
 | |
|         {
 | |
|             return data.ValidateAsset(entryData?.AssetPath);
 | |
|         }
 | |
|     }
 | |
|     /// <summary>
 | |
|     /// Surrogate to AssetReferenceUILabelRestriction
 | |
|     /// This surrogate class provides the editor-side implementation of AssetReferenceUILabelRestriction attribute
 | |
|     /// Used to restrict an AssetReference field or property to only allow items wil specific labels. This is only enforced through the UI.
 | |
|     /// </summary>
 | |
|     [AssetReferenceSurrogate(typeof(AssetReferenceUILabelRestriction))]
 | |
|     public class AssetReferenceUILabelRestrictionSurrogate : AssetReferenceUIRestrictionSurrogate
 | |
|     {
 | |
|         AssetReferenceUILabelRestriction data;
 | |
|         /// <summary>
 | |
|         /// Sets the AssetReferenceUILabelRestriction for this surrogate
 | |
|         /// </summary>
 | |
|         /// <param name="initData">To initialize AssetReferenceUILabelRestriction field</param>
 | |
|         public override void Init(AssetReferenceUIRestriction initData)
 | |
|         {
 | |
|             data = initData as AssetReferenceUILabelRestriction;
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc/>
 | |
|         public override bool ValidateAsset(Object obj)
 | |
|         {
 | |
|             var path = AssetDatabase.GetAssetOrScenePath(obj);
 | |
|             return ValidateAsset(path);
 | |
|         }
 | |
| 
 | |
|         /// <inheritdoc/>
 | |
|         public override bool ValidateAsset(string path)
 | |
|         {
 | |
|             if (AddressableAssetSettingsDefaultObject.Settings == null)
 | |
|                 return false;
 | |
|             var guid = AssetDatabase.AssetPathToGUID(path);
 | |
|             var entry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(guid, true);
 | |
|             return ValidateAsset(entry);
 | |
|         }
 | |
| 
 | |
|         internal override bool ValidateAsset(IReferenceEntryData entry)
 | |
|         {
 | |
|             if (entry != null)
 | |
|             {
 | |
|                 foreach (var label in data.m_AllowedLabels)
 | |
|                 {
 | |
|                     if (entry.labels.Contains(label))
 | |
|                         return true;
 | |
|                 }
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         ///<inheritdoc/>
 | |
|         public override string ToString()
 | |
|         {
 | |
|             return data.ToString();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Utility Class
 | |
|     /// </summary>
 | |
|     public class AssetReferenceUtility
 | |
|     {
 | |
|         /// <summary>
 | |
|         /// Finds surrogate class for an Assembly with a particular TargetType
 | |
|         /// </summary>
 | |
|         /// <param name="targetType">Target Type to search</param>
 | |
|         /// <returns>Type of the surrogate found for the Assembly with a particular Target Type.</returns>
 | |
|         public static Type GetSurrogate(Type targetType)
 | |
|         {
 | |
|             if (targetType == null)
 | |
|             {
 | |
|                 Debug.LogError("targetType cannot be null");
 | |
|                 return null;
 | |
|             }
 | |
| 
 | |
|             Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
 | |
|             List<Type> typesList = new List<Type>();
 | |
|             foreach (Assembly assem in assemblies)
 | |
|             {
 | |
|                 var assemblyTypeList = GatherTargetTypesFromAssembly(assem, targetType, out bool concreteTypeFound);
 | |
|                 if (concreteTypeFound == true)
 | |
|                     return assemblyTypeList[0];
 | |
|                 typesList.AddRange(assemblyTypeList);
 | |
|             }
 | |
| 
 | |
|             if (typesList.Count == 0)
 | |
|                 return null;
 | |
| 
 | |
|             typesList.Sort(AssetReferenceUtility.CompareTypes);
 | |
|             return typesList[0];
 | |
|         }
 | |
| 
 | |
|         static int CompareTypes(object x, object y)
 | |
|         {
 | |
|             Type t1 = (Type)x;
 | |
|             Type t2 = (Type)y;
 | |
|             if (t1 == t2)
 | |
|                 return 0;
 | |
|             else if (t1.IsAssignableFrom(t2))
 | |
|                 return 1;
 | |
|             else
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         private static List<Type> GatherTargetTypesFromAssembly(Assembly assembly, Type targetType, out bool concreteTypeFound)
 | |
|         {
 | |
|             List<Type> assignableTypesList = new List<Type>();
 | |
|             var typeList = assembly.GetTypes().Where(attrType => typeof(AssetReferenceUIRestrictionSurrogate).IsAssignableFrom(attrType)).ToList();
 | |
|             foreach (var type in typeList)
 | |
|             {
 | |
|                 var customAttribute = type.GetCustomAttribute<AssetReferenceSurrogateAttribute>();
 | |
|                 if (customAttribute == null)
 | |
|                     continue;
 | |
|                 if (customAttribute.TargetType == targetType)
 | |
|                 {
 | |
|                     assignableTypesList.Clear();
 | |
|                     assignableTypesList.Add(type);
 | |
|                     concreteTypeFound = true;
 | |
|                     return assignableTypesList;
 | |
|                 }
 | |
|                 if (customAttribute.TargetType.IsAssignableFrom(targetType))
 | |
|                 {
 | |
|                     assignableTypesList.Add(type);
 | |
|                 }
 | |
|             }
 | |
|             concreteTypeFound = false;
 | |
|             return assignableTypesList;
 | |
|         }
 | |
|     }
 | |
| }
 |