using UnityEditor; using UnityEngine; using System.Collections.Generic; using System.Text; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace U3DExtends { public class PrefabWin : EditorWindow { private static int _labelDefaultFontSize; [MenuItem("Window/PrefabWin", false, 9)] static public void OpenPrefabTool() { _labelDefaultFontSize = EditorStyles.label.fontSize; PrefabWin prefabWin = (PrefabWin)EditorWindow.GetWindow(false, "Prefab Win", true); prefabWin.autoRepaintOnSceneChange = true; prefabWin.Show(); } static public PrefabWin instance; class Item { public GameObject prefab; public string guid; public Texture tex; public bool dynamicTex = false; } enum Mode { CompactMode, IconMode, DetailedMode, } const int cellPadding = 4; float mSizePercent = 0.5f; public float SizePercent { get { return mSizePercent; } set { if (mSizePercent != value) { mReset = true; mSizePercent = value; mCellSize = Mathf.FloorToInt(80 * SizePercent + 10); EditorPrefs.SetFloat("PrefabWin_SizePercent", mSizePercent); } } } int mCellSize=50; int cellSize { get { return mCellSize; } } int mTab = 0; Mode mMode = Mode.CompactMode; Vector2 mPos = Vector2.zero; bool mMouseIsInside = false; GUIContent mContent; GUIStyle mStyle; BetterList mItems = new BetterList(); GameObject[] draggedObjects { get { if (DragAndDrop.objectReferences == null || DragAndDrop.objectReferences.Length == 0) return null; return DragAndDrop.objectReferences.Where(x=>x as GameObject).Cast().ToArray(); } set { if (value != null) { DragAndDrop.PrepareStartDrag(); DragAndDrop.objectReferences = value; draggedObjectIsOurs = true; } else DragAndDrop.AcceptDrag(); } } bool draggedObjectIsOurs { get { object obj = DragAndDrop.GetGenericData("Prefab Tool"); if (obj == null) return false; return (bool)obj; } set { DragAndDrop.SetGenericData("Prefab Tool", value); } } void OnEnable () { instance = this; Load(); mContent = new GUIContent(); mStyle = new GUIStyle(); mStyle.alignment = TextAnchor.MiddleCenter; mStyle.padding = new RectOffset(2, 2, 2, 2); mStyle.clipping = TextClipping.Clip; mStyle.wordWrap = true; mStyle.stretchWidth = false; mStyle.stretchHeight = false; mStyle.normal.textColor = UnityEditor.EditorGUIUtility.isProSkin ? new Color(1f, 1f, 1f, 0.5f) : new Color(0f, 0f, 0f, 0.5f); mStyle.normal.background = null; } void OnDisable () { instance = null; foreach (Item item in mItems) DestroyTexture(item); Save(); } void OnSelectionChange () { Repaint(); } public void Reset () { foreach (Item item in mItems) DestroyTexture(item); mItems.Clear(); if (mTab == 0 && Configure.PrefabWinFirstSearchPath!="") { List filtered = new List(); string[] allAssets = AssetDatabase.GetAllAssetPaths(); foreach (string s in allAssets) { //search prefab files in folder:Configure.PrefabWinFirstSearchPath bool isComeFromPrefab = Regex.IsMatch(s, Configure.PrefabWinFirstSearchPath+@"/((?!/).)*\.prefab"); if (isComeFromPrefab) filtered.Add(s); } filtered.Sort(string.Compare); foreach (string s in filtered) AddGUID(AssetDatabase.AssetPathToGUID(s), -1); RectivateLights(); } } void AddItem (GameObject go, int index) { string guid = U3DExtends.UIEditorHelper.ObjectToGUID(go); if (string.IsNullOrEmpty(guid)) { string path = EditorUtility.SaveFilePanelInProject("Save a prefab", go.name + ".prefab", "prefab", "Save prefab as...", ""); if (string.IsNullOrEmpty(path)) return; go = PrefabUtility.CreatePrefab(path, go); if (go == null) return; guid = U3DExtends.UIEditorHelper.ObjectToGUID(go); if (string.IsNullOrEmpty(guid)) return; } Item ent = new Item(); ent.prefab = go; ent.guid = guid; GeneratePreview(ent); RectivateLights(); if (index < mItems.size) mItems.Insert(index, ent); else mItems.Add(ent); Save(); } Item AddGUID (string guid, int index) { GameObject go = U3DExtends.UIEditorHelper.GUIDToObject(guid); if (go != null) { Item ent = new Item(); ent.prefab = go; ent.guid = guid; GeneratePreview(ent, false); if (index < mItems.size) mItems.Insert(index, ent); else mItems.Add(ent); return ent; } return null; } void RemoveItem (object obj) { if (this == null) return; int index = (int)obj; if (index < mItems.size && index > -1) { Item item = mItems[index]; DestroyTexture(item); mItems.RemoveAt(index); } Save(); } Item FindItem (GameObject go) { for (int i = 0; i < mItems.size; ++i) if (mItems[i].prefab == go) return mItems[i]; return null; } string saveKey { get { return "PrefabWin " + Application.dataPath + " " + mTab; } } void Save () { string data = ""; if (mItems.size > 0) { string guid = mItems[0].guid; StringBuilder sb = new StringBuilder(); sb.Append(guid); for (int i = 1; i < mItems.size; ++i) { guid = mItems[i].guid; if (string.IsNullOrEmpty(guid)) { Debug.LogWarning("Unable to save " + mItems[i].prefab.name); } else { sb.Append('|'); sb.Append(mItems[i].guid); } } data = sb.ToString(); } EditorPrefs.SetString(saveKey, data); } void Load () { mTab = EditorPrefs.GetInt("PrefabWin Prefab Tab", 0); SizePercent = EditorPrefs.GetFloat("PrefabWin_SizePercent", 0.5f); foreach (Item item in mItems) DestroyTexture(item); mItems.Clear(); string data = EditorPrefs.GetString(saveKey, ""); //data = "";//For test if (string.IsNullOrEmpty(data)) { Reset(); } else { if (string.IsNullOrEmpty(data)) return; string[] guids = data.Split('|'); foreach (string s in guids) AddGUID(s, -1); RectivateLights(); } } void DestroyTexture (Item item) { if (item != null && item.dynamicTex && item.tex != null) { DestroyImmediate(item.tex); item.dynamicTex = false; item.tex = null; } } void UpdateVisual () { if (draggedObjects == null) DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; else if (draggedObjectIsOurs) DragAndDrop.visualMode = DragAndDropVisualMode.Move; else DragAndDrop.visualMode = DragAndDropVisualMode.Copy; } Item CreateItemByPath (string path) { if (!string.IsNullOrEmpty(path)) { path = FileUtil.GetProjectRelativePath(path); string guid = AssetDatabase.AssetPathToGUID(path); if (!string.IsNullOrEmpty(guid)) { GameObject go = AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)) as GameObject; Item ent = new Item(); ent.prefab = go; ent.guid = guid; GeneratePreview(ent); return ent; } else Debug.Log("No GUID"); } return null; } void GeneratePreview (Item item, bool isReCreate = true) { if (item == null || item.prefab == null) return; { string preview_path = Configure.ResAssetsPath + "/Preview/" + item.prefab.name + ".png"; if (!isReCreate && File.Exists(preview_path)) { Texture texture = UIEditorHelper.LoadTextureInLocal(preview_path); item.tex = texture; } else { Texture Tex = UIEditorHelper.GetAssetPreview(item.prefab); if (Tex != null) { DestroyTexture(item); item.tex = Tex; UIEditorHelper.SaveTextureToPNG(Tex, preview_path); } } item.dynamicTex = false; return; } } static Transform FindChild (Transform t, string startsWith) { if (t.name.StartsWith(startsWith)) return t; for (int i = 0, imax = t.childCount; i < imax; ++i) { Transform ch = FindChild(t.GetChild(i), startsWith); if (ch != null) return ch; } return null; } static BetterList mLights; static void RectivateLights () { if (mLights != null) { for (int i = 0; i < mLights.size; ++i) mLights[i].enabled = true; mLights = null; } } int GetCellUnderMouse (int spacingX, int spacingY) { Vector2 pos = Event.current.mousePosition + mPos; int topPadding = 24; int x = cellPadding, y = cellPadding + topPadding; if (pos.y < y) return -1; float width = Screen.width - cellPadding + mPos.x; float height = Screen.height - cellPadding + mPos.y; int index = 0; for (; ; ++index) { Rect rect = new Rect(x, y, spacingX, spacingY); if (rect.Contains(pos)) break; x += spacingX; if (x + spacingX > width) { if (pos.x > x) return -1; y += spacingY; x = cellPadding; if (y + spacingY > height) return -1; } } return index; } bool mReset = false; private List _selections = new List(); void OnGUI () { Event currentEvent = Event.current; EventType type = currentEvent.type; int x = cellPadding, y = cellPadding; int width = Screen.width - cellPadding; int spacingX = cellSize + cellPadding; int spacingY = spacingX; if (mMode == Mode.DetailedMode) spacingY += 32; GameObject[] draggeds = draggedObjects; bool isDragging = (draggeds != null); int indexUnderMouse = GetCellUnderMouse(spacingX, spacingY); if (isDragging) { foreach (var gameObject in draggeds) { var result = FindItem(gameObject); if (result != null) { _selections.Add(result); } } } string searchFilter = EditorPrefs.GetString("PrefabWin_SearchFilter", null); int newTab = mTab; GUILayout.BeginHorizontal(); if (GUILayout.Toggle(newTab == 0, "通用控件", "ButtonLeft")) newTab = 0; if (GUILayout.Toggle(newTab == 1, "其它模板", "ButtonRight")) newTab = 1; GUILayout.EndHorizontal(); if (mTab != newTab) { Save(); mTab = newTab; mReset = true; EditorPrefs.SetInt("PrefabWin Prefab Tab", mTab); Load(); } if (mReset && type == EventType.Repaint) { mReset = false; foreach (Item item in mItems) GeneratePreview(item, false); RectivateLights(); } bool eligibleToDrag = (currentEvent.mousePosition.y < Screen.height - 40); if (type == EventType.MouseDown) { mMouseIsInside = true; } else if (type == EventType.MouseDrag) { mMouseIsInside = true; if (indexUnderMouse != -1 && eligibleToDrag) { if (draggedObjectIsOurs) DragAndDrop.StartDrag("Prefab Tool"); currentEvent.Use(); } } else if (type == EventType.MouseUp) { DragAndDrop.PrepareStartDrag(); mMouseIsInside = false; Repaint(); } else if (type == EventType.DragUpdated) { mMouseIsInside = true; UpdateVisual(); currentEvent.Use(); } else if (type == EventType.DragPerform) { if (draggeds != null) { if (_selections != null) { foreach (var selection in _selections) { DestroyTexture(selection); mItems.Remove(selection); } } foreach (var dragged in draggeds) { AddItem(dragged, indexUnderMouse); ++indexUnderMouse; } draggeds = null; } mMouseIsInside = false; currentEvent.Use(); } else if (type == EventType.DragExited || type == EventType.Ignore) { mMouseIsInside = false; } if (!mMouseIsInside) { _selections.Clear(); draggeds = null; } BetterList indices = new BetterList(); for (int i = 0; i < mItems.size; ) { if (draggeds != null && indices.size == indexUnderMouse) indices.Add(-1); var has = _selections.Exists(item => item == mItems[i]); if (!has) { if (string.IsNullOrEmpty(searchFilter) || mItems[i].prefab.name.IndexOf(searchFilter, System.StringComparison.CurrentCultureIgnoreCase) != -1) indices.Add(i); } ++i; } if (!indices.Contains(-1)) indices.Add(-1); if (eligibleToDrag && type == EventType.MouseDown && indexUnderMouse > -1) { GUIUtility.keyboardControl = 0; if (currentEvent.button == 0 && indexUnderMouse < indices.size) { int index = indices[indexUnderMouse]; if (index != -1 && index < mItems.size) { _selections.Add(mItems[index]); draggedObjects = _selections.Select(item => item.prefab).ToArray(); draggeds = _selections.Select(item=>item.prefab).ToArray(); currentEvent.Use(); } } } mPos = EditorGUILayout.BeginScrollView(mPos); { Color normal = new Color(1f, 1f, 1f, 0.5f); for (int i = 0; i < indices.size; ++i) { int index = indices[i]; Item ent = (index != -1) ? mItems[index] : _selections.Count == 0 ? null : _selections[0]; if (ent != null && ent.prefab == null) { mItems.RemoveAt(index); continue; } Rect rect = new Rect(x, y, cellSize, cellSize); Rect inner = rect; inner.xMin += 2f; inner.xMax -= 2f; inner.yMin += 2f; inner.yMax -= 2f; rect.yMax -= 1f; if (!isDragging && (mMode == Mode.CompactMode || (ent == null || ent.tex != null))) mContent.tooltip = (ent != null) ? ent.prefab.name : "Click to add"; else mContent.tooltip = ""; //if (ent == selection) { GUI.color = normal; U3DExtends.UIEditorHelper.DrawTiledTexture(inner, U3DExtends.UIEditorHelper.backdropTexture); } GUI.color = Color.white; GUI.backgroundColor = normal; if (GUI.Button(rect, mContent, "Button")) { if (ent == null || currentEvent.button == 0) { string path = EditorUtility.OpenFilePanel("Add a prefab", "", "prefab"); if (!string.IsNullOrEmpty(path)) { Item newEnt = CreateItemByPath(path); if (newEnt != null) { mItems.Add(newEnt); Save(); } } } else if (currentEvent.button == 1) { //ContextMenu.AddItem("Update Preview", false, UpdatePreView, index); ContextMenu.AddItemWithArge("Delete", false, RemoveItem, index); ContextMenu.Show(); } } string caption = (ent == null) ? "" : ent.prefab.name.Replace("Control - ", ""); if (ent != null) { if (ent.tex == null) { //texture may be destroy after exit game GeneratePreview(ent, false); } if (ent.tex != null) { GUI.DrawTexture(inner, ent.tex); var labelPos = new Rect(inner); var labelStyle = EditorStyles.label; labelPos.height = labelStyle.lineHeight; labelPos.y = inner.height - labelPos.height + 5; labelStyle.fontSize = (int) (_labelDefaultFontSize * SizePercent); labelStyle.alignment = TextAnchor.LowerCenter; { GUI.Label(labelPos, ent.prefab.name,labelStyle); } labelStyle.alignment = TextAnchor.UpperLeft; labelStyle.fontSize = _labelDefaultFontSize; } else if (mMode != Mode.DetailedMode) { GUI.Label(inner, caption, mStyle); caption = ""; } } else GUI.Label(inner, "Add", mStyle); if (mMode == Mode.DetailedMode) { GUI.backgroundColor = new Color(1f, 1f, 1f, 0.5f); GUI.contentColor = new Color(1f, 1f, 1f, 0.7f); GUI.Label(new Rect(rect.x, rect.y + rect.height, rect.width, 32f), caption, "ProgressBarBack"); GUI.contentColor = Color.white; GUI.backgroundColor = Color.white; } x += spacingX; if (x + spacingX > width) { y += spacingY; x = cellPadding; } } GUILayout.Space(y + spacingY); } EditorGUILayout.EndScrollView(); //if (mTab == 0) { //EditorGUILayout.BeginHorizontal(); //bool isCreateBackground = GUILayout.Button("背景"); //if (isCreateBackground) // EditorApplication.ExecuteMenuItem("UIEditor/创建/Background"); //bool isCreateDecorate = GUILayout.Button("参考图"); //if (isCreateDecorate) // EditorApplication.ExecuteMenuItem("UIEditor/创建/Decorate"); //EditorGUILayout.EndHorizontal(); } //else if (mTab != 0) { GUILayout.BeginHorizontal(); { string after = EditorGUILayout.TextField("", searchFilter, "SearchTextField", GUILayout.Width(Screen.width - 20f)); if (GUILayout.Button("", "SearchCancelButton", GUILayout.Width(18f))) { after = ""; GUIUtility.keyboardControl = 0; } if (searchFilter != after) { EditorPrefs.SetString("PrefabWin_SearchFilter", after); searchFilter = after; } } GUILayout.EndHorizontal(); } SizePercent = EditorGUILayout.Slider(SizePercent, 0, 1); } } }