diff --git a/Editor.meta b/Editor.meta new file mode 100644 index 0000000..8f721d0 --- /dev/null +++ b/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3404f03e41295794998e14f145cc0000 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Favorites.meta b/Editor/Favorites.meta new file mode 100644 index 0000000..0353026 --- /dev/null +++ b/Editor/Favorites.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 22b00eaff1a81a44ea0fcf0c016bd2e2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Favorites/Enums.meta b/Editor/Favorites/Enums.meta new file mode 100644 index 0000000..6b3c5fa --- /dev/null +++ b/Editor/Favorites/Enums.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1b77a50e79274018aac89e7a4f0c185d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Favorites/Enums/EManipulatingState.cs b/Editor/Favorites/Enums/EManipulatingState.cs new file mode 100644 index 0000000..3d5476a --- /dev/null +++ b/Editor/Favorites/Enums/EManipulatingState.cs @@ -0,0 +1,16 @@ +namespace Game.NavigationTool.Editor +{ + public enum EManipulatingState : byte + { + None, + BeginClick, + PerformedClick, + EndClick, + BeginContextClick, + PerformedContextClick, + EndContextClick, + BeginDrag, + PerformedDrag, + EndDrag + } +} \ No newline at end of file diff --git a/Editor/Favorites/Enums/EManipulatingState.cs.meta b/Editor/Favorites/Enums/EManipulatingState.cs.meta new file mode 100644 index 0000000..c8fbb1d --- /dev/null +++ b/Editor/Favorites/Enums/EManipulatingState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a166f7a5dc6042dcae04bf352b8144ce +timeCreated: 1613213516 \ No newline at end of file diff --git a/Editor/Favorites/Utilities.meta b/Editor/Favorites/Utilities.meta new file mode 100644 index 0000000..514b9fb --- /dev/null +++ b/Editor/Favorites/Utilities.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: feac89e79687422296585bfe2a8a4ebc +timeCreated: 1613162380 \ No newline at end of file diff --git a/Editor/Favorites/Utilities/Favorites.cs b/Editor/Favorites/Utilities/Favorites.cs new file mode 100644 index 0000000..be1d6e0 --- /dev/null +++ b/Editor/Favorites/Utilities/Favorites.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Game.NavigationTool.Editor +{ + [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 = EditorPrefs.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); + } + } + } + } + + 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) + { + 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 (var i = 0; i < e.children.Count; 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); + } + + public void Save() + { + string json = EditorJsonUtility.ToJson(this, false); + EditorPrefs.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 = !string.IsNullOrEmpty(path); + 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)); + } + + 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; + } + } + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Utilities/Favorites.cs.meta b/Editor/Favorites/Utilities/Favorites.cs.meta new file mode 100644 index 0000000..7e23bd1 --- /dev/null +++ b/Editor/Favorites/Utilities/Favorites.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7ec45414bdf74e81800f8ffaf95f26ec +timeCreated: 1613162445 \ No newline at end of file diff --git a/Editor/Favorites/Utilities/FavoritesGUIUtility.cs b/Editor/Favorites/Utilities/FavoritesGUIUtility.cs new file mode 100644 index 0000000..f877904 --- /dev/null +++ b/Editor/Favorites/Utilities/FavoritesGUIUtility.cs @@ -0,0 +1,112 @@ +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + internal static class FavoritesGUIUtility + { + private static readonly int ENTRY_HASH = "DrawEntry".GetHashCode(); + + public static Favorites.Entry manipulatingEntry; + public static EManipulatingState manipulatingState; + public static Favorites.Entry hoverEntry; + + public static void DrawEntry(Rect rect, Favorites.Entry entry, Styles styles, bool ignoreIndentLevel = false) + { + int id = GUIUtility.GetControlID(ENTRY_HASH, FocusType.Passive, rect); + bool on = manipulatingEntry == entry; + bool intersects = rect.Contains(Event.current.mousePosition); + + if (intersects) + hoverEntry = entry; + else if (hoverEntry == entry) + hoverEntry = null; + + switch (Event.current.type) + { + case EventType.MouseDown: + if (intersects) + { + SetManipulating(entry, Event.current.button == 0 ? EManipulatingState.BeginClick : EManipulatingState.BeginContextClick); + Event.current.Use(); + } + break; + case EventType.MouseUp: + if (manipulatingEntry == entry) + { + if (manipulatingState == EManipulatingState.BeginClick) + { + if (intersects && Event.current.button == 0) + SetManipulating(entry, EManipulatingState.PerformedClick); + else + SetManipulating(entry, EManipulatingState.EndClick); + } + else if (manipulatingState == EManipulatingState.BeginContextClick) + { + if (intersects && Event.current.button == 1) + SetManipulating(entry, EManipulatingState.PerformedContextClick); + else + SetManipulating(entry, EManipulatingState.EndContextClick); + } + + Event.current.Use(); + } + break; + case EventType.MouseDrag: + if (manipulatingEntry == entry) + Event.current.Use(); + break; + case EventType.KeyDown: + break; + case EventType.KeyUp: + break; + case EventType.Repaint: + if (!ignoreIndentLevel) + { + rect.x += entry.indentLevel * 15.0f; + rect.width -= entry.indentLevel * 15.0f; + } + + if (!entry.isAsset) + entry.content.image = entry.expanded ? styles.foldoutOut : styles.foldoutIn; + + styles.entry.Draw(rect, entry.content, id, on, intersects); + break; + case EventType.DragUpdated: + break; + case EventType.DragPerform: + break; + case EventType.DragExited: + break; + } + } + + public static Texture2D GetIcon(string path) + { + var texture = AssetDatabase.GetCachedIcon(path) as Texture2D; + + if (texture == null) + texture = InternalEditorUtility.GetIconForFile(path); + + return texture; + } + + public static Texture2D GetFolderIcon() + { + return AssetDatabase.GetCachedIcon("Assets") as Texture2D; + } + + private static void SetManipulating(Favorites.Entry entry, EManipulatingState state) + { + manipulatingEntry = entry; + manipulatingState = state; + } + + public static void ClearManipulating() + { + SetManipulating(null, EManipulatingState.None); + hoverEntry = null; + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Utilities/FavoritesGUIUtility.cs.meta b/Editor/Favorites/Utilities/FavoritesGUIUtility.cs.meta new file mode 100644 index 0000000..e45c52e --- /dev/null +++ b/Editor/Favorites/Utilities/FavoritesGUIUtility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a964d9decf5f4d619caccf91a2448661 +timeCreated: 1613211223 \ No newline at end of file diff --git a/Editor/Favorites/Utilities/FavoritesUtility.cs b/Editor/Favorites/Utilities/FavoritesUtility.cs new file mode 100644 index 0000000..b807d6f --- /dev/null +++ b/Editor/Favorites/Utilities/FavoritesUtility.cs @@ -0,0 +1,26 @@ +using UnityEditor; + +namespace Game.NavigationTool.Editor +{ + internal static class FavoritesUtility + { + private static Favorites FAVORITES; + + static FavoritesUtility() + { + FAVORITES = null; + } + + public static Favorites GetFavorites() + { + return FAVORITES ?? (FAVORITES = new Favorites()); + } + + [MenuItem("Tools/Utilities/Favorites/Delete")] + public static void DeleteAll() + { + FAVORITES = null; + EditorPrefs.DeleteKey(Favorites.PREF_ID); + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Utilities/FavoritesUtility.cs.meta b/Editor/Favorites/Utilities/FavoritesUtility.cs.meta new file mode 100644 index 0000000..7ea1769 --- /dev/null +++ b/Editor/Favorites/Utilities/FavoritesUtility.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 892d63101f6a41ecb789405dc0d7576a +timeCreated: 1613210854 \ No newline at end of file diff --git a/Editor/Favorites/Window.meta b/Editor/Favorites/Window.meta new file mode 100644 index 0000000..833a8d3 --- /dev/null +++ b/Editor/Favorites/Window.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f365d80e29254040a54189ce0a22f29f +timeCreated: 1613211006 \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesPopupWindowAddFolder.cs b/Editor/Favorites/Window/EditorFavoritesPopupWindowAddFolder.cs new file mode 100644 index 0000000..3bbd21d --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesPopupWindowAddFolder.cs @@ -0,0 +1,47 @@ +using UnityEditor; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + internal sealed class EditorFavoritesPopupWindowAddFolder : PopupWindowContent + { + private readonly Favorites.Entry toParent; + private string inputStr = string.Empty; + + public static void Show(Vector2 mousePosition, Favorites.Entry toParent) + { + var rect = new Rect(mousePosition.x - 200.0f, mousePosition.y, 0.0f, 0.0f); + PopupWindow.Show(rect, new EditorFavoritesPopupWindowAddFolder(toParent)); + } + + private EditorFavoritesPopupWindowAddFolder(Favorites.Entry toParent) + { + this.toParent = toParent; + } + + public override void OnGUI(Rect rect) + { + bool isReturn = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return; + bool isEscape = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape; + + GUI.SetNextControlName("TextField"); + inputStr = GUI.TextField(rect, inputStr); + GUI.FocusControl("TextField"); + + if (isReturn && !string.IsNullOrEmpty(inputStr)) + { + Favorites favorites = FavoritesUtility.GetFavorites(); + favorites.AddFolder(inputStr, toParent); + editorWindow.Close(); + } + + if (isEscape) + editorWindow.Close(); + } + + public override Vector2 GetWindowSize() + { + return new Vector2(200.0f, EditorGUIUtility.singleLineHeight); + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesPopupWindowAddFolder.cs.meta b/Editor/Favorites/Window/EditorFavoritesPopupWindowAddFolder.cs.meta new file mode 100644 index 0000000..8eb6613 --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesPopupWindowAddFolder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 645cd8d4c1164fc491789d22763f6167 +timeCreated: 1613252500 \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesPopupWindowContextMenu.cs b/Editor/Favorites/Window/EditorFavoritesPopupWindowContextMenu.cs new file mode 100644 index 0000000..9e87276 --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesPopupWindowContextMenu.cs @@ -0,0 +1,93 @@ +using UnityEditor; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + internal sealed class EditorFavoritesPopupWindowContextMenu : PopupWindowContent + { + private readonly Favorites.Entry entry; + private readonly Option[] options; + private readonly Styles styles; + + public static void Show(Vector2 mousePosition, Favorites.Entry entry) + { + var rect = new Rect(mousePosition.x, mousePosition.y, 0.0f, 0.0f); + PopupWindow.Show(rect, new EditorFavoritesPopupWindowContextMenu(entry)); + } + + private EditorFavoritesPopupWindowContextMenu(Favorites.Entry entry) + { + this.entry = entry; + styles = new Styles(); + + options = new[] + { + new Option("Add Folder", OnAddFolder, () => entry == null || !entry.isAsset), + new Option("Rename Folder", OnRenameFolder, () => entry != null && !entry.isAsset), + new Option("Remove", OnRemove, () => entry != null) + }; + } + + public override void OnGUI(Rect rect) + { + styles.Initialize(GUI.skin); + var entryRect = new Rect(0.0f, 0.0f, rect.width, EditorGUIUtility.singleLineHeight); + + for (var i = 0; i < options.Length; i++) + { + GUI.enabled = options[i].criteria.Invoke(); + + if (GUI.Button(entryRect, options[i].name, styles.contextOption)) + options[i].action.Invoke(); + + entryRect.y += EditorGUIUtility.singleLineHeight; + } + + GUI.enabled = true; + } + + public override Vector2 GetWindowSize() + { + return new Vector2(200.0f, options.Length * EditorGUIUtility.singleLineHeight + 6.0f); + } + + private void OnAddFolder() + { + editorWindow.Close(); + EditorFavoritesPopupWindowAddFolder.Show(new Vector2(0.0f, 0.0f), entry); + } + + private void OnRenameFolder() + { + editorWindow.Close(); + EditorFavoritesPopupWindowRenameFolder.Show(new Vector2(0.0f, 0.0f), entry); + } + + private void OnRemove() + { + Favorites favorites = FavoritesUtility.GetFavorites(); + favorites.Remove(entry); + editorWindow.Close(); + } + + /// + /// Class: Option in drop down menu + /// + private sealed class Option + { + public delegate void DoAction(); + public delegate bool DoActionCriteria(); + + public readonly string name; + public readonly DoAction action; + public readonly DoActionCriteria criteria; + + public Option(string name, DoAction action, DoActionCriteria criteria = null) + { + this.name = name; + this.action = action; + this.criteria = criteria; + } + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesPopupWindowContextMenu.cs.meta b/Editor/Favorites/Window/EditorFavoritesPopupWindowContextMenu.cs.meta new file mode 100644 index 0000000..52dfb8a --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesPopupWindowContextMenu.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 476b6add28b14bde9fe51db4ba9497ff +timeCreated: 1613218094 \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesPopupWindowRenameFolder.cs b/Editor/Favorites/Window/EditorFavoritesPopupWindowRenameFolder.cs new file mode 100644 index 0000000..69c294b --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesPopupWindowRenameFolder.cs @@ -0,0 +1,53 @@ +using UnityEditor; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + internal sealed class EditorFavoritesPopupWindowRenameFolder : PopupWindowContent + { + private readonly Favorites.Entry entry; + private string inputStr; + + public static void Show(Vector2 mousePosition, Favorites.Entry entry) + { + var rect = new Rect(mousePosition.x - 200.0f, mousePosition.y, 0.0f, 0.0f); + PopupWindow.Show(rect, new EditorFavoritesPopupWindowRenameFolder(entry)); + } + + private EditorFavoritesPopupWindowRenameFolder(Favorites.Entry entry) + { + this.entry = entry; + inputStr = entry.name; + } + + public override void OnGUI(Rect rect) + { + bool isReturn = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return; + bool isEscape = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape; + + GUI.SetNextControlName("TextField"); + inputStr = GUI.TextField(rect, inputStr); + GUI.FocusControl("TextField"); + + if (isReturn && !string.IsNullOrEmpty(inputStr)) + { + entry.name = inputStr; + entry.lowerName = inputStr.ToLower(); + entry.content.text = inputStr; + + Favorites favorites = FavoritesUtility.GetFavorites(); + favorites.Save(); + + editorWindow.Close(); + } + + if (isEscape) + editorWindow.Close(); + } + + public override Vector2 GetWindowSize() + { + return new Vector2(200.0f, EditorGUIUtility.singleLineHeight); + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesPopupWindowRenameFolder.cs.meta b/Editor/Favorites/Window/EditorFavoritesPopupWindowRenameFolder.cs.meta new file mode 100644 index 0000000..1181998 --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesPopupWindowRenameFolder.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0c7a514e1c6b4846857dcc980b42485d +timeCreated: 1613291517 \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesWindow.cs b/Editor/Favorites/Window/EditorFavoritesWindow.cs new file mode 100644 index 0000000..4d26e34 --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesWindow.cs @@ -0,0 +1,128 @@ +using System; +using UnityEditor; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + internal sealed class EditorFavoritesWindow : EditorWindow + { + [NonSerialized] + private Styles styles; + [SerializeField] + public EditorFavoritesViewTools viewTools; + [SerializeField] + private EditorFavoritesViewTreeList viewTreeList; + [SerializeField] + private EditorFavoritesViewSearchList viewSearchList; + + [MenuItem("Tools/Windows/Favorites")] + public static void Open() + { + var window = GetWindow(); + window.titleContent = new GUIContent("Favorites"); + window.Show(); + } + + private void OnEnable() + { + if (styles == null) + styles = new Styles(); + if (viewTools == null) + viewTools = new EditorFavoritesViewTools(); + if (viewTreeList == null) + viewTreeList = new EditorFavoritesViewTreeList(); + if (viewSearchList == null) + viewSearchList = new EditorFavoritesViewSearchList(); + } + + private void OnGUI() + { + if (Event.current.type == EventType.Layout) + return; + + styles.Initialize(GUI.skin); + var rectTools = new Rect(0.0f, 0.0f, position.width, EditorGUIUtility.singleLineHeight); + var rectList = new Rect(0.0f, rectTools.yMax, position.width, position.height - rectTools.height); + + viewTools.Initialize(); + viewTools.Draw(this, rectTools, styles); + + if (viewTools.IsSearching()) + { + viewSearchList.Initialize(); + viewSearchList.Draw(this, rectList, styles); + } + else + { + viewTreeList.Initialize(); + viewTreeList.Draw(this, rectList, styles); + } + + HandleDragAndDropEvents(); + HandleManipulatedEntry(); + } + + private void HandleDragAndDropEvents() + { + Favorites favorites = FavoritesUtility.GetFavorites(); + + if (Event.current.type == EventType.DragUpdated) + { + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + } + else if (Event.current.type == EventType.DragPerform) + { + Favorites.Entry toParent = null; + + if (!viewTools.IsSearching()) + toParent = FavoritesGUIUtility.hoverEntry; + + favorites.AddRangeByPath(DragAndDrop.paths, toParent); + } + } + + private static void HandleManipulatedEntry() + { + Favorites favorites = FavoritesUtility.GetFavorites(); + Favorites.Entry entry = FavoritesGUIUtility.manipulatingEntry; + + switch (FavoritesGUIUtility.manipulatingState) + { + case EManipulatingState.BeginClick: + break; + case EManipulatingState.PerformedClick: + if (entry.isAsset) + { + Selection.activeObject = favorites.GetObject(entry); + EditorGUIUtility.PingObject(Selection.activeObject); + } + else + { + entry.expanded = !entry.expanded; + favorites.Save(); + } + + FavoritesGUIUtility.ClearManipulating(); + break; + case EManipulatingState.EndClick: + FavoritesGUIUtility.ClearManipulating(); + break; + case EManipulatingState.BeginDrag: + break; + case EManipulatingState.PerformedDrag: + FavoritesGUIUtility.ClearManipulating(); + break; + case EManipulatingState.EndDrag: + FavoritesGUIUtility.ClearManipulating(); + break; + case EManipulatingState.PerformedContextClick: + FavoritesGUIUtility.ClearManipulating(); + EditorFavoritesPopupWindowContextMenu.Show(Event.current.mousePosition, entry); + break; + case EManipulatingState.EndContextClick: + FavoritesGUIUtility.ClearManipulating(); + break; + } + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/EditorFavoritesWindow.cs.meta b/Editor/Favorites/Window/EditorFavoritesWindow.cs.meta new file mode 100644 index 0000000..e741231 --- /dev/null +++ b/Editor/Favorites/Window/EditorFavoritesWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba47d77c4aeba1543bc1f55d25236caa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Favorites/Window/Styles.meta b/Editor/Favorites/Window/Styles.meta new file mode 100644 index 0000000..eca2f5b --- /dev/null +++ b/Editor/Favorites/Window/Styles.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 15b0d6cd0e064f94bf81f3738a36ff85 +timeCreated: 1613211891 \ No newline at end of file diff --git a/Editor/Favorites/Window/Styles/Styles.cs b/Editor/Favorites/Window/Styles/Styles.cs new file mode 100644 index 0000000..7eea0e2 --- /dev/null +++ b/Editor/Favorites/Window/Styles/Styles.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + internal sealed class Styles + { + public GUIStyle toolbox; + public GUIStyle buttonAddFolder; + + public GUIStyle entry; + public Texture2D foldoutIn; + public Texture2D foldoutOut; + + public GUIStyle contextOption; + + private GUISkin skin; + + public void Initialize(GUISkin skin) + { + if (this.skin == skin) + return; + + toolbox = new GUIStyle(skin.box); + + buttonAddFolder = new GUIStyle(skin.button); + buttonAddFolder.padding = new RectOffset(4, 0, 0, 4); + + entry = new GUIStyle(skin.label); + entry.hover.textColor = entry.onHover.textColor = Color.white; + entry.active.textColor = entry.onActive.textColor = Color.yellow; + + GUIStyle style = skin.FindStyle("Foldout"); + + if (style != null) + { + foldoutIn = style.normal.scaledBackgrounds[0]; + foldoutOut = style.onNormal.scaledBackgrounds[0]; + } + + contextOption = new GUIStyle(skin.label); + contextOption.hover.textColor = contextOption.onHover.textColor = Color.white; + contextOption.hover.scaledBackgrounds = contextOption.onHover.scaledBackgrounds = skin.box.normal.scaledBackgrounds; + contextOption.active.textColor = contextOption.onActive.textColor = Color.yellow; + contextOption.active.scaledBackgrounds = contextOption.onActive.scaledBackgrounds = skin.box.normal.scaledBackgrounds; + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/Styles/Styles.cs.meta b/Editor/Favorites/Window/Styles/Styles.cs.meta new file mode 100644 index 0000000..44dfce7 --- /dev/null +++ b/Editor/Favorites/Window/Styles/Styles.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6d7dcc40bb3d471d91439071d6157a8f +timeCreated: 1613211898 \ No newline at end of file diff --git a/Editor/Favorites/Window/Views.meta b/Editor/Favorites/Window/Views.meta new file mode 100644 index 0000000..27c716b --- /dev/null +++ b/Editor/Favorites/Window/Views.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8d38858044174890af932bea10cfbb9d +timeCreated: 1613211112 \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/AbstractEditorFavoritesView.cs b/Editor/Favorites/Window/Views/AbstractEditorFavoritesView.cs new file mode 100644 index 0000000..24094b8 --- /dev/null +++ b/Editor/Favorites/Window/Views/AbstractEditorFavoritesView.cs @@ -0,0 +1,10 @@ +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + internal abstract class AbstractEditorFavoritesView + { + public abstract void Initialize(); + public abstract void Draw(EditorFavoritesWindow window, Rect rect, Styles styles); + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/AbstractEditorFavoritesView.cs.meta b/Editor/Favorites/Window/Views/AbstractEditorFavoritesView.cs.meta new file mode 100644 index 0000000..d88a7fd --- /dev/null +++ b/Editor/Favorites/Window/Views/AbstractEditorFavoritesView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1d16482b17fb430fa905dea21ec2fdab +timeCreated: 1613211130 \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/EditorFavoritesViewSearchList.cs b/Editor/Favorites/Window/Views/EditorFavoritesViewSearchList.cs new file mode 100644 index 0000000..2f90ba8 --- /dev/null +++ b/Editor/Favorites/Window/Views/EditorFavoritesViewSearchList.cs @@ -0,0 +1,51 @@ +using System; +using UnityEditor; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + [Serializable] + internal sealed class EditorFavoritesViewSearchList : AbstractEditorFavoritesView + { + [SerializeField] + private Vector2 scrollPosition; + + [NonSerialized] + private Favorites favorites; + + public override void Initialize() + { + favorites = FavoritesUtility.GetFavorites(); + } + + public override void Draw(EditorFavoritesWindow window, Rect rect, Styles styles) + { + float entryHeight = EditorGUIUtility.singleLineHeight; + float height = entryHeight * favorites.entries.Count; + string lowerSearchStr = window.viewTools.searchStr.ToLower(); + + GUI.BeginGroup(rect); + { + var position = new Rect(0.0f, 0.0f, rect.width, rect.height); + var viewRect = new Rect(0.0f, 0.0f, position.height > height ? position.width : position.width - 14.0f, height); + var entryRect = new Rect(0.0f, 0.0f, viewRect.width, entryHeight); + + scrollPosition = GUI.BeginScrollView(position, scrollPosition, viewRect); + { + for (var i = 0; i < favorites.entries.Count; i++) + { + Favorites.Entry e = favorites.entries[i]; + + if (!e.isAsset || !e.lowerName.Contains(lowerSearchStr)) + continue; + + FavoritesGUIUtility.DrawEntry(entryRect, favorites.entries[i], styles, true); + entryRect.y += entryHeight; + } + } + GUI.EndScrollView(); + } + GUI.EndGroup(); + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/EditorFavoritesViewSearchList.cs.meta b/Editor/Favorites/Window/Views/EditorFavoritesViewSearchList.cs.meta new file mode 100644 index 0000000..6b1772b --- /dev/null +++ b/Editor/Favorites/Window/Views/EditorFavoritesViewSearchList.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 65d3b26a5050438c8d995b3c383c474f +timeCreated: 1613211189 \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/EditorFavoritesViewTools.cs b/Editor/Favorites/Window/Views/EditorFavoritesViewTools.cs new file mode 100644 index 0000000..f1cfc57 --- /dev/null +++ b/Editor/Favorites/Window/Views/EditorFavoritesViewTools.cs @@ -0,0 +1,38 @@ +using System; +using UnityEditor; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + [Serializable] + internal sealed class EditorFavoritesViewTools : AbstractEditorFavoritesView + { + public string searchStr; + + public override void Initialize() + { + } + + public override void Draw(EditorFavoritesWindow window, Rect rect, Styles styles) + { + float dim = rect.height; + + GUI.BeginGroup(rect, styles.toolbox); + { + var r0 = new Rect(0.0f, 0.0f, rect.width - dim, dim); + var r1 = new Rect(rect.width - r0.height, 0.0f, dim, dim); + + searchStr = EditorGUI.TextField(r0, searchStr); + + if (GUI.Button(r1, "+", styles.buttonAddFolder)) + EditorFavoritesPopupWindowAddFolder.Show(new Vector2(r1.xMax, dim), null); + } + GUI.EndGroup(); + } + + public bool IsSearching() + { + return !string.IsNullOrEmpty(searchStr); + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/EditorFavoritesViewTools.cs.meta b/Editor/Favorites/Window/Views/EditorFavoritesViewTools.cs.meta new file mode 100644 index 0000000..0927bf6 --- /dev/null +++ b/Editor/Favorites/Window/Views/EditorFavoritesViewTools.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b597a5bc0de34d96accec02ef9cb80ed +timeCreated: 1613211779 \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/EditorFavoritesViewTreeList.cs b/Editor/Favorites/Window/Views/EditorFavoritesViewTreeList.cs new file mode 100644 index 0000000..f43f01d --- /dev/null +++ b/Editor/Favorites/Window/Views/EditorFavoritesViewTreeList.cs @@ -0,0 +1,61 @@ +using System; +using UnityEditor; +using UnityEngine; + +namespace Game.NavigationTool.Editor +{ + [Serializable] + internal sealed class EditorFavoritesViewTreeList : AbstractEditorFavoritesView + { + [SerializeField] + private Vector2 scrollPosition; + + [NonSerialized] + private Favorites favorites; + + public override void Initialize() + { + favorites = FavoritesUtility.GetFavorites(); + } + + public override void Draw(EditorFavoritesWindow window, Rect rect, Styles styles) + { + float entryHeight = EditorGUIUtility.singleLineHeight; + float height = entryHeight * favorites.entries.Count; + + GUI.BeginGroup(rect); + { + var position = new Rect(0.0f, 0.0f, rect.width, rect.height); + var viewRect = new Rect(0.0f, 0.0f, position.height > height ? position.width : position.width - 14.0f, height); + var entryRect = new Rect(0.0f, 0.0f, viewRect.width, entryHeight); + + scrollPosition = GUI.BeginScrollView(position, scrollPosition, viewRect); + { + for (var i = 0; i < favorites.entries.Count; i++) + { + Favorites.Entry e = favorites.entries[i]; + + if (e.indentLevel == 0) + DrawEntry(e, ref entryRect, entryHeight, styles); + } + } + GUI.EndScrollView(); + } + GUI.EndGroup(); + } + + private void DrawEntry(Favorites.Entry entry, ref Rect entryRect, float entryHeight, Styles styles) + { + FavoritesGUIUtility.DrawEntry(entryRect, entry, styles); + entryRect.y += entryHeight; + + if (entry.isAsset || !entry.expanded) + return; + + for (var i = 0; i < entry.children.Count; i++) + { + DrawEntry(entry.children[i].entry, ref entryRect, entryHeight, styles); + } + } + } +} \ No newline at end of file diff --git a/Editor/Favorites/Window/Views/EditorFavoritesViewTreeList.cs.meta b/Editor/Favorites/Window/Views/EditorFavoritesViewTreeList.cs.meta new file mode 100644 index 0000000..10bfd79 --- /dev/null +++ b/Editor/Favorites/Window/Views/EditorFavoritesViewTreeList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb65c37d7ed60d14daa08e98eb9ee334 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Game.NavigationTool.Editor.asmdef b/Editor/Game.NavigationTool.Editor.asmdef new file mode 100644 index 0000000..63878ee --- /dev/null +++ b/Editor/Game.NavigationTool.Editor.asmdef @@ -0,0 +1,15 @@ +{ + "name": "Game.NavigationTool.Editor", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Editor/Game.NavigationTool.Editor.asmdef.meta b/Editor/Game.NavigationTool.Editor.asmdef.meta new file mode 100644 index 0000000..678e91c --- /dev/null +++ b/Editor/Game.NavigationTool.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8339b3ce77cb32f489776d81010ad36c +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json new file mode 100644 index 0000000..a72dda0 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "com.module.navigationtool", + "version": "0.1.0", + "displayName": "Module.NavigationTool", + "description": "Support for navigation tools", + "unity": "2019.2", + "unityRelease": "17f1", + "dependencies": { + }, + "keywords": [ + "navigation" + ], + "author": { + "name": "Anders Ejlersen", + "email": "anders@ejlersen.info", + "url": "https://www.ejlersen.info" + } +} \ No newline at end of file diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..fec5a84 --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6896738deb10ff34e8931d5e91ac5f8a +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: