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("Prefab Browser"); } private void LoadDatabase() { string[] guids = AssetDatabase.FindAssets("t:PrefabBrowserDatabase"); if (guids.Length > 0) { string path = AssetDatabase.GUIDToAssetPath(guids[0]); database = AssetDatabase.LoadAssetAtPath(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() .Where(go => PrefabUtility.IsPartOfPrefabAsset(go) && !database.groups[selectedGroupIndex].prefabs.Contains(go)); database.groups[selectedGroupIndex].prefabs.AddRange(selectedPrefabs); EditorUtility.SetDirty(database); } currentEvent.Use(); } }