diff --git a/Scripts/Editor/Core/CollectionCustomEditor.cs b/Scripts/Editor/Core/CollectionCustomEditor.cs
index efd109d..f3cc1a3 100644
--- a/Scripts/Editor/Core/CollectionCustomEditor.cs
+++ b/Scripts/Editor/Core/CollectionCustomEditor.cs
@@ -831,9 +831,12 @@ private void CheckGeneratedCodeLocation()
}
}
- public static void SetLastAddedEnum(ScriptableObjectCollectionItem collectionItem)
+ public static ScriptableObjectCollectionItem AddNewItem(ScriptableObjectCollection collection, Type itemType)
{
+ ScriptableObjectCollectionItem collectionItem = collection.AddNew(itemType);
+ Selection.objects = new Object[] {collection};
LAST_ADDED_COLLECTION_ITEM = collectionItem;
+ return collectionItem;
}
}
}
diff --git a/Scripts/Editor/Core/CollectionItemDropdown.cs b/Scripts/Editor/Core/CollectionItemDropdown.cs
index 55f5429..217a886 100644
--- a/Scripts/Editor/Core/CollectionItemDropdown.cs
+++ b/Scripts/Editor/Core/CollectionItemDropdown.cs
@@ -86,10 +86,8 @@ protected override void ItemSelected(AdvancedDropdownItem item)
if (item.name.Equals(CREATE_NEW_TEXT, StringComparison.OrdinalIgnoreCase))
{
ScriptableObjectCollection collection = collections.First();
- ScriptableObjectCollectionItem collectionItem = collection.AddNew(itemType);
- callback.Invoke(collectionItem);
- Selection.objects = new Object[] {collection};
- CollectionCustomEditor.SetLastAddedEnum(collectionItem);
+ ScriptableObjectCollectionItem newItem = CollectionCustomEditor.AddNewItem(collection, itemType);
+ callback.Invoke(newItem);
return;
}
diff --git a/Scripts/Editor/Core/CollectionItemPickerPropertyDrawer.cs b/Scripts/Editor/Core/CollectionItemPickerPropertyDrawer.cs
new file mode 100644
index 0000000..a407282
--- /dev/null
+++ b/Scripts/Editor/Core/CollectionItemPickerPropertyDrawer.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace BrunoMikoski.ScriptableObjectCollections
+{
+ ///
+ /// Lets you pick one or more items from a collection, similar to how an enum field would work if the enum had the
+ /// [Flags] attribute applied to it.
+ ///
+ [CustomPropertyDrawer(typeof(CollectionItemPicker<>))]
+ public class CollectionItemPickerPropertyDrawer : PropertyDrawer
+ {
+ private readonly List
+ tempMaskItems = new List();
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ return EditorGUIUtility.singleLineHeight;
+ }
+
+ private static bool Contains(SerializedProperty itemsProperty, ScriptableObjectCollectionItem item)
+ {
+ for (int i = 0; i < itemsProperty.arraySize; i++)
+ {
+ if (itemsProperty.GetArrayElementAtIndex(i).objectReferenceValue == item)
+ return true;
+ }
+
+ return false;
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ // Figure out the collection item type.
+ Type[] genericArguments = fieldInfo.FieldType.GetGenericArguments();
+ Type collectionItemType = genericArguments[0];
+
+ // Now figure out the collection type.
+ ScriptableObjectCollection collection;
+ CollectionsRegistry.Instance.TryGetCollectionFromItemType(collectionItemType, out collection);
+
+ // TODO: Should this support multiple collections? I'm not sure how that use case works. I just saw it
+ // being used elsewhere.
+
+ if (collection == null)
+ {
+ EditorGUI.LabelField(position, label, new GUIContent("Could not determine collection."));
+ return;
+ }
+
+ // Get the list of items.
+ SerializedProperty itemsProperty = property.FindPropertyRelative("items");
+
+ // Reserve space for the buttons. We can't use AdvancedDropdown for Flags kind of behaviour, so we have
+ // to use a real MaskField to get it done. I still want to support adding a new entry from the inspector
+ // though. For that reason I am adding an Add button next to the dropdown.
+ position.width -= CollectionItemPropertyDrawer.BUTTON_WIDTH * 2;
+ Rect goToButtonRect = new Rect(
+ position.xMax, position.y, CollectionItemPropertyDrawer.BUTTON_WIDTH, position.height);
+ Rect addButtonRect = new Rect(
+ goToButtonRect.xMax, position.y, CollectionItemPropertyDrawer.BUTTON_WIDTH, position.height);
+
+ // If the collection is empty, we cannot use MaskField with an empty array because that throws exceptions.
+ // Because of that we treat it as a special case where we draw a disabled PopUp field. You can then use
+ // the Go To and Add buttons to add an item to the collection and then you can begin picking.
+ if (collection.Count == 0)
+ {
+ // Calculate the rects for the label and the add button and such.
+ Rect labelRect = new Rect(position.x, position.y, EditorGUIUtility.labelWidth, position.height);
+ Rect valueRect = new Rect(
+ position.x + EditorGUIUtility.labelWidth, position.y, position.width - EditorGUIUtility.labelWidth,
+ position.height);
+
+ // Draw the label.
+ EditorGUI.LabelField(labelRect, label);
+
+ // Draw the inactive dropdown.
+ bool wasGuiEnabled = GUI.enabled;
+ GUI.enabled = false;
+ EditorGUI.Popup(valueRect, GUIContent.none, 0, new[] {new GUIContent("")});
+ GUI.enabled = wasGuiEnabled;
+ }
+ else
+ {
+ // Create an array of displayed options.
+ string[] displayedOptions = new string[collection.Count];
+ int mask = 0;
+ for (int i = 0; i < collection.Count; i++)
+ {
+ displayedOptions[i] = collection[i].name;
+ if (Contains(itemsProperty, collection[i]))
+ mask |= 1 << i;
+ }
+
+ int maskNew = EditorGUI.MaskField(position, label, mask, displayedOptions);
+ if (mask != maskNew)
+ {
+ // First convert the newly selected mask to a list of items.
+ tempMaskItems.Clear();
+ for (int i = 0; i < collection.Count; i++)
+ {
+ int flag = 1 << i;
+ if ((maskNew & flag) == flag)
+ tempMaskItems.Add(collection[i]);
+ }
+
+ // Now update the property to have the values in that list...
+ itemsProperty.arraySize = tempMaskItems.Count;
+ for (int i = 0; i < tempMaskItems.Count; i++)
+ {
+ itemsProperty.GetArrayElementAtIndex(i).objectReferenceValue = tempMaskItems[i];
+ }
+ }
+ }
+
+ // Draw the Go To button.
+ bool shouldGoToCollection = GUI.Button(goToButtonRect, CollectionEditorGUI.ARROW_RIGHT_CHAR);
+ if (shouldGoToCollection)
+ Selection.activeObject = collection;
+
+ // Draw the add button.
+ bool shouldCreateNewItem = GUI.Button(addButtonRect, "+");
+ if (shouldCreateNewItem)
+ CollectionCustomEditor.AddNewItem(collection, collectionItemType);
+ }
+ }
+}
diff --git a/Scripts/Editor/Core/CollectionItemPickerPropertyDrawer.cs.meta b/Scripts/Editor/Core/CollectionItemPickerPropertyDrawer.cs.meta
new file mode 100644
index 0000000..4faa4ac
--- /dev/null
+++ b/Scripts/Editor/Core/CollectionItemPickerPropertyDrawer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 264d8d94cfdf3a346bf47076fd933599
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Editor/Core/CollectionItemPropertyDrawer.cs b/Scripts/Editor/Core/CollectionItemPropertyDrawer.cs
index 5678d96..8521561 100644
--- a/Scripts/Editor/Core/CollectionItemPropertyDrawer.cs
+++ b/Scripts/Editor/Core/CollectionItemPropertyDrawer.cs
@@ -10,6 +10,8 @@ namespace BrunoMikoski.ScriptableObjectCollections
[CustomPropertyDrawer(typeof(ScriptableObjectCollectionItem), true)]
public class CollectionItemPropertyDrawer : PropertyDrawer
{
+ public const float BUTTON_WIDTH = 30;
+
private static readonly CollectionItemEditorOptionsAttribute DefaultAttribute
= new CollectionItemEditorOptionsAttribute();
@@ -210,7 +212,7 @@ private void DrawGotoButton(ref Rect popupRect, ScriptableObjectCollectionItem c
if (!OptionsAttribute.ShouldDrawGotoButton) return;
Rect buttonRect = popupRect;
- buttonRect.width = 30;
+ buttonRect.width = BUTTON_WIDTH;
buttonRect.height = 18;
popupRect.width -= buttonRect.width;
buttonRect.x += popupRect.width;
@@ -227,7 +229,7 @@ private void DrawEditFoldoutButton(ref Rect popupRect, ScriptableObjectCollectio
return;
Rect buttonRect = popupRect;
- buttonRect.width = 30;
+ buttonRect.width = BUTTON_WIDTH;
buttonRect.height = 18;
popupRect.width -= buttonRect.width;
buttonRect.x += popupRect.width;
diff --git a/Scripts/Runtime/Core/CollectionItemPicker.cs b/Scripts/Runtime/Core/CollectionItemPicker.cs
new file mode 100644
index 0000000..0ebfc8e
--- /dev/null
+++ b/Scripts/Runtime/Core/CollectionItemPicker.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace BrunoMikoski.ScriptableObjectCollections
+{
+ ///
+ /// Collection Item Picker lets you pick one or more items from a collection, similar to how an enum field would
+ /// work if the enum had the [Flags] attribute applied to it.
+ ///
+ [Serializable]
+ public class CollectionItemPicker : IList
+ where ItemType : ScriptableObjectCollectionItem
+ {
+ [SerializeField] private List items = new List();
+ public List Items => items;
+
+ // Implement IList and forward its members to items. This way we can conveniently use this thing as a list.
+ #region IList members implementation
+
+ public IEnumerator GetEnumerator()
+ {
+ return items.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable)items).GetEnumerator();
+ }
+
+ public void Add(ItemType item)
+ {
+ items.Add(item);
+ }
+
+ public void Clear()
+ {
+ items.Clear();
+ }
+
+ public bool Contains(ItemType item)
+ {
+ return items.Contains(item);
+ }
+
+ public void CopyTo(ItemType[] array, int arrayIndex)
+ {
+ items.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(ItemType item)
+ {
+ return items.Remove(item);
+ }
+
+ public int Count => items.Count;
+
+ public bool IsReadOnly => false;
+
+ public int IndexOf(ItemType item)
+ {
+ return items.IndexOf(item);
+ }
+
+ public void Insert(int index, ItemType item)
+ {
+ items.Insert(index, item);
+ }
+
+ public void RemoveAt(int index)
+ {
+ items.RemoveAt(index);
+ }
+
+ public ItemType this[int index]
+ {
+ get => items[index];
+ set => items[index] = value;
+ }
+ #endregion
+ }
+}
diff --git a/Scripts/Runtime/Core/CollectionItemPicker.cs.meta b/Scripts/Runtime/Core/CollectionItemPicker.cs.meta
new file mode 100644
index 0000000..ca93e84
--- /dev/null
+++ b/Scripts/Runtime/Core/CollectionItemPicker.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4a66a5f731a02d84e8af0aecf1d4aca8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: