using System; using System.Collections.Generic; using System.IO; using Module.NavigationTool.Editor.Utilities; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace Module.NavigationTool.Editor.Favorite { [Serializable] internal sealed class Favorites : ISerializationCallbackReceiver { public static readonly string PREF_ID = "PREF_FAVORITE_LIST"; public List entries; public Favorites() { entries = new List(); string json = EditorProjectPrefs.GetString(PREF_ID); if (!string.IsNullOrEmpty(json)) EditorJsonUtility.FromJsonOverwrite(json, this); } void ISerializationCallbackReceiver.OnBeforeSerialize() { } void ISerializationCallbackReceiver.OnAfterDeserialize() { var guidToEntry = new Dictionary(entries.Count); for (var i = 0; i < entries.Count; i++) { Entry e = entries[i]; guidToEntry.Add(e.guid, e); } for (var i = 0; i < entries.Count; i++) { Entry e = entries[i]; if (e.isAsset) continue; for (int j = e.children.Count - 1; j >= 0; j--) { Entry c = guidToEntry[e.children[j].guid]; if (c != null) { e.children[j].entry = c; c.parent = e; } else { e.children.RemoveAt(j); } } } for (var i = 0; i < entries.Count; i++) { Entry e = entries[i]; if (e.indentLevel != 0 && e.parent == null) e.indentLevel = 0; } } public void Add(string guid, Entry toParent = null) { if (InternalAddEntry(Entry.CreateAsset(guid), toParent)) Save(); } public void AddFolder(string name, Entry toParent = null) { if (InternalAddEntry(Entry.CreateFolder(name), toParent)) Save(); } private bool InternalAddEntry(Entry e, Entry toParent) { e.Refresh(); if (!e.valid) return false; entries.Add(e); if (toParent != null && toParent.isAsset) toParent = toParent.parent; if (toParent != null) { e.parent = toParent; e.indentLevel = toParent.indentLevel + 1; toParent.AddChild(e); } return true; } public void AddRange(string[] guids, Entry toParent = null) { var isDirty = false; for (var i = 0; i < guids.Length; i++) { if (InternalAddEntry(Entry.CreateAsset(guids[i]), toParent)) isDirty = true; } if (isDirty) Save(); } public void AddRangeByPath(string[] paths, Entry toParent = null) { var isDirty = false; for (var i = 0; i < paths.Length; i++) { string guid = AssetDatabase.AssetPathToGUID(paths[i]); if (InternalAddEntry(Entry.CreateAsset(guid), toParent)) isDirty = true; } if (isDirty) Save(); } public void Remove(Entry e) { if (InternalRemove(e)) Save(); } private bool InternalRemove(Entry e) { if (!entries.Remove(e)) return false; e.parent?.RemoveChild(e); if (!e.isAsset) { for (int i = e.children.Count - 1; i >= 0; i--) { InternalRemove(e.children[i].entry); } } return true; } public void Move(Entry e, Entry toParent) { InternalMove(e, toParent); Save(); } private void InternalMove(Entry e, Entry toParent) { if (e.parent != null) { e.parent.RemoveChild(e); e.parent = null; e.indentLevel = 0; } if (toParent != null && toParent.isAsset) toParent = toParent.parent; if (toParent != null) { toParent.AddChild(e); e.parent = toParent; e.indentLevel = e.parent.indentLevel + 1; } InternalMoveIndentLevel(e); } private void InternalMoveIndentLevel(Entry parent) { if (parent.isAsset) return; for (var i = 0; i < parent.children.Count; i++) { parent.children[i].entry.indentLevel = parent.indentLevel + 1; InternalMoveIndentLevel(parent.children[i].entry); } } public Object GetObject(Entry entry) { string path = AssetDatabase.GUIDToAssetPath(entry.assetGuid); return AssetDatabase.LoadMainAssetAtPath(path); } private void Sort() { entries.Sort((e0, e1) => { int compare = e0.isAsset.CompareTo(e1.isAsset); if (compare == 0) compare = string.Compare(e0.lowerName, e1.lowerName, StringComparison.Ordinal); return compare; }); } public void Save() { Sort(); string json = EditorJsonUtility.ToJson(this, false); EditorProjectPrefs.SetString(PREF_ID, json); } /// /// Class: Entry /// [Serializable] public sealed class Entry : ISerializationCallbackReceiver { public string guid; public string assetGuid; public string name; public bool isAsset; public bool expanded; public int indentLevel; public List children; [NonSerialized] public bool valid; [NonSerialized] public Entry parent; [NonSerialized] public GUIContent content; [NonSerialized] public string lowerName; private Entry(string assetGuid, string name, bool isAsset) { guid = Guid.NewGuid().ToString(); this.assetGuid = assetGuid; this.name = name; this.isAsset = isAsset; if (!isAsset) { expanded = true; children = new List(); } } void ISerializationCallbackReceiver.OnBeforeSerialize() { } void ISerializationCallbackReceiver.OnAfterDeserialize() { if (isAsset) children = null; Refresh(); } public void Refresh() { if (isAsset) { string path = AssetDatabase.GUIDToAssetPath(assetGuid); name = Path.GetFileNameWithoutExtension(path); lowerName = name.ToLower(); valid = AssetDatabase.LoadMainAssetAtPath(path) != null; content = new GUIContent(name, FavoritesGUIUtility.GetIcon(path), path); } else { valid = true; content = new GUIContent(name, FavoritesGUIUtility.GetFolderIcon()); } } public void AddChild(Entry entry) { children.Add(new Child(entry)); children.Sort((c0, c1) => { int compare = c0.entry.isAsset.CompareTo(c1.entry.isAsset); if (compare == 0) compare = string.Compare(c0.entry.lowerName, c1.entry.lowerName, StringComparison.Ordinal); return compare; }); } public void RemoveChild(Entry entry) { for (int i = children.Count - 1; i >= 0; i--) { if (children[i].entry == entry) children.RemoveAt(i); } } public static Entry CreateFolder(string name) { return new Entry(string.Empty, name, false); } public static Entry CreateAsset(string assetGuid) { return new Entry(assetGuid, string.Empty, true); } /// /// Class: Entry child /// [Serializable] public sealed class Child { public string guid; [NonSerialized] public Entry entry; public Child(Entry entry) { guid = entry.guid; this.entry = entry; } } } } }