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<PrefabWin>(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<Item> mItems = new BetterList<Item>();
|
|
|
|
GameObject[] draggedObjects
|
|
{
|
|
get
|
|
{
|
|
if (DragAndDrop.objectReferences == null || DragAndDrop.objectReferences.Length == 0)
|
|
return null;
|
|
|
|
return DragAndDrop.objectReferences.Where(x=>x as GameObject).Cast<GameObject>().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<string> filtered = new List<string>();
|
|
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<GameObject>(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<Light> 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<Item> _selections = new List<Item>();
|
|
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<int> indices = new BetterList<int>();
|
|
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);
|
|
}
|
|
}
|
|
}
|