From cedbc6329fc6bafb19b3b3e652aeda2583f9aa13 Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Wed, 30 Oct 2024 12:16:54 +0000 Subject: [PATCH 1/6] fix: unity 6 renaming --- .../CustomEditors/CollectionCustomEditor.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs index 3a0df6e..b7fa0e0 100644 --- a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs +++ b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs @@ -885,7 +885,7 @@ private void RenameItemAtIndex(MouseDownEvent evt, int targetIndex) private void RenameItemAtIndex(int targetIndex) { - ClearCurrentRenamingItem(); + ClearCurrentRenamingItem(false); Undo.RecordObject(filteredItems[targetIndex], "Rename Item"); VisualElement targetElement = collectionItemListView.GetRootElementForIndex(targetIndex); @@ -895,19 +895,24 @@ private void RenameItemAtIndex(int targetIndex) currentRenamingLabel.style.display = DisplayStyle.None; currentRenamingTextField = targetElement.Q(); - currentRenamingTextField.RegisterCallback(OnRenamingAssetLostFocus); - currentRenamingTextField.RegisterValueChangedCallback(_ => OnFinishRenamingItem(targetIndex)); currentRenamingTextField.SetValueWithoutNotify(currentRenamingLabel.text); currentRenamingTextField.style.display = DisplayStyle.Flex; currentRenamingTextField.SelectAll(); currentRenamingTextField.Focus(); collectionItemListView.ClearSelection(); + + currentRenamingTextField.schedule.Execute(() => + { + currentRenamingTextField.SelectAll(); + currentRenamingTextField.RegisterCallback(OnRenamingAssetLostFocus); + currentRenamingTextField.RegisterValueChangedCallback(_ => OnFinishRenamingItem(targetIndex)); + }).ExecuteLater(0); } private void OnRenamingAssetLostFocus(FocusOutEvent evt) { - ClearCurrentRenamingItem(); + ClearCurrentRenamingItem(false); } private void OnFinishRenamingItem(int targetIndex) @@ -929,18 +934,22 @@ private void OnFinishRenamingItem(int targetIndex) AssetDatabase.SaveAssetIfDirty(asset); } - ClearCurrentRenamingItem(); + ClearCurrentRenamingItem(true); } - private void ClearCurrentRenamingItem() + private void ClearCurrentRenamingItem(bool renamedSuccessfully) { if (currentRenamingTextField == null) return; + currentRenamingTextField.UnregisterCallback(OnRenamingAssetLostFocus); currentRenamingTextField.style.display = DisplayStyle.None; currentRenamingLabel.style.display = DisplayStyle.Flex; - currentRenamingLabel.text = currentRenamingTextField.text; - currentRenamingTextField.SetValueWithoutNotify(""); + if (renamedSuccessfully) + { + currentRenamingLabel.text = currentRenamingTextField.text; + currentRenamingTextField.SetValueWithoutNotify(""); + } currentRenamingLabel = null; currentRenamingTextField = null; } From e0a6b476d3c4e4d015d99bec1fa99775e4885bc9 Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Thu, 31 Oct 2024 09:09:50 +0000 Subject: [PATCH 2/6] fix: null importer check --- Scripts/Editor/Core/SOCSettings.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Scripts/Editor/Core/SOCSettings.cs b/Scripts/Editor/Core/SOCSettings.cs index 5af907e..de383fb 100644 --- a/Scripts/Editor/Core/SOCSettings.cs +++ b/Scripts/Editor/Core/SOCSettings.cs @@ -262,10 +262,8 @@ public CollectionSettings GetOrCreateCollectionSettings(ScriptableObjectCollecti string path = AssetDatabase.GetAssetPath(collection); AssetImporter importer = AssetImporter.GetAtPath(path); - string collectionSettingsData = importer.userData; - CollectionSettings collectionSetting; - if (string.IsNullOrEmpty(collectionSettingsData)) + if (importer == null || string.IsNullOrEmpty(importer.userData)) { collectionSetting = new CollectionSettings(collection); } @@ -302,4 +300,4 @@ public void SaveCollectionSettings(ScriptableObjectCollection collection, bool f settings.Save(); } } -} +} \ No newline at end of file From 6e813afc1bb12ebc165ce08845a757ad7df4663b Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Tue, 19 Nov 2024 15:11:45 +0000 Subject: [PATCH 3/6] fix: picker with null items and proper type filter --- .../CollectionItemPickerPropertyDrawer.cs | 43 ++++++ .../Core/CollectionItemIndirectReference.cs | 11 +- Scripts/Runtime/Core/CollectionItemPicker.cs | 9 +- Scripts/Runtime/Core/CollectionItemQuery.cs | 139 ++++++++++++++++++ .../Runtime/Core/CollectionItemQuery.cs.meta | 3 + Scripts/Runtime/Core/CollectionsRegistry.cs | 56 ++++--- 6 files changed, 239 insertions(+), 22 deletions(-) create mode 100644 Scripts/Runtime/Core/CollectionItemQuery.cs create mode 100644 Scripts/Runtime/Core/CollectionItemQuery.cs.meta diff --git a/Scripts/Editor/PropertyDrawers/CollectionItemPickerPropertyDrawer.cs b/Scripts/Editor/PropertyDrawers/CollectionItemPickerPropertyDrawer.cs index bb24ad3..018b41c 100644 --- a/Scripts/Editor/PropertyDrawers/CollectionItemPickerPropertyDrawer.cs +++ b/Scripts/Editor/PropertyDrawers/CollectionItemPickerPropertyDrawer.cs @@ -265,6 +265,8 @@ private void Initialize(SerializedProperty property) if (initializedPropertiesPaths.Contains(property.propertyPath)) return; + ValidateIndirectReferencesInProperty(property); + Type arrayOrListType = fieldInfo.FieldType.GetArrayOrListType(); Type itemType = arrayOrListType ?? fieldInfo.FieldType; @@ -297,5 +299,46 @@ private void Initialize(SerializedProperty property) labelStyle = assetLabelStyle; initializedPropertiesPaths.Add(property.propertyPath); } + + private void ValidateIndirectReferencesInProperty(SerializedProperty property) + { + SerializedProperty indirectReferencesProperty = property.FindPropertyRelative(ITEMS_PROPERTY_NAME); + + bool changed = false; + for (int i = indirectReferencesProperty.arraySize - 1; i >= 0; i--) + { + SerializedProperty elementProperty = indirectReferencesProperty.GetArrayElementAtIndex(i); + + long collectionGUIDValueA = elementProperty.FindPropertyRelative(COLLECTION_GUID_VALUE_A).longValue; + long collectionGUIDValueB = elementProperty.FindPropertyRelative(COLLECTION_GUID_VALUE_B).longValue; + LongGuid collectionGUID = new(collectionGUIDValueA, collectionGUIDValueB); + + long itemGUIDValueA = elementProperty.FindPropertyRelative(COLLECTION_ITEM_GUID_VALUE_A).longValue; + long itemGUIDValueB = elementProperty.FindPropertyRelative(COLLECTION_ITEM_GUID_VALUE_B).longValue; + LongGuid itemGUID = new(itemGUIDValueA, itemGUIDValueB); + + bool validReference = false; + if(CollectionsRegistry.Instance.TryGetCollectionByGUID(collectionGUID, out ScriptableObjectCollection collection)) + { + if (collection.TryGetItemByGUID(itemGUID, out _)) + { + validReference = true; + } + } + + if (!validReference) + { + indirectReferencesProperty.DeleteArrayElementAtIndex(i); + changed = true; + } + } + + if (changed) + { + indirectReferencesProperty.serializedObject.ApplyModifiedProperties(); + } + } + + } } \ No newline at end of file diff --git a/Scripts/Runtime/Core/CollectionItemIndirectReference.cs b/Scripts/Runtime/Core/CollectionItemIndirectReference.cs index 8c9900e..08d1495 100644 --- a/Scripts/Runtime/Core/CollectionItemIndirectReference.cs +++ b/Scripts/Runtime/Core/CollectionItemIndirectReference.cs @@ -85,7 +85,7 @@ public TObject Ref private bool TryResolveReference(out TObject result) { - if (CollectionsRegistry.Instance.TryGetCollectionByGUID(CollectionGUID, out ScriptableObjectCollection collection)) + if (CollectionsRegistry.Instance.TryGetCollectionByGUID(CollectionGUID, out ScriptableObjectCollection collection)) { if (collection.TryGetItemByGUID(CollectionItemGUID, out ScriptableObject item)) { @@ -103,8 +103,13 @@ private bool TryResolveReference(out TObject result) if (!string.IsNullOrEmpty(itemLastKnownName)) { - if(collection.TryGetItemByName(itemLastKnownName, out result)) + if(collection.TryGetItemByName(itemLastKnownName, out ScriptableObject possibleResult)) { + result = possibleResult as TObject; + if (result == null) + { + return false; + } SetCollectionItem(result); return true; } @@ -148,4 +153,4 @@ public void SetCollection(ScriptableObjectCollection targetCollection) collectionLastKnowName = targetCollection.name; } } -} +} \ No newline at end of file diff --git a/Scripts/Runtime/Core/CollectionItemPicker.cs b/Scripts/Runtime/Core/CollectionItemPicker.cs index a180ba0..203b15e 100644 --- a/Scripts/Runtime/Core/CollectionItemPicker.cs +++ b/Scripts/Runtime/Core/CollectionItemPicker.cs @@ -33,7 +33,14 @@ public List Items for (int i = 0; i < indirectReferences.Count; i++) { - cachedItems.Add(indirectReferences[i].Ref); + CollectionItemIndirectReference collectionItemIndirectReference = indirectReferences[i]; + if (!collectionItemIndirectReference.IsValid() || collectionItemIndirectReference.Ref == null) + { + indirectReferences.RemoveAt(i); + continue; + } + + cachedItems.Add(collectionItemIndirectReference.Ref); } isDirty = false; diff --git a/Scripts/Runtime/Core/CollectionItemQuery.cs b/Scripts/Runtime/Core/CollectionItemQuery.cs new file mode 100644 index 0000000..df97d8b --- /dev/null +++ b/Scripts/Runtime/Core/CollectionItemQuery.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace BrunoMikoski.ScriptableObjectCollections.Picker +{ + [Serializable] + public class CollectionItemQuery where T : ScriptableObject, ISOCItem + { + public enum MatchType + { + Any = 0, + All = 1, + NotAny = 2, + NotAll = 3, + } + + [Serializable] + public class QuerySet + { + [SerializeField] + private MatchType matchType; + public MatchType MatchType => matchType; + + [SerializeField] + private CollectionItemPicker picker; + public CollectionItemPicker Picker => picker; + + public override string ToString() + { + return string.Join(", ", picker.Select(o => o.name)); + } + } + + [SerializeField] + private QuerySet[] query = Array.Empty(); + + public bool Matches(params T[] targetItems) + { + return Matches(targetItems, out _); + } + + public bool Matches(IList targetItems) + { + return Matches(targetItems, out _); + } + + public override string ToString() + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (var querySet in query) + { + stringBuilder.Append(querySet.MatchType); + stringBuilder.Append(" "); + stringBuilder.Append(querySet); + stringBuilder.Append(" "); + } + + return stringBuilder.ToString(); + } + + public bool Matches(IList targetItems, out int resultMatchCount) + { + resultMatchCount = 0; + + if (query.Length == 0) + return true; + + for (int i = 0; i < query.Length; i++) + { + QuerySet querySet = query[i]; + + int matchCount = 0; + for (int j = 0; j < querySet.Picker.Count; j++) + { + T socItem = querySet.Picker[j]; + if (!socItem) + { + continue; + } + for (int k = 0; k < targetItems.Count; k++) + { + T item = targetItems[k]; + if (!item) + { + continue; + } + if (socItem.GUID.Equals(item.GUID)) + { + matchCount++; + } + } + } + + resultMatchCount += matchCount; + + switch (querySet.MatchType) + { + case MatchType.NotAny: + { + if (matchCount > 0) + { + return false; + } + break; + } + case MatchType.NotAll: + { + if(matchCount == querySet.Picker.Count) + { + return false; + } + break; + } + case MatchType.Any: + { + if (matchCount == 0) + { + return false; + } + break; + } + case MatchType.All: + { + if (matchCount < querySet.Picker.Count) + { + return false; + } + break; + } + } + } + + return resultMatchCount > 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Runtime/Core/CollectionItemQuery.cs.meta b/Scripts/Runtime/Core/CollectionItemQuery.cs.meta new file mode 100644 index 0000000..98a2d3a --- /dev/null +++ b/Scripts/Runtime/Core/CollectionItemQuery.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1612857925484c12af46b11a31f6da81 +timeCreated: 1732011060 \ No newline at end of file diff --git a/Scripts/Runtime/Core/CollectionsRegistry.cs b/Scripts/Runtime/Core/CollectionsRegistry.cs index 34d156d..d4492ea 100644 --- a/Scripts/Runtime/Core/CollectionsRegistry.cs +++ b/Scripts/Runtime/Core/CollectionsRegistry.cs @@ -122,18 +122,37 @@ public List GetAllCollectionItemsOfType(Type targetItemType) return results; } - public bool TryGetCollectionsOfItemType(Type targetType, out List results) { - List availables = new List(); + List availables = new(); + int minDistance = int.MaxValue; + for (int i = 0; i < Collections.Count; i++) { ScriptableObjectCollection collection = Collections[i]; if (collection == null) continue; - - if (collection.GetItemType() == targetType || collection.GetItemType().IsAssignableFrom(targetType)) + + Type itemType = collection.GetItemType(); + + if (itemType == null) + continue; + + if (itemType == typeof(ISOCItem) || itemType == typeof(ScriptableObjectCollectionItem) || itemType.BaseType == null) + continue; + + if (!itemType.IsAssignableFrom(targetType)) + continue; + + int distance = GetInheritanceDistance(targetType, itemType); + if (distance < minDistance) + { + availables.Clear(); + availables.Add(collection); + minDistance = distance; + } + else if (distance == minDistance) { availables.Add(collection); } @@ -145,21 +164,22 @@ public bool TryGetCollectionsOfItemType(Type targetType, out List(); - for (int i = 0; i < availables.Count; i++) + private int GetInheritanceDistance(Type fromType, Type toType) + { + int distance = 0; + Type currentType = fromType; + while (currentType != null && currentType != toType) { - ScriptableObjectCollection collection = availables[i]; - if (collection.GetItemType() == targetType) - results.Add(collection); + currentType = currentType.BaseType; + distance++; } - - return results.Count > 0; + if (currentType == toType) + return distance; + return int.MaxValue; } public bool TryGetCollectionsOfItemType(out List> results) @@ -308,13 +328,13 @@ public bool TryGetCollectionByGUID(LongGuid targetGUID, out T resultCollectio return false; } - public bool TryGetCollectionByGUID(LongGuid targetGUID, out ScriptableObjectCollection resultCollection) where T : ScriptableObject, ISOCItem + public bool TryGetCollectionByGUID(LongGuid targetGUID, out ScriptableObjectCollection resultCollection) where T : ScriptableObject, ISOCItem { if (targetGUID.IsValid()) { if (TryGetCollectionByGUID(targetGUID, out ScriptableObjectCollection foundCollection)) { - resultCollection = foundCollection as ScriptableObjectCollection; + resultCollection = foundCollection as ScriptableObjectCollection; return true; } } From d19406b27d35dd9083a8ab17d78cdf7330f7e3ea Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Mon, 27 Jan 2025 10:22:36 +0000 Subject: [PATCH 4/6] add: new move between available collections mode --- .../CustomEditors/CollectionCustomEditor.cs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs index b7fa0e0..253e491 100644 --- a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs +++ b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs @@ -823,6 +823,57 @@ private void ShowOptionsForIndex(MouseUpEvent evt, int targetIndex) } ); + + List possibleAnotherCollections = GetPossibleAnotherCollections(); + + if (possibleAnotherCollections.Count > 0) + { + foreach (ScriptableObjectCollection scriptableObjectCollection in possibleAnotherCollections) + { + if (scriptableObjectCollection == collection) + continue; + + menu.AddItem( + new GUIContent($"Move to {(AssetDatabase.GetAssetPath(scriptableObject).Replace("/","\\").Replace("Assets", "").Replace(".asset", ""))}"), + false, + () => + { + if (selectedItemsCount > 0) + { + if (!EditorUtility.DisplayDialog($"Move {collectionItemListView.selectedIndices.Count()} Items", + $"Are you sure you want to move {collectionItemListView.selectedIndices.Count()} items, from {AssetDatabase.GetAssetPath(collection)} to {AssetDatabase.GetAssetPath(scriptableObject)}", "Yes", "No")) + { + return; + } + + List moveItems = + new List(); + foreach (int selectedIndex in collectionItemListView.selectedIndices) + { + moveItems.Add(filteredItems[selectedIndex]); + } + + foreach (ScriptableObject item in moveItems) + { + MoveItem(item, scriptableObjectCollection); + } + + } + else + { + if (!EditorUtility.DisplayDialog($"Move Item", + $"Are you sure you want to move {filteredItems[^1].name}, from {AssetDatabase.GetAssetPath(collection)} to {AssetDatabase.GetAssetPath(scriptableObject)}", "Yes", "No")) + { + return; + } + + MoveItem(filteredItems[targetIndex], scriptableObjectCollection); + } + } + ); + } + } + menu.AddSeparator(""); menu.AddItem( new GUIContent("Select Asset"), @@ -843,6 +894,26 @@ private void ShowOptionsForIndex(MouseUpEvent evt, int targetIndex) menu.ShowAsContext(); } + private void MoveItem(ScriptableObject item, ScriptableObjectCollection targetCollection) + { + Undo.RecordObject(collection, "Move Item"); + Undo.RecordObject(targetCollection, "Move Item"); + + collection.Remove(item); + targetCollection.Add(item); + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + ReloadFilteredItems(); + } + + private List GetPossibleAnotherCollections() + { + CollectionsRegistry.Instance.TryGetCollectionsOfItemType(collection.GetItemType(), out List collections); + return collections; + } + private void SelectItemAtIndex(params int[] index) { Object[] selectedObjects = new Object[index.Length]; From ca1c2e2eb550a4387b5ee58cb6cd13e408348c30 Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Mon, 27 Jan 2025 10:22:50 +0000 Subject: [PATCH 5/6] fix: removing non automatically loaded when exiting edit mode. --- Scripts/Editor/Core/EditorBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/Core/EditorBehaviour.cs b/Scripts/Editor/Core/EditorBehaviour.cs index b1ee22a..e430c26 100644 --- a/Scripts/Editor/Core/EditorBehaviour.cs +++ b/Scripts/Editor/Core/EditorBehaviour.cs @@ -12,7 +12,7 @@ static EditorBehaviour() private static void OnPlayModeStateChanged(PlayModeStateChange playModeStateChange) { - if (playModeStateChange == PlayModeStateChange.EnteredPlayMode) + if (playModeStateChange == PlayModeStateChange.ExitingEditMode) { CollectionsRegistry.Instance.RemoveNonAutomaticallyInitializedCollections(); } From 254dd0fa8d66398b295f6b1b81738ae3e0a759e6 Mon Sep 17 00:00:00 2001 From: Bruno Mikoski Date: Mon, 27 Jan 2025 10:29:49 +0000 Subject: [PATCH 6/6] add: renaming on right click --- .../Editor/CustomEditors/CollectionCustomEditor.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs index 253e491..d9e250d 100644 --- a/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs +++ b/Scripts/Editor/CustomEditors/CollectionCustomEditor.cs @@ -891,6 +891,18 @@ private void ShowOptionsForIndex(MouseUpEvent evt, int targetIndex) } ); + if (selectedItemsCount == 1) + { + menu.AddItem( + new GUIContent("Rename Asset"), + false, + () => + { + RenameItemAtIndex(collectionItemListView.selectedIndices.First()); + } + ); + } + menu.ShowAsContext(); }