diff --git a/Attributes.meta b/Attributes.meta
new file mode 100644
index 0000000..7a012f9
--- /dev/null
+++ b/Attributes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3a249b41117a9b043a88be309acc8411
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Attributes/Spiney.SubclassSelector.Runtime.asmdef b/Attributes/Spiney.SubclassSelector.Runtime.asmdef
new file mode 100644
index 0000000..66eca49
--- /dev/null
+++ b/Attributes/Spiney.SubclassSelector.Runtime.asmdef
@@ -0,0 +1,14 @@
+{
+ "name": "Spiney.SubclassSelector.Runtime",
+ "rootNamespace": "",
+ "references": [],
+ "includePlatforms": [],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": true,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": [],
+ "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Attributes/Spiney.SubclassSelector.Runtime.asmdef.meta b/Attributes/Spiney.SubclassSelector.Runtime.asmdef.meta
new file mode 100644
index 0000000..ba2deff
--- /dev/null
+++ b/Attributes/Spiney.SubclassSelector.Runtime.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: faf144d3e138eac43ae4270ba99588ca
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Attributes/SubclassPathAttribute.cs b/Attributes/SubclassPathAttribute.cs
index abe5ae8..b789c6c 100644
--- a/Attributes/SubclassPathAttribute.cs
+++ b/Attributes/SubclassPathAttribute.cs
@@ -1,81 +1,84 @@
using System;
-///
-/// Use this attribute on plain class types to determine their path when using .
-///
-[System.Diagnostics.Conditional("UNITY_EDITOR")]
-[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
-public sealed class SubclassPathAttribute : Attribute
+namespace Spiney.SubclassSelector
{
- #region Constructors
-
- ///
- /// Empty constructor. The types names in will not be modified.
- ///
- public SubclassPathAttribute() { }
-
///
- /// Determines the path for types displayed in .
- /// The default type name will be used.
+ /// Use this attribute on plain class types to determine their path when using .
///
- /// The path for this type.
- public SubclassPathAttribute(string path)
+ [System.Diagnostics.Conditional("UNITY_EDITOR")]
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+ public sealed class SubclassPathAttribute : Attribute
{
- SubClassPath = path;
- }
+ #region Constructors
- ///
- /// Determines both the path and the display name for types displayed in .
- ///
- /// The path for this type.
- /// The display name for this type.
- public SubclassPathAttribute(string path, string subclassName)
- {
- SubClassPath = path;
- SubClassName = subclassName;
- }
+ ///
+ /// Empty constructor. The types names in will not be modified.
+ ///
+ public SubclassPathAttribute() { }
+
+ ///
+ /// Determines the path for types displayed in .
+ /// The default type name will be used.
+ ///
+ /// The path for this type.
+ public SubclassPathAttribute(string path)
+ {
+ SubClassPath = path;
+ }
- #endregion
+ ///
+ /// Determines both the path and the display name for types displayed in .
+ ///
+ /// The path for this type.
+ /// The display name for this type.
+ public SubclassPathAttribute(string path, string subclassName)
+ {
+ SubClassPath = path;
+ SubClassName = subclassName;
+ }
- #region Internal Members
+ #endregion
- private string _subClassPath = string.Empty;
+ #region Internal Members
- private string _subClassName = string.Empty;
+ private string _subClassPath = string.Empty;
- #endregion
+ private string _subClassName = string.Empty;
- #region Properties
+ #endregion
- ///
- /// The path for this type as used by .
- ///
- public string SubClassPath
- {
- get => _subClassPath;
- set
+ #region Properties
+
+ ///
+ /// The path for this type as used by .
+ ///
+ public string SubClassPath
{
- HasSubClassPath = !string.IsNullOrEmpty(value);
- _subClassPath = value;
+ get => _subClassPath;
+ set
+ {
+ HasSubClassPath = !string.IsNullOrEmpty(value);
+ _subClassPath = value;
+ }
}
- }
- ///
- /// The display name for this type as used by .
- ///
- public string SubClassName
- {
- get => _subClassName;
- set
+ ///
+ /// The display name for this type as used by .
+ ///
+ public string SubClassName
{
- HasSubClassName = !string.IsNullOrEmpty(value);
- _subClassName = value;
+ get => _subClassName;
+ set
+ {
+ HasSubClassName = !string.IsNullOrEmpty(value);
+ _subClassName = value;
+ }
}
- }
- public bool HasSubClassPath { get; private set; } = false;
+ public bool HasSubClassPath { get; private set; } = false;
- public bool HasSubClassName { get; private set; } = false;
+ public bool HasSubClassName { get; private set; } = false;
- #endregion
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/Attributes/SubclassPathAttribute.cs.meta b/Attributes/SubclassPathAttribute.cs.meta
new file mode 100644
index 0000000..1994bce
--- /dev/null
+++ b/Attributes/SubclassPathAttribute.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 229d895b10849c849bb2a10e53ffddd3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Attributes/SubclassSelectorAttribute.cs b/Attributes/SubclassSelectorAttribute.cs
index f6c415c..4541936 100644
--- a/Attributes/SubclassSelectorAttribute.cs
+++ b/Attributes/SubclassSelectorAttribute.cs
@@ -1,91 +1,94 @@
using System;
-///
-/// Attribute that allows you to easily select from among the subclsses of a non-Unity Object, polymorphic reference value.
-/// Supports both singular fields and collections of any type that Odin supports.
-///
-[System.Diagnostics.Conditional("UNITY_EDITOR")]
-[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
-public sealed class SubclassSelectorAttribute : Attribute
+namespace Spiney.SubclassSelector
{
- #region Constructor
-
- public SubclassSelectorAttribute() { }
-
- #endregion
-
- #region Internal Members
-
- private string _customTypeFilter = string.Empty;
-
- private bool _hasCustomTypeFilter = false;
-
- #endregion
-
- #region Properties
-
- ///
- /// If false, duplicate won't be selectable in the collection selector, or collection element dropdowns.
- ///
- ///
- /// Only affects collections. True by default.
- ///
- public bool AllowDuplicates { get; set; } = true;
-
- ///
- /// Will hide the Odin default object reference picker if true as it it has been
- /// decorated with .
- ///
- ///
- /// True by default.
- ///
- public bool HideReferencePicker { get; set; } = true;
-
///
- /// If true, dropdowns will be drawn at the top
- /// of each list element.
+ /// Attribute that allows you to easily select from among the subclsses of a non-Unity Object, polymorphic reference value.
+ /// Supports both singular fields and collections of any type that Odin supports.
///
- ///
- /// Only affects collections. False by default.
- ///
- public bool DrawDropdownForListElements { get; set; } = false;
-
- ///
- /// If true, will draw a BoxGroup-style box around each element of a collection.
- ///
- ///
- /// False by default. Only affects collections.
- ///
- public bool DrawBoxForListElements { get; set; } = false;
-
- ///
- /// If true, will hide the class' label as if it had been decorated with .
- ///
- ///
- /// Only affects non-collection fields. True by default.
- ///
- public bool HideClassLabel { get; set; } = true;
-
- ///
- /// Provide a value resolver expression to filter types after the intial collection of subtypes has been generated.
- ///
- ///
- /// Abstract types are already filtered.
- ///
- public string CustomTypeFilter
+ [System.Diagnostics.Conditional("UNITY_EDITOR")]
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
+ public sealed class SubclassSelectorAttribute : Attribute
{
- get => _customTypeFilter;
- set
+ #region Constructor
+
+ public SubclassSelectorAttribute() { }
+
+ #endregion
+
+ #region Internal Members
+
+ private string _customTypeFilter = string.Empty;
+
+ private bool _hasCustomTypeFilter = false;
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// If false, duplicate won't be selectable in the collection selector, or collection element dropdowns.
+ ///
+ ///
+ /// Only affects collections. True by default.
+ ///
+ public bool AllowDuplicates { get; set; } = true;
+
+ ///
+ /// Will hide the Odin default object reference picker if true as it it has been
+ /// decorated with .
+ ///
+ ///
+ /// True by default.
+ ///
+ public bool HideReferencePicker { get; set; } = true;
+
+ ///
+ /// If true, dropdowns will be drawn at the top
+ /// of each list element.
+ ///
+ ///
+ /// Only affects collections. False by default.
+ ///
+ public bool DrawDropdownForListElements { get; set; } = false;
+
+ ///
+ /// If true, will draw a BoxGroup-style box around each element of a collection.
+ ///
+ ///
+ /// False by default. Only affects collections.
+ ///
+ public bool DrawBoxForListElements { get; set; } = false;
+
+ ///
+ /// If true, will hide the class' label as if it had been decorated with .
+ ///
+ ///
+ /// Only affects non-collection fields. True by default.
+ ///
+ public bool HideClassLabel { get; set; } = true;
+
+ ///
+ /// Provide a value resolver expression to filter types after the intial collection of subtypes has been generated.
+ ///
+ ///
+ /// Abstract types are already filtered.
+ ///
+ public string CustomTypeFilter
{
- _hasCustomTypeFilter = !string.IsNullOrEmpty(value);
- _customTypeFilter = value;
+ get => _customTypeFilter;
+ set
+ {
+ _hasCustomTypeFilter = !string.IsNullOrEmpty(value);
+ _customTypeFilter = value;
+ }
}
- }
- ///
- /// Is there a custom type filter expression?
- ///
- public bool HasCustomTypeFilter => _hasCustomTypeFilter;
+ ///
+ /// Is there a custom type filter expression?
+ ///
+ public bool HasCustomTypeFilter => _hasCustomTypeFilter;
- #endregion
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/Attributes/SubclassSelectorAttribute.cs.meta b/Attributes/SubclassSelectorAttribute.cs.meta
new file mode 100644
index 0000000..7cb0311
--- /dev/null
+++ b/Attributes/SubclassSelectorAttribute.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 789e008babfd6c14d8edbd8799c9ed10
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor.meta b/Editor.meta
new file mode 100644
index 0000000..26c3f25
--- /dev/null
+++ b/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7fb88d155aec1ad4297eef3b37234b9e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/Spiney.SubclassSelector.Editor.asmdef b/Editor/Spiney.SubclassSelector.Editor.asmdef
new file mode 100644
index 0000000..d022bb9
--- /dev/null
+++ b/Editor/Spiney.SubclassSelector.Editor.asmdef
@@ -0,0 +1,24 @@
+{
+ "name": "Spiney.SubclassSelector.Editor",
+ "rootNamespace": "",
+ "references": [
+ "GUID:faf144d3e138eac43ae4270ba99588ca"
+ ],
+ "includePlatforms": [
+ "Editor"
+ ],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": true,
+ "precompiledReferences": [
+ "Sirenix.OdinInspector.Attributes.dll",
+ "Sirenix.OdinInspector.Editor.dll",
+ "Sirenix.Utilities.dll",
+ "Sirenix.Utilities.Editor.dll",
+ "Sirenix.Serialization.dll"
+ ],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": [],
+ "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Editor/Spiney.SubclassSelector.Editor.asmdef.meta b/Editor/Spiney.SubclassSelector.Editor.asmdef.meta
new file mode 100644
index 0000000..e195fc3
--- /dev/null
+++ b/Editor/Spiney.SubclassSelector.Editor.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 5e8aaa8b269e7d94f871987aa970bdd6
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/SubclassSelectorAttributeProcessor.cs b/Editor/SubclassSelectorAttributeProcessor.cs
index 2a02242..2c043a1 100644
--- a/Editor/SubclassSelectorAttributeProcessor.cs
+++ b/Editor/SubclassSelectorAttributeProcessor.cs
@@ -5,111 +5,114 @@
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
-internal sealed class SubclassSelectorAttributeProcessor : OdinAttributeProcessor where T : class
+namespace Spiney.SubclassSelector
{
- #region Internal Members
+ internal sealed class SubclassSelectorAttributeProcessor : OdinAttributeProcessor where T : class
+ {
+ #region Internal Members
- private SubclassSelectorAttribute subclassSelector;
- private ICollectionResolver collectionResolver;
+ private SubclassSelectorAttribute subclassSelector;
+ private ICollectionResolver collectionResolver;
- private bool isCollection = false;
- private bool parentIsCollection = false;
+ private bool isCollection = false;
+ private bool parentIsCollection = false;
- #endregion
+ #endregion
- #region Processor Overrides
+ #region Processor Overrides
- public override bool CanProcessSelfAttributes(InspectorProperty property)
- {
- if ((subclassSelector = property.Attributes.GetAttribute()) == null)
+ public override bool CanProcessSelfAttributes(InspectorProperty property)
{
- return false;
- }
-
- bool isUnityObject;
- collectionResolver = property.ChildResolver as ICollectionResolver;
- isCollection = collectionResolver != null;
+ if ((subclassSelector = property.Attributes.GetAttribute()) == null)
+ {
+ return false;
+ }
- if (isCollection)
- {
- Type elementType = collectionResolver.ElementType;
- isUnityObject = typeof(UnityEngine.Object).IsAssignableFrom(elementType);
- return !isUnityObject;
- }
- else
- {
- collectionResolver = property.ParentValueProperty?.ChildResolver as ICollectionResolver ?? null;
- parentIsCollection = collectionResolver != null;
- isUnityObject = typeof(UnityEngine.Object).IsAssignableFrom(property.ValueEntry.BaseValueType);
+ bool isUnityObject;
+ collectionResolver = property.ChildResolver as ICollectionResolver;
+ isCollection = collectionResolver != null;
- if (parentIsCollection)
+ if (isCollection)
{
- return !isUnityObject && subclassSelector.DrawDropdownForListElements;
+ Type elementType = collectionResolver.ElementType;
+ isUnityObject = typeof(UnityEngine.Object).IsAssignableFrom(elementType);
+ return !isUnityObject;
}
else
{
- return !isUnityObject;
+ collectionResolver = property.ParentValueProperty?.ChildResolver as ICollectionResolver ?? null;
+ parentIsCollection = collectionResolver != null;
+ isUnityObject = typeof(UnityEngine.Object).IsAssignableFrom(property.ValueEntry.BaseValueType);
+
+ if (parentIsCollection)
+ {
+ return !isUnityObject && subclassSelector.DrawDropdownForListElements;
+ }
+ else
+ {
+ return !isUnityObject;
+ }
}
}
- }
- public override void ProcessSelfAttributes(InspectorProperty property, List attributes)
- {
- var hideReferencePicker = property.Attributes.GetAttribute();
- if (subclassSelector.HideReferencePicker && hideReferencePicker == null)
+ public override void ProcessSelfAttributes(InspectorProperty property, List attributes)
{
- hideReferencePicker = new HideReferenceObjectPickerAttribute();
- attributes.Add(hideReferencePicker);
- }
-
- var hideLabel = property.Attributes.GetAttribute();
-
- if (subclassSelector.HideClassLabel && hideLabel == null)
- {
- hideLabel = new HideLabelAttribute();
- attributes.Add(hideLabel);
- }
-
- if (isCollection)
- {
- var labelText = new LabelTextAttribute(property.NiceName);
- attributes.Add(labelText);
-
- var lds = property.Attributes.GetAttribute();
-
- if (lds == null)
+ var hideReferencePicker = property.Attributes.GetAttribute();
+ if (subclassSelector.HideReferencePicker && hideReferencePicker == null)
{
- lds = new ListDrawerSettingsAttribute();
- attributes.Add(lds);
+ hideReferencePicker = new HideReferenceObjectPickerAttribute();
+ attributes.Add(hideReferencePicker);
}
- lds.CustomAddFunction = SubclassSelectorUtilities.OpenSubclassSelectorString;
+ var hideLabel = property.Attributes.GetAttribute();
- if (subclassSelector.DrawBoxForListElements)
+ if (subclassSelector.HideClassLabel && hideLabel == null)
{
- lds.OnBeginListElementGUI = SubclassSelectorUtilities.OnBeginBoxSubclassElementString;
- lds.OnEndListElementGUI = SubclassSelectorUtilities.OnEndBoxSubclassElementString;
+ hideLabel = new HideLabelAttribute();
+ attributes.Add(hideLabel);
}
- }
- else
- {
- var typeFilter = new TypeFilterAttribute(SubclassSelectorUtilities.TypeFilterResolverString);
- if (parentIsCollection)
+ if (isCollection)
{
- typeFilter.DrawValueNormally = true;
+ var labelText = new LabelTextAttribute(property.NiceName);
+ attributes.Add(labelText);
+
+ var lds = property.Attributes.GetAttribute();
+
+ if (lds == null)
+ {
+ lds = new ListDrawerSettingsAttribute();
+ attributes.Add(lds);
+ }
+
+ lds.CustomAddFunction = SubclassSelectorUtilities.OpenSubclassSelectorString;
+
+ if (subclassSelector.DrawBoxForListElements)
+ {
+ lds.OnBeginListElementGUI = SubclassSelectorUtilities.OnBeginBoxSubclassElementString;
+ lds.OnEndListElementGUI = SubclassSelectorUtilities.OnEndBoxSubclassElementString;
+ }
}
else
{
- bool valueIsNull = property.ValueEntry.WeakSmartValue == null;
- bool hidePicker = subclassSelector.HideReferencePicker;
- typeFilter.DrawValueNormally = !valueIsNull || !hidePicker;
+ var typeFilter = new TypeFilterAttribute(SubclassSelectorUtilities.TypeFilterResolverString);
+
+ if (parentIsCollection)
+ {
+ typeFilter.DrawValueNormally = true;
+ }
+ else
+ {
+ bool valueIsNull = property.ValueEntry.WeakSmartValue == null;
+ bool hidePicker = subclassSelector.HideReferencePicker;
+ typeFilter.DrawValueNormally = !valueIsNull || !hidePicker;
+ }
+
+ attributes.Add(typeFilter);
}
-
- attributes.Add(typeFilter);
}
- }
- #endregion
+ #endregion
+ }
}
#endif
\ No newline at end of file
diff --git a/Editor/SubclassSelectorAttributeProcessor.cs.meta b/Editor/SubclassSelectorAttributeProcessor.cs.meta
new file mode 100644
index 0000000..2e13dec
--- /dev/null
+++ b/Editor/SubclassSelectorAttributeProcessor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0f2d5d65571cfd24d8b61ce170f074bf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/SubclassSelectorUtilities.cs b/Editor/SubclassSelectorUtilities.cs
index 7e5e2c1..74f5ac7 100644
--- a/Editor/SubclassSelectorUtilities.cs
+++ b/Editor/SubclassSelectorUtilities.cs
@@ -10,269 +10,272 @@
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
-///
-/// Utility methods for use with .
-///
-public static class SubclassSelectorUtilities
+namespace Spiney.SubclassSelector
{
- #region Resolver Strings
-
- public static string OpenSubclassSelectorString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(OpenSubclassCollectionSelector)}($property)";
+ ///
+ /// Utility methods for use with .
+ ///
+ public static class SubclassSelectorUtilities
+ {
+ #region Resolver Strings
- public static string TypeFilterResolverString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(GetSubclassSelectorDropdownItems)}($property)";
+ public static string OpenSubclassSelectorString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(OpenSubclassCollectionSelector)}($property)";
- public static string OnBeginBoxSubclassElementString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(BeginDrawBoxedSubclassElement)}($property, $index)";
+ public static string TypeFilterResolverString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(GetSubclassSelectorDropdownItems)}($property)";
- public static string OnEndBoxSubclassElementString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(EndDrawBoxedSubclassElement)}()";
+ public static string OnBeginBoxSubclassElementString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(BeginDrawBoxedSubclassElement)}($property, $index)";
- #endregion
+ public static string OnEndBoxSubclassElementString => $"@{nameof(SubclassSelectorUtilities)}.{nameof(EndDrawBoxedSubclassElement)}()";
- #region Validation and Data Retreival
+ #endregion
- ///
- /// Gets all child types of , filtering abstract types.
- ///
- /// Type to get all derived/child types of.
- public static IEnumerable GetTypeDerivedTypes(Type baseType, InspectorProperty inspectorProperty)
- {
- var subclassSelector = inspectorProperty.Attributes.GetAttribute();
- ValueResolver typeFilterResolver = null;
+ #region Validation and Data Retreival
- if (subclassSelector.HasCustomTypeFilter)
+ ///
+ /// Gets all child types of , filtering abstract types.
+ ///
+ /// Type to get all derived/child types of.
+ public static IEnumerable GetTypeDerivedTypes(Type baseType, InspectorProperty inspectorProperty)
{
- typeFilterResolver = ValueResolver.Get(inspectorProperty, subclassSelector.CustomTypeFilter,
- new NamedValue("type", typeof(Type)));
+ var subclassSelector = inspectorProperty.Attributes.GetAttribute();
+ ValueResolver typeFilterResolver = null;
- if (typeFilterResolver.HasError)
+ if (subclassSelector.HasCustomTypeFilter)
{
- Debug.LogError(typeFilterResolver.ErrorMessage);
- }
- }
+ typeFilterResolver = ValueResolver.Get(inspectorProperty, subclassSelector.CustomTypeFilter,
+ new NamedValue("type", typeof(Type)));
- var types = TypeCache.GetTypesDerivedFrom(baseType).Where(AbstractTypeFilter);
-
- if (!baseType.IsAbstract && PassesCustomTypeFilter(baseType))
- {
- yield return baseType;
- }
-
- foreach (Type type in types)
- {
- if (PassesCustomTypeFilter(type))
- {
- yield return type;
+ if (typeFilterResolver.HasError)
+ {
+ Debug.LogError(typeFilterResolver.ErrorMessage);
+ }
}
- }
- // local methods
- static bool AbstractTypeFilter(Type type) => !type.IsAbstract;
+ var types = TypeCache.GetTypesDerivedFrom(baseType).Where(AbstractTypeFilter);
- bool PassesCustomTypeFilter(Type type)
- {
- if (typeFilterResolver == null || typeFilterResolver.HasError)
+ if (!baseType.IsAbstract && PassesCustomTypeFilter(baseType))
{
- return true;
+ yield return baseType;
}
- else
+
+ foreach (Type type in types)
{
- typeFilterResolver.Context.NamedValues.Set("type", type);
- bool result = typeFilterResolver.GetValue();
- return result;
+ if (PassesCustomTypeFilter(type))
+ {
+ yield return type;
+ }
}
- }
- }
- public static string GetTypeSubclassPath(Type type)
- {
- SubclassPathAttribute pathAttribute = type.GetAttribute();
+ // local methods
+ static bool AbstractTypeFilter(Type type) => !type.IsAbstract;
- if (pathAttribute == null || (!pathAttribute.HasSubClassPath && !pathAttribute.HasSubClassName))
- {
- return type.GetNiceName();
- }
-
- string path = string.Empty;
- if (pathAttribute.HasSubClassPath)
- {
- path += (pathAttribute.SubClassPath + "/");
+ bool PassesCustomTypeFilter(Type type)
+ {
+ if (typeFilterResolver == null || typeFilterResolver.HasError)
+ {
+ return true;
+ }
+ else
+ {
+ typeFilterResolver.Context.NamedValues.Set("type", type);
+ bool result = typeFilterResolver.GetValue();
+ return result;
+ }
+ }
}
- path += pathAttribute.HasSubClassName ? pathAttribute.SubClassName : type.Name;
-
- return path;
- }
-
- ///
- /// Returns whether a particular type exists with children of an .
- ///
- /// If true, types derived from will also count.
- public static bool TypeExistsInPropertyChildren(PropertyChildren children, Type type)
- {
- foreach (var child in children)
+ public static string GetTypeSubclassPath(Type type)
{
- Type valueType = child.ValueEntry.TypeOfValue;
- bool duplicate = (valueType == type);
+ SubclassPathAttribute pathAttribute = type.GetAttribute();
- if (duplicate)
+ if (pathAttribute == null || (!pathAttribute.HasSubClassPath && !pathAttribute.HasSubClassName))
{
- return true;
+ return type.GetNiceName();
}
- }
-
- return false;
- }
-
- #endregion
- #region Field Methods
-
- public static IEnumerable> GetSubclassSelectorDropdownItems(InspectorProperty property)
- {
- SubclassSelectorAttribute subclassSelector = property.Attributes.GetAttribute();
- IEnumerable types = GetTypeDerivedTypes(property.ValueEntry.BaseValueType, property);
-
- InspectorProperty parent = property.ParentValueProperty;
- bool parentIsCollection = parent != null && parent.ChildResolver is ICollectionResolver;
-
- // if the parent is not a collection,
- // or we don't care about duplicate values...
- if (!parentIsCollection || subclassSelector.AllowDuplicates)
- {
- foreach (var type in types)
+ string path = string.Empty;
+ if (pathAttribute.HasSubClassPath)
{
- yield return GetDropdownItem(type);
+ path += (pathAttribute.SubClassPath + "/");
}
+
+ path += pathAttribute.HasSubClassName ? pathAttribute.SubClassName : type.Name;
+
+ return path;
}
- else
+
+ ///
+ /// Returns whether a particular type exists with children of an .
+ ///
+ /// If true, types derived from will also count.
+ public static bool TypeExistsInPropertyChildren(PropertyChildren children, Type type)
{
- foreach (var type in types)
+ foreach (var child in children)
{
- // the current type should be selectable so that the dropdown
- // draws the correct Subclass path
- if (type == property.ValueEntry.TypeOfValue)
- {
- yield return GetDropdownItem(type);
- continue;
- }
+ Type valueType = child.ValueEntry.TypeOfValue;
+ bool duplicate = (valueType == type);
- if (!TypeExistsInPropertyChildren(parent.Children, type))
+ if (duplicate)
{
- yield return GetDropdownItem(type);
+ return true;
}
}
- }
- static ValueDropdownItem GetDropdownItem(Type type)
- {
- string name = GetTypeSubclassPath(type);
- return new ValueDropdownItem(name, type);
+ return false;
}
- }
- #endregion
+ #endregion
- #region Collection Methods
+ #region Field Methods
- public static void OpenSubclassCollectionSelector(InspectorProperty property)
- {
- var collectionResolver = (ICollectionResolver)property.ChildResolver;
- var selectorAttribute = property.Attributes.GetAttribute();
-
- Type elementType = collectionResolver.ElementType;
- IEnumerable types = GetTypeDerivedTypes(collectionResolver.ElementType, property).Where(GenericSelectorTypeFilter);
-
- List> selectorTypes = new List>();
- foreach (Type type in types)
+ public static IEnumerable> GetSubclassSelectorDropdownItems(InspectorProperty property)
{
- string name = GetTypeSubclassPath(type);
- var selectorItem = new GenericSelectorItem(name, type);
+ SubclassSelectorAttribute subclassSelector = property.Attributes.GetAttribute();
+ IEnumerable types = GetTypeDerivedTypes(property.ValueEntry.BaseValueType, property);
- selectorTypes.Add(selectorItem);
- }
+ InspectorProperty parent = property.ParentValueProperty;
+ bool parentIsCollection = parent != null && parent.ChildResolver is ICollectionResolver;
- GenericSelector selector = new GenericSelector(title: "Select Types", true, selectorTypes);
-
- foreach (var menuItem in selector.SelectionTree.EnumerateTree())
- {
- // skip menu item if it has children (a dropdown) or has a null value
- if (menuItem.ChildMenuItems.Count > 0 && menuItem.Value == null)
+ // if the parent is not a collection,
+ // or we don't care about duplicate values...
+ if (!parentIsCollection || subclassSelector.AllowDuplicates)
{
- continue;
+ foreach (var type in types)
+ {
+ yield return GetDropdownItem(type);
+ }
}
-
- Type itemType = (Type)menuItem.Value;
- bool hasDefaultConstructor = itemType.GetConstructor(Type.EmptyTypes) != null;
-
- if (!hasDefaultConstructor)
+ else
{
- menuItem.Icon = EditorIcons.AlertTriangle.Raw;
- menuItem.Name = $"{menuItem.Name} - No Default Constructor";
+ foreach (var type in types)
+ {
+ // the current type should be selectable so that the dropdown
+ // draws the correct Subclass path
+ if (type == property.ValueEntry.TypeOfValue)
+ {
+ yield return GetDropdownItem(type);
+ continue;
+ }
+
+ if (!TypeExistsInPropertyChildren(parent.Children, type))
+ {
+ yield return GetDropdownItem(type);
+ }
+ }
}
- else
+
+ static ValueDropdownItem GetDropdownItem(Type type)
{
- menuItem.Icon = EditorIcons.File.Raw;
+ string name = GetTypeSubclassPath(type);
+ return new ValueDropdownItem(name, type);
}
}
- selector.DrawConfirmSelectionButton = true;
- selector.SelectionConfirmed += AddSelectedTypes;
- selector.ShowInPopup();
+ #endregion
+
+ #region Collection Methods
- // local methods
- bool GenericSelectorTypeFilter(Type type)
+ public static void OpenSubclassCollectionSelector(InspectorProperty property)
{
- if (selectorAttribute.AllowDuplicates)
+ var collectionResolver = (ICollectionResolver)property.ChildResolver;
+ var selectorAttribute = property.Attributes.GetAttribute();
+
+ Type elementType = collectionResolver.ElementType;
+ IEnumerable types = GetTypeDerivedTypes(collectionResolver.ElementType, property).Where(GenericSelectorTypeFilter);
+
+ List> selectorTypes = new List>();
+ foreach (Type type in types)
{
- return true;
+ string name = GetTypeSubclassPath(type);
+ var selectorItem = new GenericSelectorItem(name, type);
+
+ selectorTypes.Add(selectorItem);
}
- return !TypeExistsInPropertyChildren(property.Children, type);
- }
+ GenericSelector selector = new GenericSelector(title: "Select Types", true, selectorTypes);
- void AddSelectedTypes(IEnumerable selectedTypes)
- {
- foreach (var selection in selectedTypes)
+ foreach (var menuItem in selector.SelectionTree.EnumerateTree())
{
- // prevent errors when selecting dropdown values
- if (selection == null)
+ // skip menu item if it has children (a dropdown) or has a null value
+ if (menuItem.ChildMenuItems.Count > 0 && menuItem.Value == null)
{
continue;
}
- bool hasDefaultConstructor = selection.GetConstructor(Type.EmptyTypes) != null;
- object[] values;
+ Type itemType = (Type)menuItem.Value;
+ bool hasDefaultConstructor = itemType.GetConstructor(Type.EmptyTypes) != null;
- if (hasDefaultConstructor)
+ if (!hasDefaultConstructor)
{
- var instance = Activator.CreateInstance(selection);
- values = new object[] { instance };
+ menuItem.Icon = EditorIcons.AlertTriangle.Raw;
+ menuItem.Name = $"{menuItem.Name} - No Default Constructor";
}
else
{
- var instance = Sirenix.Serialization.UnitySerializationUtility.CreateDefaultUnityInitializedObject(selection);
- values = new object[] { instance };
+ menuItem.Icon = EditorIcons.File.Raw;
}
+ }
+
+ selector.DrawConfirmSelectionButton = true;
+ selector.SelectionConfirmed += AddSelectedTypes;
+ selector.ShowInPopup();
+
+ // local methods
+ bool GenericSelectorTypeFilter(Type type)
+ {
+ if (selectorAttribute.AllowDuplicates)
+ {
+ return true;
+ }
+
+ return !TypeExistsInPropertyChildren(property.Children, type);
+ }
- collectionResolver.QueueAdd(values);
- property.Children.Update();
+ void AddSelectedTypes(IEnumerable selectedTypes)
+ {
+ foreach (var selection in selectedTypes)
+ {
+ // prevent errors when selecting dropdown values
+ if (selection == null)
+ {
+ continue;
+ }
+
+ bool hasDefaultConstructor = selection.GetConstructor(Type.EmptyTypes) != null;
+ object[] values;
+
+ if (hasDefaultConstructor)
+ {
+ var instance = Activator.CreateInstance(selection);
+ values = new object[] { instance };
+ }
+ else
+ {
+ var instance = Sirenix.Serialization.UnitySerializationUtility.CreateDefaultUnityInitializedObject(selection);
+ values = new object[] { instance };
+ }
+
+ collectionResolver.QueueAdd(values);
+ property.Children.Update();
+ }
}
}
- }
- public static void BeginDrawBoxedSubclassElement(InspectorProperty property, int index)
- {
- var child = property.Children[index];
- var subclassPath = child.ValueEntry.TypeOfValue.GetAttribute();
- string groupName = subclassPath?.SubClassName ?? child.ValueEntry.TypeOfValue.GetNiceName();
- groupName = groupName.Replace('.', '-');
- SirenixEditorGUI.BeginBox(groupName, centerLabel: true);
- }
+ public static void BeginDrawBoxedSubclassElement(InspectorProperty property, int index)
+ {
+ var child = property.Children[index];
+ var subclassPath = child.ValueEntry.TypeOfValue.GetAttribute();
+ string groupName = subclassPath?.SubClassName ?? child.ValueEntry.TypeOfValue.GetNiceName();
+ groupName = groupName.Replace('.', '-');
+ SirenixEditorGUI.BeginBox(groupName, centerLabel: true);
+ }
- public static void EndDrawBoxedSubclassElement()
- {
- SirenixEditorGUI.EndBox();
- }
+ public static void EndDrawBoxedSubclassElement()
+ {
+ SirenixEditorGUI.EndBox();
+ }
- #endregion
+ #endregion
+ }
}
#endif
\ No newline at end of file
diff --git a/Editor/SubclassSelectorUtilities.cs.meta b/Editor/SubclassSelectorUtilities.cs.meta
new file mode 100644
index 0000000..38d2f75
--- /dev/null
+++ b/Editor/SubclassSelectorUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cceef74008801724f81412e2a4bbe06f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/LICENSE.meta b/LICENSE.meta
new file mode 100644
index 0000000..b43c09f
--- /dev/null
+++ b/LICENSE.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 4fce850ad394c1d4a9e3299cd2f77665
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/README.md.meta b/README.md.meta
new file mode 100644
index 0000000..acc7309
--- /dev/null
+++ b/README.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 82e47bc39d43a1449bac3b8549f0e347
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Examples/PlainClasses.cs b/Samples~/PlainClasses.cs
similarity index 100%
rename from Examples/PlainClasses.cs
rename to Samples~/PlainClasses.cs
diff --git a/Examples/SubclassSelectorExample.cs b/Samples~/SubclassSelectorExample.cs
similarity index 100%
rename from Examples/SubclassSelectorExample.cs
rename to Samples~/SubclassSelectorExample.cs
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..bd70bcf
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "com.spiney.odin-subclass-selector",
+ "version": "1.0.0",
+ "displayName": "Odin Subclass Selector",
+ "description": "",
+ "unity": "2020.3",
+ "unityRelease": "21f1",
+ "licensesUrl": "https://github.com/spiney199/Subclass-Selector/blob/main/LICENSE",
+ "author": {
+ "name": "",
+ "email": "",
+ "url": ""
+ },
+ "devDependencies": {
+ },
+ "dependencies": {
+ },
+ "samples": [
+ {
+ "displayName": "Sample Scripts",
+ "description": "Example scripts using SubclassSelector attribute",
+ "path": "Samples~/"
+ }
+ ]
+}
diff --git a/package.json.meta b/package.json.meta
new file mode 100644
index 0000000..22fba55
--- /dev/null
+++ b/package.json.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 5b1c4525179d20548b93e690e2b7211c
+PackageManifestImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant: