//-----------------------------------------------------------------------
//
// Copyright (c) Sirenix ApS. All rights reserved.
//
//-----------------------------------------------------------------------
#if !SIRENIX_INTERNAL
#pragma warning disable
#endif
using System;
using Sirenix.OdinInspector;
[assembly: RegisterAssetReferenceAttributeForwardToChild(typeof(InlineEditorAttribute))]
[assembly: RegisterAssetReferenceAttributeForwardToChild(typeof(PreviewFieldAttribute))]
namespace Sirenix.OdinInspector
{
using System.Diagnostics;
///
/// DisallowAddressableSubAssetField is used on AssetReference properties, and disallows and prevents assigned sub-assets to the asset reference.
///
///
///
/// [DisallowAddressableSubAssetField]
/// public AssetReference Reference;
///
///
///
///
[Conditional("UNITY_EDITOR")]
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Parameter)]
public class DisallowAddressableSubAssetFieldAttribute : Attribute
{
}
///
/// Registers an attribute to be applied to an AssetRefenece property, to be forwarded and applied to the AssetReference's child instead.
/// This allows attributes designed for use on UnityEngine.Objects to be used on AssetReference properties as well.
/// By default, InlineEditorAttribute and PreviewFieldAttribute are registered for forwarding.
///
///
///
/// [assembly: Sirenix.OdinInspector.Modules.RegisterAssetReferenceAttributeForwardToChild(typeof(InlineEditorAttribute))]
/// [assembly: Sirenix.OdinInspector.Modules.RegisterAssetReferenceAttributeForwardToChild(typeof(PreviewFieldAttribute))]
///
///
///
[Conditional("UNITY_EDITOR")]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class RegisterAssetReferenceAttributeForwardToChildAttribute : Attribute // TODO: Should this be a global attribute?
{
///
/// The type of the attribute to forward.
///
public readonly Type AttributeType;
///
/// Registers the specified attribute to be copied and applied to the AssetReference's UnityEngine.Object child instead.
///
/// The attribute type to forward.
public RegisterAssetReferenceAttributeForwardToChildAttribute(Type attributeType)
{
this.AttributeType = attributeType;
}
}
}
#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Modules.Addressables.Editor
{
using Sirenix.OdinInspector.Editor;
using Sirenix.Serialization;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using Sirenix.OdinInspector.Modules.Addressables.Editor.Internal;
using Sirenix.Reflection.Editor;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.GUI;
using UnityEngine;
using UnityEngine.AddressableAssets;
using System.Runtime.Serialization;
using UnityEngine.U2D;
using UnityEditor.U2D;
using System.IO;
///
/// Draws an AssetReference property.
///
/// The concrete type of AssetReference to be drawn. For example, AssetReferenceTexture.
[DrawerPriority(0, 1, 0)]
public class AssetReferenceDrawer : OdinValueDrawer, IDefinesGenericMenuItems
where T : AssetReference
{
private bool hideAssetReferenceField;
private Type[] validMainAssetTypes;
private Type targetType;
private bool targetTypeIsNotValidMainAsset;
private string NoneSelectedLabel;
//private string[] labelRestrictions;
private bool showSubAssetField;
private bool updateShowSubAssetField;
private bool disallowSubAssets_Backing;
private bool ActuallyDisallowSubAssets => this.disallowSubAssets_Backing && !this.targetTypeIsNotValidMainAsset;
private List restrictions;
private bool isSpriteAtlas;
protected override bool CanDrawValueProperty(InspectorProperty property)
{
return property.GetAttribute() == null;
}
protected override void Initialize()
{
// If a child exists, we draw that child instead of the AssetReference field.
if (this.Property.Children.Count > 0)
{
this.hideAssetReferenceField = true;
return;
}
this.EnsureNotRealNull();
this.validMainAssetTypes = OdinAddressableUtility.GetAssetReferenceValidMainAssetTypes(typeof(T));
this.targetType = OdinAddressableUtility.GetAssetReferenceTargetType(typeof(T));
this.targetTypeIsNotValidMainAsset = this.validMainAssetTypes.Contains(this.targetType) == false;
this.isSpriteAtlas = this.validMainAssetTypes.Length > 0 && this.validMainAssetTypes[0] == typeof(SpriteAtlas);
if (this.targetType == typeof(UnityEngine.Object))
{
this.NoneSelectedLabel = "None (Addressable Asset)";
}
else if (this.validMainAssetTypes.Length > 1 || this.validMainAssetTypes[0] != this.targetType)
{
this.NoneSelectedLabel = $"None (Addressable [{string.Join("/", this.validMainAssetTypes.Select(n => n.GetNiceName()))}]>{this.targetType.GetNiceName()})";
}
else
{
this.NoneSelectedLabel = $"None (Addressable {this.targetType.GetNiceName()})";
}
this.restrictions = new List();
foreach (var attr in this.Property.Attributes)
{
if (attr is AssetReferenceUIRestriction r)
{
this.restrictions.Add(r);
}
}
this.disallowSubAssets_Backing = Property.GetAttribute() != null;
this.updateShowSubAssetField = true;
}
private string lastGuid;
protected override void DrawPropertyLayout(GUIContent label)
{
if (this.disallowSubAssets_Backing && this.targetTypeIsNotValidMainAsset)
{
SirenixEditorGUI.WarningMessageBox($"This {typeof(T).GetNiceName()} field has been marked as not allowing sub assets, but the target type '{this.targetType.GetNiceName()}' is not a valid main asset for {typeof(T).GetNiceName()}, so the target value *must* be a sub asset. Therefore sub assets have been enabled. (Valid main asset types for {typeof(T).GetNiceName()} are: {string.Join(", ", this.validMainAssetTypes.Select(t => t.GetNiceName()))})");
}
if (this.hideAssetReferenceField == false)
{
var value = ValueEntry.SmartValue;
if (this.lastGuid != this.ValueEntry.SmartValue?.AssetGUID)
{
this.updateShowSubAssetField = true;
}
this.lastGuid = this.ValueEntry.SmartValue?.AssetGUID;
// Update showSubAssetField.
if (this.updateShowSubAssetField && Event.current.type == EventType.Layout)
{
if (value == null || value.AssetGUID == null || value.editorAsset == null)
{
this.showSubAssetField = false;
}
else if (string.IsNullOrEmpty(value.SubObjectName) == false)
{
this.showSubAssetField = true;
}
else if (this.ActuallyDisallowSubAssets)
{
this.showSubAssetField = false;
}
else
{
var path = AssetDatabase.GUIDToAssetPath(value.AssetGUID);
if (path == null)
{
this.showSubAssetField = false;
}
else
{
var mainAsset = AssetDatabase.LoadMainAssetAtPath(path);
this.showSubAssetField = OdinAddressableUtility.EnumerateAllActualAndVirtualSubAssets(mainAsset, path).Any();
}
}
this.updateShowSubAssetField = false;
}
var rect = SirenixEditorGUI.GetFeatureRichControlRect(label, out var controlId, out var _, out var valueRect);
Rect mainRect = valueRect;
Rect subRect = default, subPickerRect = default;
if (this.showSubAssetField)
{
subRect = mainRect.Split(1, 2).AddX(1);
mainRect = mainRect.Split(0, 2).SubXMax(1);
subPickerRect = subRect.AlignRight(16);
}
var mainPickerRect = mainRect.AlignRight(16);
// Cursor
EditorGUIUtility.AddCursorRect(mainPickerRect, MouseCursor.Link);
if (showSubAssetField)
{
EditorGUIUtility.AddCursorRect(subPickerRect, MouseCursor.Link);
}
// Selector
if (GUI.Button(mainPickerRect, "", SirenixGUIStyles.None))
{
OpenMainAssetSelector(valueRect);
}
if (showSubAssetField && GUI.Button(subPickerRect, "", SirenixGUIStyles.None))
{
OpenSubAssetSelector(valueRect);
}
// Ping
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && mainRect.Contains(Event.current.mousePosition) && value != null && value.editorAsset != null)
{
EditorGUIUtility.PingObject(value.editorAsset);
}
// Drag and drop
EditorGUI.BeginChangeCheck();
var drop = DragAndDropUtilities.DropZone(rect, null, typeof(object), false, controlId);
if (EditorGUI.EndChangeCheck())
{
this.EnsureNotRealNull();
if (this.ConvertToValidAssignment(drop, out Object obj, out bool isSubAssetAssignment))
{
if (this.isSpriteAtlas && obj is Sprite sprite)
{
foreach (SpriteAtlas spriteAtlas in AssetDatabase_Internals.FindAssets(String.Empty, false, AssetDatabaseSearchArea.AllAssets))
{
if (!spriteAtlas.CanBindTo(sprite))
{
continue;
}
this.SetMainAndSubAsset(spriteAtlas, sprite);
break;
}
}
else
{
if (isSubAssetAssignment)
{
string path = AssetDatabase.GetAssetPath(obj);
UnityEngine.Object mainAsset = AssetDatabase.LoadMainAssetAtPath(path);
if (mainAsset != null)
{
if (mainAsset is Sprite mainAssetSprite)
{
this.SetMainAndSubAsset(mainAssetSprite, obj);
}
else
{
this.SetMainAndSubAsset(mainAsset, obj);
}
}
this.updateShowSubAssetField = true;
}
else
{
var isSet = false;
if (string.IsNullOrEmpty(this.ValueEntry.SmartValue.SubObjectName))
{
if (obj is Sprite)
{
Object[] subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetDatabase.GetAssetPath(obj));
if (subAssets.Length > 0)
{
this.SetMainAndSubAsset(obj, subAssets[0]);
isSet = true;
}
}
}
if (!isSet)
{
this.SetMainAsset(obj);
}
}
}
if (this.ActuallyDisallowSubAssets &&
!this.targetTypeIsNotValidMainAsset &&
!string.IsNullOrEmpty(this.ValueEntry.SmartValue.SubObjectName))
{
this.SetSubAsset(null);
}
}
else if (drop == null)
{
this.SetMainAsset(null);
}
}
// Drawing
if (Event.current.type == EventType.Repaint)
{
GUIContent valueLabel;
if (value == null || string.IsNullOrEmpty(value.AssetGUID) || value.editorAsset == null)
{
valueLabel = GUIHelper.TempContent(NoneSelectedLabel);
}
else if (showSubAssetField)
{
var path = AssetDatabase.GUIDToAssetPath(value.AssetGUID);
var assetName = System.IO.Path.GetFileNameWithoutExtension(path);
valueLabel = GUIHelper.TempContent(assetName, GetTheDamnPreview(value.editorAsset));
}
else
{
valueLabel = GUIHelper.TempContent(value.editorAsset.name, GetTheDamnPreview(value.editorAsset));
}
GUI.Label(mainRect, valueLabel, EditorStyles.objectField);
SdfIcons.DrawIcon(mainPickerRect.SetWidth(12), SdfIconType.Record2);
if (this.showSubAssetField)
{
if (string.IsNullOrEmpty(value.SubObjectName) || value.editorAsset == null)
{
valueLabel = GUIHelper.TempContent("");
}
else
{
valueLabel = GUIHelper.TempContent(value.SubObjectName);
}
GUI.Label(subRect, valueLabel, EditorStyles.objectField);
SdfIcons.DrawIcon(subPickerRect.SetWidth(12), SdfIconType.Record2);
}
}
}
else
{
this.Property.Children[0].Draw(label);
}
}
private static Texture2D GetTheDamnPreview(UnityEngine.Object obj)
{
Texture2D img = obj as Texture2D;
if (img == null)
{
img = (obj as Sprite)?.texture;
}
if (img == null)
{
img = AssetPreview.GetMiniThumbnail(obj);
}
return img;
}
private bool ConvertToValidAssignment(object drop, out UnityEngine.Object converted, out bool isSubAssetAssignment)
{
converted = null;
isSubAssetAssignment = false;
bool isDefinitelyMainAssetAssignment = false;
if (object.ReferenceEquals(drop, null)) return false;
if (!ConvertUtility.TryWeakConvert(drop, this.targetType, out object convertedObj))
{
for (int i = 0; i < this.validMainAssetTypes.Length; i++)
{
if (ConvertUtility.TryWeakConvert(drop, this.validMainAssetTypes[i], out convertedObj))
{
isDefinitelyMainAssetAssignment = true;
break;
}
}
}
if (convertedObj == null || !(convertedObj is UnityEngine.Object unityObj) || unityObj == null) return false;
converted = unityObj;
if (isDefinitelyMainAssetAssignment)
{
isSubAssetAssignment = false;
return true;
}
else if (AssetDatabase.IsSubAsset(converted))
{
if (this.ActuallyDisallowSubAssets)
{
return false;
}
isSubAssetAssignment = true;
return true;
}
return true;
}
private void OpenMainAssetSelector(Rect rect)
{
this.EnsureNotRealNull();
var selector = new AddressableSelector("Select", this.validMainAssetTypes, this.restrictions, typeof(T));
bool isUnityRoot = this.Property.SerializationRoot?.ValueEntry.WeakSmartValue is UnityEngine.Object;
if (isUnityRoot)
{
Undo.IncrementCurrentGroup();
int undoIndex = Undo.GetCurrentGroup();
selector.SelectionCancelled += () => { Undo.RevertAllDownToGroup(undoIndex); };
selector.SelectionConfirmed += entries =>
{
Undo.RevertAllDownToGroup(undoIndex);
this.OnMainAssetSelect(entries.FirstOrDefault());
};
}
else
{
selector.SelectionConfirmed += entries => { this.OnMainAssetSelect(entries.FirstOrDefault()); };
}
selector.SelectionChangedWithType += (type, entries) =>
{
if (type == SelectionChangedType.SelectionCleared)
{
return;
}
AddressableAssetEntry entry = entries.FirstOrDefault();
this.OnMainAssetSelect(entry);
};
selector.ShowInPopup(rect);
}
private void OpenSubAssetSelector(Rect rect)
{
this.EnsureNotRealNull();
if (this.ValueEntry.SmartValue == null || this.ValueEntry.SmartValue.AssetGUID == null)
return;
var path = AssetDatabase.GUIDToAssetPath(this.ValueEntry.SmartValue.AssetGUID);
if (path == null)
return;
var mainAsset = AssetDatabase.LoadMainAssetAtPath(path);
List