using System; using System.Linq; using System.Collections.Generic; using UnityEditorInternal; using UnityEngine; using UnityEngine.U2D.Animation; namespace UnityEditor.U2D.Animation { internal class SpriteLibraryDataInspector { class Style { public GUIContent duplicateWarningText = EditorGUIUtility.TrTextContent("Duplicate name found or name hash clashes. Please use a different name"); public GUIContent duplicateWarning; public int lineSpacing = 3; public int spriteGridSize = 64; public float gridPadding = EditorGUIUtility.standardVerticalSpacing * 2; public int gridHeaderSize = 20; public int gridFooterSize = 20; public float gridHeight; public Vector2 gridSize; public GUIStyle footerBackground; public GUIStyle boxBackground; public GUIStyle preButton; public GUIStyle headerBackground; public GUIStyle gridList; public GUIContent iconToolbarPlus = EditorGUIUtility.TrIconContent("Toolbar Plus", "Add to list"); public GUIContent iconToolbarMinus = EditorGUIUtility.TrIconContent("Toolbar Minus", "Remove selection or last element from list"); public GUIContent overrideIcon = EditorGUIUtility.TrIconContent("PrefabOverlayAdded Icon"); public string newCategoryText = L10n.Tr("New Category"); public string categoryLabel = L10n.Tr("Category"); public float categoryElementHeight; public float categoryTextFieldHeight; public string newEntryText = L10n.Tr("Entry"); public float categoryLabelWidth = 100; public Style() { gridSize = new Vector2(spriteGridSize + gridPadding * 2, spriteGridSize + lineSpacing + EditorGUIUtility.singleLineHeight + gridPadding * 2); duplicateWarning = EditorGUIUtility.TrIconContent("console.warnicon.sml", duplicateWarningText.text); gridHeight = gridSize.y +gridPadding + gridFooterSize; categoryTextFieldHeight = EditorGUIUtility.singleLineHeight + lineSpacing; categoryElementHeight = gridHeight + categoryTextFieldHeight + gridHeaderSize; } public void InitStyle(Event currentEvent) { if (footerBackground == null && currentEvent.type == EventType.Repaint) { footerBackground = "RL Footer"; boxBackground = "RL Background"; preButton = "RL FooterButton"; headerBackground = "RL Header"; gridList = "GridList"; } } } class SpriteCategoryGridState { public Vector2 scrollPos; public int selectedIndex; public static SpriteCategoryGridState Default => new SpriteCategoryGridState() { scrollPos = Vector2.zero, selectedIndex = 0 }; } private Style m_Style; private SerializedProperty m_Library; private List m_GridStates = new List(); private ReorderableList m_LabelReorderableList; public SpriteLibraryDataInspector(SerializedObject so, SerializedProperty library) { m_Style = new Style(); m_Library = library; m_LabelReorderableList = new ReorderableList(so, library, true, false, true, true); m_LabelReorderableList.drawElementCallback = DrawElement; m_LabelReorderableList.elementHeight = m_Style.categoryTextFieldHeight; m_LabelReorderableList.elementHeightCallback = delegate(int index) { return m_Style.categoryElementHeight; }; m_LabelReorderableList.onAddCallback = OnAddCallback; m_LabelReorderableList.onCanRemoveCallback = OnCanRemoveCallback; } public static void UpdateLibraryWithNewMainLibrary(SpriteLibraryAsset spriteLib, SerializedProperty library) { var emptyStringArray = new string[0]; var categories = spriteLib != null ? spriteLib.GetCategoryNames() : emptyStringArray; // populate new primary int newCatgoryIndex = 0; foreach (var newCategory in categories) { SerializedProperty existingCategory = null; if (library.arraySize > 0) { var cat = library.GetArrayElementAtIndex(0); for (int i = 0; i < library.arraySize; ++i) { if (cat.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue == newCategory) { existingCategory = cat; if(i != newCatgoryIndex) library.MoveArrayElement(i, newCatgoryIndex); break; } cat.Next(false); } } if (existingCategory != null) { if(!existingCategory.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue) existingCategory.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue = true; } else { library.InsertArrayElementAtIndex(newCatgoryIndex); existingCategory = library.GetArrayElementAtIndex(newCatgoryIndex); SetPropertyName(existingCategory, newCategory); existingCategory.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue = true; existingCategory.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntryCount).intValue = 0; existingCategory.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries).arraySize = 0; } newCatgoryIndex++; var newEntries = spriteLib.GetCategoryLabelNames(newCategory); var entries = existingCategory.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries); int newEntryIndex = 0; foreach (var newEntry in newEntries) { SerializedProperty cacheEntry = null; if (entries.arraySize > 0) { var ent = entries.GetArrayElementAtIndex(0); for (int j = 0; j < entries.arraySize; ++j) { if (ent.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue == newEntry) { cacheEntry = ent; if(j != newEntryIndex) entries.MoveArrayElement(j, newEntryIndex); break; } ent.Next(false); } } var mainSprite = spriteLib.GetSprite(newCategory, newEntry); if (cacheEntry == null) { entries.InsertArrayElementAtIndex(newEntryIndex); cacheEntry = entries.GetArrayElementAtIndex(newEntryIndex); SetPropertyName(cacheEntry, newEntry); cacheEntry.FindPropertyRelative(SpriteLibraryPropertyString.spriteOverride) .objectReferenceValue = mainSprite; } ++newEntryIndex; if(!cacheEntry.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue) cacheEntry.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue = true; if(cacheEntry.FindPropertyRelative(SpriteLibraryPropertyString.sprite).objectReferenceValue != mainSprite) cacheEntry.FindPropertyRelative(SpriteLibraryPropertyString.sprite).objectReferenceValue = mainSprite; } } // Remove any library or entry that is not in primary and not overridden for (int i = 0; i < library.arraySize; ++i) { var categoryProperty = library.GetArrayElementAtIndex(i); var categoryEntriesProperty = categoryProperty.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries); var categoryFromMainProperty = categoryProperty.FindPropertyRelative(SpriteLibraryPropertyString.fromMain); var categoryName = categoryProperty.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue; var categoryInPrimary = categories.Contains(categoryName); var entriesInPrimary = categoryInPrimary ? spriteLib.GetCategoryLabelNames(categoryName) : emptyStringArray; var categoryOverride = 0; for (int j = 0; j < categoryEntriesProperty.arraySize; ++j) { var entry = categoryEntriesProperty.GetArrayElementAtIndex(j); var entryName = entry.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue; var entryInPrimary = entriesInPrimary.Contains(entryName); var entryFromMainProperty = entry.FindPropertyRelative(SpriteLibraryPropertyString.fromMain); var overrideSpriteProperty = entry.FindPropertyRelative(SpriteLibraryPropertyString.spriteOverride); var spriteProperty = entry.FindPropertyRelative(SpriteLibraryPropertyString.sprite); if (!entryInPrimary) { // Entry no longer in new primary. // Check for override and set it to us if (entryFromMainProperty.boolValue) { if (overrideSpriteProperty.objectReferenceValue == spriteProperty.objectReferenceValue) { categoryEntriesProperty.DeleteArrayElementAtIndex(j); --j; continue; } } if(entryFromMainProperty.boolValue) entryFromMainProperty.boolValue = false; if(spriteProperty.objectReferenceValue != overrideSpriteProperty.objectReferenceValue) spriteProperty.objectReferenceValue = overrideSpriteProperty.objectReferenceValue; ++categoryOverride; } else { // Check if sprite has been override if(spriteProperty.objectReferenceValue != overrideSpriteProperty.objectReferenceValue) ++categoryOverride; } } if (!categoryInPrimary && categoryEntriesProperty.arraySize == 0 && categoryFromMainProperty.boolValue) { library.DeleteArrayElementAtIndex(i); --i; continue; } // since there is override, and we removed the main. This category now // belows to the library if (!categoryInPrimary) { if(categoryFromMainProperty.boolValue) categoryFromMainProperty.boolValue = false; } else { if(categoryProperty.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntryCount).intValue != categoryOverride) categoryProperty.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntryCount).intValue = categoryOverride; } } } public void OnGUI() { m_Style.InitStyle(Event.current); m_LabelReorderableList.DoLayoutList(); HandleCategoryCreateSpriteDragAndDrop(); } void HandleCategoryCreateSpriteDragAndDrop() { bool dragAccepted = false; switch (Event.current.type) { case EventType.DragPerform: foreach (var obj in DragAndDrop.objectReferences) { if (obj is Sprite || obj is Texture2D) { dragAccepted = true; } } if (dragAccepted) { DragAndDrop.AcceptDrag(); foreach (var obj in DragAndDrop.objectReferences) { if (obj is Sprite) { var category = AddCategory(obj.name, false); AddSpriteToCategory(category, (Sprite)obj); } else if (obj is Texture2D) { var sprites = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(obj)) .Where(x => x is Sprite); foreach (var s in sprites) { var category = AddCategory(s.name, false); AddSpriteToCategory(category, (Sprite)s); } } } } Event.current.Use(); break; case EventType.DragUpdated: if (DragAndDrop.objectReferences.Count(x => x is Sprite || x is Texture2D) > 0) DragAndDrop.visualMode = DragAndDropVisualMode.Copy; else DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; Event.current.Use(); break; } } SerializedProperty IsCategoryNameInUsed(string name) { if (m_Library.arraySize == 0) return null; var nameHash = SpriteLibraryUtility.GetStringHash(name); var sp = m_Library.GetArrayElementAtIndex(0); for (int i = 0; i < m_Library.arraySize; ++i) { var nameProperty = sp.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue; var hash = SpriteLibraryUtility.GetStringHash(nameProperty); if (nameProperty == name || nameHash == hash) return sp; sp.Next(false); } return null; } void DrawElement(Rect rect, int index, bool selected, bool focused) { DrawCategory(rect, index); } void DrawCategory(Rect rect, int index) { var categorySerializedProperty = m_Library.GetArrayElementAtIndex(index); var catRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight); var vaRect = new Rect(rect.x, rect.y + m_Style.categoryTextFieldHeight, rect.width, EditorGUIUtility.singleLineHeight); var categoryName = categorySerializedProperty.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue; var fromMain = categorySerializedProperty.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue; using (new EditorGUI.DisabledScope(fromMain)) { EditorGUI.BeginChangeCheck(); var oldLabelWidth = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = m_Style.categoryLabelWidth; var newCatName = EditorGUI.TextField(catRect, m_Style.categoryLabel, categoryName); EditorGUIUtility.labelWidth = oldLabelWidth; if (EditorGUI.EndChangeCheck()) { newCatName = newCatName.Trim(); if (categoryName != newCatName) { // Check if this nameLabel is already taken if (IsCategoryNameInUsed(newCatName) == null) SetPropertyName(categorySerializedProperty, newCatName); else Debug.LogWarning(m_Style.duplicateWarningText.text); } } } while(m_GridStates.Count <= index) m_GridStates.Add(new SpriteCategoryGridState()); DrawCategorySpriteListHeader(vaRect, m_Style.newEntryText); vaRect.y += vaRect.height; vaRect.height = m_Style.gridHeight; DrawCategorySpriteList(vaRect, index, categorySerializedProperty); HandleSpriteDragAndDropToCategory(vaRect, categorySerializedProperty); } void OnAddCallback(ReorderableList list) { AddCategory(m_Style.newCategoryText, true); } SerializedProperty AddCategory(string categoryName, bool createNew) { var intendedCategoryName = categoryName; int catNameIncrement = 1; while (true) { var catOverride = IsCategoryNameInUsed(categoryName); if (catOverride != null) { if (!createNew) return catOverride; categoryName = string.Format("{0} {1}", intendedCategoryName, catNameIncrement++); } else break; } var oldSize = m_Library.arraySize; m_Library.arraySize += 1; var sp = m_Library.GetArrayElementAtIndex(oldSize); SetPropertyName(sp, categoryName); sp.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue = false; sp.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries).arraySize = 0; return sp; } bool HandleSpriteDragAndDropToCategory(Rect rect, SerializedProperty category) { bool dragAccepted = false; if (rect.Contains(Event.current.mousePosition)) { switch (Event.current.type) { case EventType.DragPerform: foreach (var obj in DragAndDrop.objectReferences) { if (obj is Sprite) { AddSpriteToCategory(category, (Sprite)obj); dragAccepted = true; } } if (dragAccepted) { DragAndDrop.AcceptDrag(); } Event.current.Use(); break; case EventType.DragUpdated: if (DragAndDrop.objectReferences.Count(x => x is Sprite) > 0) DragAndDrop.visualMode = DragAndDropVisualMode.Copy; else DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; Event.current.Use(); break; } } return dragAccepted; } static void GetRowColumnCount(float drawWidth, float size, int contentCount, out int column, out int row, out float columnf) { columnf = (drawWidth) / size; column = (int)Mathf.Floor(columnf); if (column == 0) row = 0; else row = (int)Mathf.Ceil((contentCount + column-1) / column); } void DrawCategorySpriteList(Rect rect, int index, SerializedProperty category) { var spriteListProp = category.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries); var footerRect = rect; var gridState = m_GridStates[index]; gridState.selectedIndex = (gridState.selectedIndex > spriteListProp.arraySize) ? 0 : gridState.selectedIndex; rect.height -= m_Style.gridFooterSize; if(Event.current.type == EventType.Repaint) m_Style.boxBackground.Draw(rect, "", false, false, false, false); rect.x += m_Style.gridPadding; rect.y += m_Style.gridPadding; rect.width -= m_Style.gridPadding * 2; rect.height -= m_Style.gridPadding * 2; float columnF; int columnCount, rowCount; GetRowColumnCount(rect.width, m_Style.gridSize.x, spriteListProp.arraySize, out columnCount, out rowCount, out columnF); bool canRemoveSelectedEntry = true; bool selectedEntryIsOverwrite = false; var overrideCountProperty = category.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntryCount); if (spriteListProp.arraySize <= 0 && overrideCountProperty.intValue != 0) overrideCountProperty.intValue = 0; if (columnCount > 0 && rowCount > 0) { var spriteOverwrite = 0; var scrollViewRect = new Rect(rect.x, rect.y, columnCount * m_Style.gridSize.x, rowCount * m_Style.gridSize.y); if (rowCount >= 2) gridState.scrollPos = GUI.BeginScrollView(new Rect(rect.x, rect.y, rect.width, rect.height),gridState.scrollPos, scrollViewRect, GUI.skin.horizontalScrollbar, GUI.skin.verticalScrollbar); var viewPortMinY = rect.y + gridState.scrollPos.y; var viewPortMaxY = rect.y + rect.height + gridState.scrollPos.y; var spriteObjectFieldRect = new Rect(rect.x, rect.y, m_Style.spriteGridSize, m_Style.spriteGridSize); var labelTextfieldRect = new Rect(rect.x, rect.y + m_Style.spriteGridSize + m_Style.lineSpacing, m_Style.spriteGridSize, EditorGUIUtility.singleLineHeight); var backgroundSelectedRect = new Rect(rect.x, rect.y, m_Style.gridSize.x, m_Style.gridSize.y); for (int i = 0, row = 0, column = 0; i < spriteListProp.arraySize; ++i, ++column) { if (column >= columnCount) { column = 0; ++row; } backgroundSelectedRect.x = column * m_Style.gridSize.x + rect.x; backgroundSelectedRect.y = row * m_Style.gridSize.y + rect.y; spriteObjectFieldRect.x = column * m_Style.gridSize.x + rect.x + m_Style.gridPadding; spriteObjectFieldRect.y = row * m_Style.gridSize.y + rect.y + m_Style.gridPadding; labelTextfieldRect.x = column * m_Style.gridSize.x +rect.x + m_Style.gridPadding; labelTextfieldRect.y = row * m_Style.gridSize.y + m_Style.spriteGridSize +rect.y + m_Style.gridPadding; var element = spriteListProp.GetArrayElementAtIndex(i); var spriteProperty = element.FindPropertyRelative(SpriteLibraryPropertyString.sprite); var spriteOverrideProperty = element.FindPropertyRelative(SpriteLibraryPropertyString.spriteOverride); var spriteOverride = spriteProperty.objectReferenceValue != spriteOverrideProperty.objectReferenceValue; var entryFromMain = element.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue; if (!entryFromMain || spriteOverride) ++spriteOverwrite; if(!IsLabelVisible(viewPortMinY, viewPortMaxY, backgroundSelectedRect)) continue; DrawLabel(i, spriteListProp, spriteOverrideProperty, gridState, spriteOverride, entryFromMain, backgroundSelectedRect, spriteObjectFieldRect, labelTextfieldRect, ref canRemoveSelectedEntry, ref selectedEntryIsOverwrite); } if (overrideCountProperty.intValue != spriteOverwrite) { overrideCountProperty.intValue = spriteOverwrite; GUI.changed = true; } if (rowCount >= 2) GUI.EndScrollView(); } footerRect = new Rect(footerRect.x, footerRect.y + footerRect.height - m_Style.gridFooterSize, footerRect.width, m_Style.gridFooterSize); Action removeCallback = DeleteSpriteEntry; if (selectedEntryIsOverwrite) removeCallback = DeleteSpriteEntryOverride; DrawCategorySpriteListFooter(footerRect, category, gridState,canRemoveSelectedEntry, removeCallback); } static bool IsLabelVisible(float viewPortMinY, float viewPortMaxY, Rect labelRect) { return (labelRect.y + labelRect.height) >= viewPortMinY && labelRect.y <= viewPortMaxY; } void DrawLabel(int index, SerializedProperty spriteListProp, SerializedProperty spriteOverrideProperty, SpriteCategoryGridState gridState, bool spriteOverride, bool entryFromMain, Rect backgroundSelectedRect, Rect spriteObjectFieldRect, Rect labelTextfieldRect, ref bool canRemoveSelectedEntry, ref bool selectedEntryIsOverwrite) { var element = spriteListProp.GetArrayElementAtIndex(index); if (gridState.selectedIndex == index) { canRemoveSelectedEntry = entryFromMain && spriteOverride || !entryFromMain; selectedEntryIsOverwrite = entryFromMain && spriteOverride; if(Event.current.type == EventType.Repaint) m_Style.gridList.Draw(backgroundSelectedRect, true, true, true, false); } spriteOverrideProperty.objectReferenceValue = EditorGUI.ObjectField(spriteObjectFieldRect, spriteOverrideProperty.objectReferenceValue, typeof(Sprite), false) as Sprite; if (Event.current.type == EventType.MouseUp && backgroundSelectedRect.Contains(Event.current.mousePosition)) { gridState.selectedIndex = index; } if (!entryFromMain || spriteOverride) { var overrideIconRect = spriteObjectFieldRect; overrideIconRect.x -= 12; overrideIconRect.y -= 12; overrideIconRect.width = 20; overrideIconRect.height = 20; GUI.Label(overrideIconRect, m_Style.overrideIcon); } //disable m_Name editing if the entry is from main using (new EditorGUI.DisabledScope(entryFromMain)) { EditorGUI.BeginChangeCheck(); var oldName = element.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue; if (string.IsNullOrEmpty(oldName) && spriteOverrideProperty.objectReferenceValue != null && entryFromMain) { oldName = spriteOverrideProperty.name; SetPropertyName(element, oldName); } var nameRect = labelTextfieldRect; bool nameDuplicate = IsEntryNameUsed(oldName, spriteListProp, 1); if (nameDuplicate) { nameRect.width -= 20; } var newName = EditorGUI.TextField( nameRect, GUIContent.none, oldName); if (nameDuplicate) { nameRect.x += nameRect.width; nameRect.width = 20; GUI.Label(nameRect, m_Style.duplicateWarning); } if (EditorGUI.EndChangeCheck() && !string.IsNullOrEmpty(newName)) { newName = newName.Trim(); SetPropertyName(element, newName); } } } bool IsEntryNameUsed(string name, SerializedProperty spriteList, int duplicateAllow) { if (spriteList.arraySize == 0) return false; var sp = spriteList.GetArrayElementAtIndex(0); var nameHash = SpriteLibraryUtility.GetStringHash(name); int count = 0; for (int i = 0; i < spriteList.arraySize; ++i, sp.Next(false)) { var stringValue = sp.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue; var hash = SpriteLibraryUtility.GetStringHash(stringValue); if (stringValue == name || hash == nameHash) { ++count; if(count > duplicateAllow) return true; } } return false; } static void DeleteSpriteEntryOverride(SerializedProperty spriteList, SpriteCategoryGridState gridState) { var sp = spriteList.GetArrayElementAtIndex(gridState.selectedIndex); sp.FindPropertyRelative(SpriteLibraryPropertyString.spriteOverride).objectReferenceValue = sp.FindPropertyRelative(SpriteLibraryPropertyString.sprite).objectReferenceValue; } static void DeleteSpriteEntry(SerializedProperty spriteList, SpriteCategoryGridState gridState) { spriteList.DeleteArrayElementAtIndex(gridState.selectedIndex); var count = spriteList.arraySize; gridState.selectedIndex = (gridState.selectedIndex >= count) ? count - 1 : gridState.selectedIndex; } void DrawCategorySpriteListHeader(Rect rect, string text) { if (Event.current.type == UnityEngine.EventType.Repaint) m_Style.headerBackground.Draw(rect, false, false, false, false); rect.x += 10; EditorGUI.LabelField(rect, text); } void DrawCategorySpriteListFooter(Rect rect, SerializedProperty category, SpriteCategoryGridState gridState, bool canRemove, Action onRemove) { float num = rect.xMax - 10f; float x = num - 8f; x -= 25f; x -= 25f; rect = new Rect(x, rect.y, num - x, rect.height); Rect rect1 = new Rect(x + 4f, rect.y, 25f, 16f); Rect position = new Rect(num - 29f, rect.y, 25f, 16f); if (Event.current.type == UnityEngine.EventType.Repaint) m_Style.footerBackground.Draw(rect, false, false, false, false); if (GUI.Button(rect1, m_Style.iconToolbarPlus, m_Style.preButton)) AddSpriteToCategory(category, null); var spriteList = category.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries); using (new EditorGUI.DisabledScope(!canRemove || gridState.selectedIndex < 0 || spriteList.arraySize <= gridState.selectedIndex)) { if (GUI.Button(position, m_Style.iconToolbarMinus, m_Style.preButton)) onRemove(spriteList, gridState); } } string GetUniqueEntryName(SerializedProperty list, string name) { var newName = name; var count = 0; while (IsEntryNameUsed(newName, list, 0)) { newName = string.Format("{0}_{1}", name, count); ++count; } return newName; } void AddSpriteToCategory(SerializedProperty category, Sprite sprite) { var name = sprite == null ? m_Style.newEntryText : sprite.name; var spriteList = category.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries); name = GetUniqueEntryName(spriteList, name); spriteList.arraySize += 1; var sp = spriteList.GetArrayElementAtIndex(spriteList.arraySize - 1); SetPropertyName(sp, name); sp.FindPropertyRelative(SpriteLibraryPropertyString.sprite).objectReferenceValue = sprite; sp.FindPropertyRelative(SpriteLibraryPropertyString.spriteOverride).objectReferenceValue = sprite; sp.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue = false; } static void SetPropertyName(SerializedProperty sp, string newName) { sp.FindPropertyRelative(SpriteLibraryPropertyString.name).stringValue = newName; sp.FindPropertyRelative(SpriteLibraryPropertyString.hash).intValue = SpriteLibraryUtility.GetStringHash(newName); } bool OnCanRemoveCallback(ReorderableList list) { bool canDelete = true; if (list.index >= 0 && list.index < list.count) { var item = list.serializedProperty.GetArrayElementAtIndex(list.index); canDelete = !item.FindPropertyRelative(SpriteLibraryPropertyString.fromMain).boolValue; } return canDelete; } public static int GetSpriteLibraryEntryCount(SerializedProperty library, int min = 100) { if (library == null || library.arraySize == 0) return 0; var entryCount = 0; var categoryCount = library.arraySize; for (var i = 0; i < categoryCount; i++) { var category = library.GetArrayElementAtIndex(i); if (category != null) { var entries = category.FindPropertyRelative(SpriteLibraryPropertyString.overrideEntries); if (entries != null) entryCount += entries.arraySize; } } return Math.Max(entryCount, min); } } }