using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.U2D.Animation;
using Object = UnityEngine.Object;
namespace UnityEditor.U2D.Animation
{
///
/// Represents a Sprite Library's label.
///
[Serializable]
public class SpriteLibraryLabel : ISpriteLibraryLabel
{
///
/// Label's name.
///
public string name => m_Name;
///
/// Sprite associated with the label.
///
public Sprite sprite => m_Sprite;
[SerializeField]
string m_Name;
[SerializeField]
Sprite m_Sprite;
///
/// Constructs a new label.
///
/// Label's name.
/// Label's Sprite.
public SpriteLibraryLabel(string labelName, Sprite labelSprite)
{
m_Name = labelName;
m_Sprite = labelSprite;
}
}
///
/// Represents a Sprite Library's category.
///
[Serializable]
public class SpriteLibraryCategory : ISpriteLibraryCategory
{
///
/// Category's name.
///
public string name => m_Name;
///
/// List of labels in category.
///
public IEnumerable labels => m_Labels;
[SerializeField]
List m_Labels;
[SerializeField]
string m_Name;
///
/// Constructs a new category.
///
/// Category's name.
/// Collection of labels in a category.
public SpriteLibraryCategory(string categoryName, IEnumerable categoryLabels)
{
m_Name = categoryName;
m_Labels = new List(categoryLabels);
}
}
///
/// Class used for creating new Sprite Library Source Assets.
///
public static class SpriteLibrarySourceAssetFactory
{
///
/// Sprite Library Source Asset's extension.
///
public const string extension = SpriteLibrarySourceAsset.extension;
///
/// Creates a new Sprite Library Source Asset at a given path.
///
/// Save path. Must be within the Assets folder.
/// Collection of categories in the library.
/// A path to the main library. Null if there is no main library.
/// A relative path to the Project with correct extension.
/// Throws when the save path is invalid/
public static string Create(string path, IEnumerable categories, string mainLibraryPath = null)
{
if (string.IsNullOrEmpty(path))
throw new InvalidOperationException("Save path cannot be null or empty.");
var relativePath = GetRelativePath(path);
if (string.IsNullOrEmpty(relativePath))
throw new InvalidOperationException($"{nameof(LoadSpriteLibrarySourceAsset)} can only be saved in the Assets folder.");
relativePath = Path.ChangeExtension(relativePath, extension);
SpriteLibraryAsset mainLibrary = null;
if (!string.IsNullOrEmpty(mainLibraryPath))
{
mainLibrary = AssetDatabase.LoadAssetAtPath(mainLibraryPath);
if (mainLibrary == null)
throw new InvalidOperationException($"No {nameof(SpriteLibraryAsset)} found at path: '{mainLibraryPath}'");
}
var asset = ScriptableObject.CreateInstance();
var categoryList = new List();
if (categories != null)
{
foreach (var category in categories)
{
var spriteLibCategory = new SpriteLibCategoryOverride
{
name = category.name,
overrideEntries = new List()
};
foreach (var label in category.labels)
{
var spriteCategoryEntryOverride = new SpriteCategoryEntryOverride
{
name = label.name,
spriteOverride = label.sprite
};
spriteLibCategory.overrideEntries.Add(spriteCategoryEntryOverride);
}
categoryList.Add(spriteLibCategory);
}
}
if (mainLibrary != null)
{
asset.SetPrimaryLibraryGUID(AssetDatabase.GUIDFromAssetPath(mainLibraryPath).ToString());
var newCategories = mainLibrary.categories ?? new List();
var existingCategories = new List(categoryList);
categoryList.Clear();
// populate new primary
foreach (var newCategory in newCategories)
{
var labels = new List();
SpriteLibCategoryOverride existingCategory = null;
for (var i = 0; i < existingCategories.Count; i++)
{
var category = existingCategories[i];
if (category.name == newCategory.name)
{
existingCategory = category;
existingCategory.fromMain = true;
existingCategories.RemoveAt(i);
break;
}
}
var newEntries = newCategory.categoryList;
foreach (var newEntry in newEntries)
{
var sprite = newEntry.sprite;
labels.Add(new SpriteCategoryEntryOverride
{
name = newEntry.name,
sprite = sprite,
spriteOverride = sprite,
fromMain = true
});
}
var overrideCount = 0;
if (existingCategory != null)
{
foreach (var existingLabel in existingCategory.overrideEntries)
{
var foundLabel = false;
foreach (var newLabel in labels)
{
if (existingLabel.name == newLabel.name)
{
if (newLabel.spriteOverride != existingLabel.spriteOverride)
{
newLabel.spriteOverride = existingLabel.spriteOverride;
overrideCount++;
}
foundLabel = true;
break;
}
}
if (!foundLabel)
{
overrideCount++;
labels.Add(new SpriteCategoryEntryOverride
{
name = existingLabel.name,
sprite = existingLabel.sprite,
spriteOverride = existingLabel.spriteOverride,
fromMain = false
});
}
}
}
categoryList.Add(new SpriteLibCategoryOverride
{
name = newCategory.name,
overrideEntries = labels,
fromMain = true,
entryOverrideCount = overrideCount
});
}
foreach (var existingCategory in existingCategories)
{
var keepCategory = false;
if (existingCategory.fromMain)
{
for (var i = existingCategory.overrideEntries.Count; i-- > 0;)
{
var entry = existingCategory.overrideEntries[i];
if (!entry.fromMain || entry.sprite != entry.spriteOverride)
{
entry.fromMain = false;
entry.sprite = entry.spriteOverride;
keepCategory = true;
}
else
existingCategory.overrideEntries.RemoveAt(i);
}
}
if (!existingCategory.fromMain || keepCategory)
{
existingCategory.fromMain = false;
existingCategory.entryOverrideCount = 0;
categoryList.Add(existingCategory);
}
}
}
asset.SetLibrary(categoryList);
SpriteLibrarySourceAssetImporter.SaveSpriteLibrarySourceAsset(asset, relativePath);
Object.DestroyImmediate(asset);
return relativePath;
}
///
/// Creates a new Sprite Library Source Asset at a given path.
///
/// Save path. Must be within the Assets folder.
/// Sprite Library Asset to be saved.
/// A path to the main library. Null if there is no main library.
/// A relative path to the Project with correct extension.
/// Throws when the save path is invalid/
public static string Create(string path, SpriteLibraryAsset spriteLibraryAsset, string mainLibraryPath = null)
{
return Create(path, spriteLibraryAsset.categories, mainLibraryPath);
}
///
/// Creates a new Sprite Library Source Asset at a given path.
///
/// Sprite Library Asset to be saved.
/// Save path. Must be within the Assets folder.
/// A path to the main library. Null if there is no main library.
/// A relative path to the Project with correct extension.
/// Throws when the save path is invalid/
public static string SaveAsSourceAsset(this SpriteLibraryAsset spriteLibraryAsset, string path, string mainLibraryPath = null)
{
return Create(path, spriteLibraryAsset, mainLibraryPath);
}
internal static SpriteLibrarySourceAsset LoadSpriteLibrarySourceAsset(string path)
{
var loadedObjects = UnityEditorInternal.InternalEditorUtility.LoadSerializedFileAndForget(path);
foreach (var obj in loadedObjects)
{
if (obj is SpriteLibrarySourceAsset asset)
return asset;
}
return null;
}
static string GetRelativePath(string path)
{
if (string.IsNullOrWhiteSpace(path))
return null;
if (!path.StartsWith("Assets/") && !path.StartsWith(Application.dataPath))
return null;
var pathStartIndex = path.IndexOf("Assets");
return pathStartIndex == -1 ? null : path.Substring(pathStartIndex);
}
}
}