352 lines
14 KiB
C#
352 lines
14 KiB
C#
using UnityEditor;
|
|
using UnityEngine;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
public class PrefabBrowserWindow : EditorWindow
|
|
{
|
|
private PrefabBrowserDatabase database;
|
|
private int selectedGroupIndex = -1;
|
|
private string newGroupName = "New Group";
|
|
private Vector2 leftPaneScroll;
|
|
private Vector2 rightPaneScroll;
|
|
private GUIStyle frameStyle;
|
|
|
|
private float iconSize = 80f;
|
|
private string searchFilter = "";
|
|
private const float ListViewThreshold = 61f;
|
|
|
|
private GameObject selectedPrefab = null;
|
|
|
|
private void OnEnable()
|
|
{
|
|
LoadDatabase();
|
|
iconSize = EditorPrefs.GetFloat("PrefabBrowser_IconSize", 80f);
|
|
}
|
|
|
|
private void OnFocus()
|
|
{
|
|
LoadDatabase();
|
|
Repaint();
|
|
}
|
|
|
|
[MenuItem("Tools/Prefab Browser")]
|
|
public static void ShowWindow()
|
|
{
|
|
GetWindow<PrefabBrowserWindow>("Prefab Browser");
|
|
}
|
|
|
|
private void LoadDatabase()
|
|
{
|
|
string[] guids = AssetDatabase.FindAssets("t:PrefabBrowserDatabase");
|
|
if (guids.Length > 0)
|
|
{
|
|
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
|
database = AssetDatabase.LoadAssetAtPath<PrefabBrowserDatabase>(path);
|
|
}
|
|
else
|
|
{
|
|
database = null;
|
|
}
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
if (database == null)
|
|
{
|
|
EditorGUILayout.HelpBox("Prefab Browser Database not found. Please create one via:\nAssets -> Create -> Tools -> Prefab Browser Database", MessageType.Warning);
|
|
if (GUILayout.Button("Try to Reload Database")) LoadDatabase();
|
|
return;
|
|
}
|
|
|
|
if (frameStyle == null)
|
|
{
|
|
frameStyle = new GUIStyle(GUI.skin.box);
|
|
frameStyle.margin = new RectOffset(4, 4, 0, 5);
|
|
frameStyle.padding = new RectOffset(2, 2, 2, 2);
|
|
}
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
DrawLeftPane();
|
|
DrawRightPane();
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private void DrawLeftPane()
|
|
{
|
|
EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.Width(200), GUILayout.ExpandHeight(true));
|
|
EditorGUILayout.LabelField("Prefab Groups", EditorStyles.boldLabel);
|
|
leftPaneScroll = EditorGUILayout.BeginScrollView(leftPaneScroll);
|
|
|
|
if (database.groups != null)
|
|
{
|
|
for (int i = 0; i < database.groups.Count; i++)
|
|
{
|
|
var group = database.groups[i];
|
|
|
|
EditorGUILayout.BeginVertical(frameStyle);
|
|
{
|
|
Color rowBackgroundColor = (i == selectedGroupIndex) ? new Color(0.24f, 0.48f, 0.85f) : group.groupColor;
|
|
Color textColor = (i == selectedGroupIndex) ? Color.white : (group.useLightText ? Color.white : Color.black);
|
|
|
|
Rect coloredBarRect = GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, GUILayout.Height(22));
|
|
EditorGUI.DrawRect(coloredBarRect, rowBackgroundColor);
|
|
|
|
GUIStyle labelStyle = new GUIStyle(EditorStyles.label) { normal = { textColor = textColor } };
|
|
labelStyle.fontStyle = FontStyle.Bold;
|
|
labelStyle.padding.left = 5;
|
|
GUI.Label(new Rect(coloredBarRect.x, coloredBarRect.y, coloredBarRect.width - 25, coloredBarRect.height), group.groupName, labelStyle);
|
|
|
|
Rect settingsIconRect = new Rect(coloredBarRect.x + coloredBarRect.width - 22, coloredBarRect.y + 2, 18, 18);
|
|
GUI.DrawTexture(settingsIconRect, EditorGUIUtility.IconContent("d_Settings").image);
|
|
|
|
Event currentEvent = Event.current;
|
|
if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
|
|
{
|
|
if (settingsIconRect.Contains(currentEvent.mousePosition))
|
|
{
|
|
PrefabGroupSettingsWindow.ShowAsDropDown(settingsIconRect, group, database);
|
|
currentEvent.Use();
|
|
}
|
|
else if (coloredBarRect.Contains(currentEvent.mousePosition))
|
|
{
|
|
if (selectedGroupIndex != i)
|
|
{
|
|
selectedPrefab = null;
|
|
}
|
|
selectedGroupIndex = i;
|
|
Repaint();
|
|
currentEvent.Use();
|
|
}
|
|
}
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.LabelField("Create New Group:", EditorStyles.miniBoldLabel);
|
|
newGroupName = EditorGUILayout.TextField(newGroupName);
|
|
if (GUILayout.Button("Add Group") && !string.IsNullOrWhiteSpace(newGroupName))
|
|
{
|
|
database.groups.Add(new PrefabGroup { groupName = newGroupName });
|
|
newGroupName = "New Group";
|
|
EditorUtility.SetDirty(database);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawRightPane()
|
|
{
|
|
EditorGUILayout.BeginVertical(GUI.skin.box, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
|
|
|
|
if (selectedGroupIndex < 0 || database.groups == null || selectedGroupIndex >= database.groups.Count)
|
|
{
|
|
EditorGUILayout.LabelField("Select a group from the left panel.", EditorStyles.centeredGreyMiniLabel);
|
|
EditorGUILayout.EndVertical();
|
|
return;
|
|
}
|
|
|
|
var selectedGroup = database.groups[selectedGroupIndex];
|
|
|
|
Color headerBgColor = selectedGroup.groupColor;
|
|
Color headerTextColor = selectedGroup.useLightText ? Color.white : Color.black;
|
|
GUIStyle headerStyle = new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter, padding = new RectOffset(5, 5, 5, 5) };
|
|
Texture2D headerBgTexture = new Texture2D(1, 1);
|
|
headerBgTexture.SetPixel(0, 0, headerBgColor);
|
|
headerBgTexture.Apply();
|
|
headerStyle.normal.background = headerBgTexture;
|
|
headerStyle.normal.textColor = headerTextColor;
|
|
GUILayout.Label($"Prefabs in '{selectedGroup.groupName}'", headerStyle, GUILayout.ExpandWidth(true));
|
|
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField);
|
|
if (GUILayout.Button("X", EditorStyles.toolbarButton, GUILayout.Width(22)))
|
|
{
|
|
searchFilter = "";
|
|
GUI.FocusControl(null);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
rightPaneScroll = EditorGUILayout.BeginScrollView(rightPaneScroll);
|
|
|
|
var filteredPrefabs = selectedGroup.prefabs.Where(p => p != null &&
|
|
(string.IsNullOrWhiteSpace(searchFilter) || p.name.ToLowerInvariant().Contains(searchFilter.ToLowerInvariant()))
|
|
).ToList();
|
|
|
|
if (iconSize < ListViewThreshold)
|
|
{
|
|
foreach (var prefab in filteredPrefabs)
|
|
{
|
|
DrawPrefabListItem(prefab, selectedGroup);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
float cellWidth = iconSize + 20;
|
|
int gridCols = Mathf.Max(1, Mathf.FloorToInt((position.width - 220) / cellWidth));
|
|
|
|
if (filteredPrefabs.Count > 0)
|
|
{
|
|
for (int i = 0; i < filteredPrefabs.Count; i += gridCols)
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
for (int j = 0; j < gridCols; j++)
|
|
{
|
|
if (i + j < filteredPrefabs.Count)
|
|
{
|
|
DrawPrefabIcon(filteredPrefabs[i + j], selectedGroup, iconSize);
|
|
}
|
|
else GUILayout.Space(cellWidth);
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
DrawBottomControls();
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawBottomControls()
|
|
{
|
|
EditorGUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
float newIconSize = GUILayout.HorizontalSlider(iconSize, 60f, 120f, GUILayout.Width(150));
|
|
if (newIconSize != iconSize)
|
|
{
|
|
iconSize = newIconSize;
|
|
EditorPrefs.SetFloat("PrefabBrowser_IconSize", iconSize);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
Rect dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
|
|
GUI.Box(dropArea, "Drag & Drop Prefabs Here");
|
|
HandleDragAndDrop(dropArea);
|
|
}
|
|
|
|
// --- NOWA FUNKCJA POMOCNICZA: Rysuje ramkê wokó³ prostok¹ta ---
|
|
private void DrawSelectionFrame(Rect rect, Color color, int thickness)
|
|
{
|
|
// Rysujemy 4 prostok¹ty (góra, dó³, lewo, prawo), aby stworzyæ ramkê
|
|
// U¿ywamy bia³ej tekstury i zabarwiamy j¹ na po¿¹dany kolor - to standardowa i wydajna technika w edytorze.
|
|
Texture2D texture = EditorGUIUtility.whiteTexture;
|
|
GUI.color = color;
|
|
GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, thickness), texture); // Góra
|
|
GUI.DrawTexture(new Rect(rect.x, rect.yMax - thickness, rect.width, thickness), texture); // Dó³
|
|
GUI.DrawTexture(new Rect(rect.x, rect.y, thickness, rect.height), texture); // Lewo
|
|
GUI.DrawTexture(new Rect(rect.xMax - thickness, rect.y, thickness, rect.height), texture); // Prawo
|
|
GUI.color = Color.white; // Resetujemy kolor do domyœlnego
|
|
}
|
|
|
|
private void DrawPrefabListItem(GameObject prefab, PrefabGroup group)
|
|
{
|
|
EditorGUILayout.BeginHorizontal(GUI.skin.box);
|
|
|
|
Texture icon = AssetPreview.GetMiniThumbnail(prefab);
|
|
GUILayout.Label(icon, GUILayout.Width(20), GUILayout.Height(20));
|
|
EditorGUILayout.LabelField(prefab.name);
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("Remove", GUILayout.Width(60)))
|
|
{
|
|
group.prefabs.Remove(prefab);
|
|
EditorUtility.SetDirty(database);
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
Rect itemRect = GUILayoutUtility.GetLastRect();
|
|
|
|
// --- ZMIANA: Rysujemy ramkê zamiast pó³przezroczystego t³a ---
|
|
if (selectedPrefab == prefab)
|
|
{
|
|
DrawSelectionFrame(itemRect, new Color(0.24f, 0.48f, 0.85f, 1f), 1); // Cieñsza ramka dla listy
|
|
}
|
|
|
|
Event currentEvent = Event.current;
|
|
// Rozdzielamy logikê: klikniêcie zaznacza, przeci¹ganie rozpoczyna siê, gdy mysz jest wciœniêta i siê rusza
|
|
if (itemRect.Contains(currentEvent.mousePosition))
|
|
{
|
|
if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
|
|
{
|
|
selectedPrefab = prefab;
|
|
Repaint();
|
|
currentEvent.Use();
|
|
}
|
|
if (currentEvent.type == EventType.MouseDrag && currentEvent.button == 0 && selectedPrefab == prefab)
|
|
{
|
|
DragAndDrop.PrepareStartDrag();
|
|
DragAndDrop.objectReferences = new Object[] { prefab };
|
|
DragAndDrop.StartDrag(prefab.name);
|
|
currentEvent.Use();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawPrefabIcon(GameObject prefab, PrefabGroup group, float currentIconSize)
|
|
{
|
|
if (prefab == null) return;
|
|
float cellWidth = currentIconSize + 20;
|
|
EditorGUILayout.BeginVertical(GUILayout.Width(cellWidth));
|
|
Texture2D preview = AssetPreview.GetAssetPreview(prefab) ?? AssetPreview.GetMiniTypeThumbnail(typeof(GameObject));
|
|
|
|
Rect previewRect = GUILayoutUtility.GetRect(currentIconSize, currentIconSize);
|
|
GUI.Box(previewRect, preview);
|
|
|
|
// --- ZMIANA: Rysujemy ramkê zamiast pó³przezroczystego t³a ---
|
|
if (selectedPrefab == prefab)
|
|
{
|
|
DrawSelectionFrame(previewRect, new Color(0.24f, 0.48f, 0.85f, 1f), 2); // Grubsza ramka dla siatki
|
|
}
|
|
|
|
Event currentEvent = Event.current;
|
|
// Rozdzielamy logikê: klikniêcie zaznacza, przeci¹ganie rozpoczyna siê, gdy mysz jest wciœniêta i siê rusza
|
|
if (previewRect.Contains(currentEvent.mousePosition))
|
|
{
|
|
if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
|
|
{
|
|
selectedPrefab = prefab;
|
|
Repaint();
|
|
currentEvent.Use();
|
|
}
|
|
if (currentEvent.type == EventType.MouseDrag && currentEvent.button == 0 && selectedPrefab == prefab)
|
|
{
|
|
DragAndDrop.PrepareStartDrag();
|
|
DragAndDrop.objectReferences = new Object[] { prefab };
|
|
DragAndDrop.StartDrag(prefab.name);
|
|
currentEvent.Use();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.LabelField(prefab.name, EditorStyles.miniLabel, GUILayout.Width(currentIconSize));
|
|
if (GUILayout.Button("Remove", EditorStyles.miniButton))
|
|
{
|
|
group.prefabs.Remove(prefab);
|
|
EditorUtility.SetDirty(database);
|
|
}
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void HandleDragAndDrop(Rect dropArea)
|
|
{
|
|
Event currentEvent = Event.current;
|
|
if (currentEvent.type != EventType.DragUpdated && currentEvent.type != EventType.DragPerform) return;
|
|
if (!dropArea.Contains(currentEvent.mousePosition)) return;
|
|
|
|
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
|
if (currentEvent.type == EventType.DragPerform)
|
|
{
|
|
DragAndDrop.AcceptDrag();
|
|
var selectedPrefabs = DragAndDrop.objectReferences
|
|
.OfType<GameObject>()
|
|
.Where(go => PrefabUtility.IsPartOfPrefabAsset(go) && !database.groups[selectedGroupIndex].prefabs.Contains(go));
|
|
|
|
database.groups[selectedGroupIndex].prefabs.AddRange(selectedPrefabs);
|
|
EditorUtility.SetDirty(database);
|
|
}
|
|
currentEvent.Use();
|
|
}
|
|
} |