using UnityEditor; using UnityEngine; using System.Linq; [InitializeOnLoad] public static class CustomHierarchyDecorator { private static HierarchyDecoratorSettings settings; static CustomHierarchyDecorator() { LoadSettings(); // Rejestracja funkcji odpowiedzialnej za rysowanie niestandardowych nagłówków EditorApplication.hierarchyWindowItemOnGUI -= OnHierarchyGUI; EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyGUI; // Monitorowanie zmian w hierarchii EditorApplication.hierarchyChanged += OnHierarchyChanged; } // Aktualizacja ustawień public static void UpdateSettings(HierarchyDecoratorSettings newSettings) { settings = newSettings; EditorApplication.RepaintHierarchyWindow(); } // Ładowanie ustawień private static void LoadSettings() { settings = AssetDatabase.LoadAssetAtPath("Assets/Scripts/Editor/HierarchyDecoratorSettings.asset"); if (settings == null) { Debug.LogWarning("HierarchyDecoratorSettings not found! Please create one in Assets/Scripts/Editor folder or check the path."); return; } //Debug.Log("HierarchyDecoratorSettings loaded successfully."); } // Rysowanie nagłówka private static void OnHierarchyGUI(int instanceID, Rect selectionRect) { if (settings == null) return; GameObject obj = EditorUtility.InstanceIDToObject(instanceID) as GameObject; if (obj != null && obj.name.Contains("=")) { DrawCustomHeader(selectionRect, obj); // Całkowite zablokowanie zmiany nazwy w Hierarchii Event e = Event.current; if (e != null && e.type == EventType.MouseDown && e.clickCount == 2 && selectionRect.Contains(e.mousePosition)) { // Zapobiegamy aktywacji trybu edycji nazwy GUIUtility.keyboardControl = 0; // Zapobiegamy edytowaniu nazwy e.Use(); } } } // Funkcja rysująca niestandardowy nagłówek private static void DrawCustomHeader(Rect rect, GameObject obj) { if (obj.name.Trim() == "= Clean") { // Jeśli jest "Clean", rysujemy tylko tło EditorGUI.DrawRect(rect, settings.cleanBackgroundColor); return; } string[] parts = obj.name.Split(new[] { '=' }, System.StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 1) return; string mainText = parts[0].Trim(); string descriptionText = parts.Length > 1 ? parts[1].Trim() : ""; // Jeśli istnieje drugi `=`, traktujemy go jako opis // Obsługa identyfikatora koloru int colorIdentifier; bool hasIdentifier = int.TryParse(parts[0].Trim(), out colorIdentifier); if (hasIdentifier) { // Jeśli jest identyfikator, przypisujemy główny tekst i opis mainText = parts.Length > 1 ? parts[1].Trim() : parts[0].Trim(); descriptionText = parts.Length > 2 ? parts[2].Trim() : ""; // Jeśli jest opis, przypisujemy go } Color backgroundColor = obj.activeSelf ? settings.defaultBackgroundColor : settings.defaultInactiveBackgroundColor; Color textColor = obj.activeSelf ? settings.defaultTextColor : settings.defaultInactiveTextColor; Color descriptionTextColor = settings.descriptionTextColor; if (hasIdentifier) { // Sprawdzamy identyfikator koloru i przypisujemy odpowiednie kolory ColorScheme scheme = settings.colorSchemes.FirstOrDefault(s => s.identifier == colorIdentifier); if (scheme != null) { backgroundColor = obj.activeSelf ? scheme.activeBackgroundColor : scheme.inactiveBackgroundColor; textColor = obj.activeSelf ? scheme.activeTextColor : scheme.inactiveTextColor; descriptionTextColor = scheme.activeTextColor; } } // Rysujemy tło dla obiektu EditorGUI.DrawRect(rect, backgroundColor); // Styl dla głównego tekstu (wycentrowany) GUIStyle mainTextStyle = new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleLeft, // Wyrównanie do lewej strony fontStyle = FontStyle.Bold, normal = { textColor = textColor }, wordWrap = false // Wyłączenie zawijania tekstu }; Rect mainTextRect = new Rect(rect.x + settings.mainTextOffset, rect.y, rect.width - 10, rect.height); // Zmniejszamy szerokość EditorGUI.LabelField(mainTextRect, new GUIContent(mainText), mainTextStyle); // Jeśli istnieje opis, wyświetlamy go mniejszą czcionką po prawej stronie if (!string.IsNullOrEmpty(descriptionText)) { GUIStyle descriptionStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft, // Zmieniono na wyrównanie do lewej fontSize = settings.descriptionFontSize, // Stała czcionka normal = { textColor = descriptionTextColor }, wordWrap = false // Wyłączenie zawijania tekstu }; // Opis wyrównany do końca głównego tekstu, zawsze w tej samej odległości float descriptionX = rect.x + settings.mainTextOffset + mainTextStyle.CalcSize(new GUIContent(mainText)).x + settings.descriptionOffset; Rect descriptionRect = new Rect(descriptionX, rect.y, rect.width - descriptionX, rect.height); // Dostosowanie szerokości do głównego tekstu EditorGUI.LabelField(descriptionRect, new GUIContent(descriptionText), descriptionStyle); } } // Funkcja monitorująca zmiany w hierarchii (np. zablokowanie dodawania dzieci do separatorów) private static void OnHierarchyChanged() { EditorApplication.delayCall += () => { GameObject[] cleanSeparators = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None) .Where(obj => obj.name.Trim() == "= Clean") .ToArray(); foreach (var cleanObj in cleanSeparators) { for (int i = cleanObj.transform.childCount - 1; i >= 0; i--) { Transform child = cleanObj.transform.GetChild(i); Undo.SetTransformParent(child, null, "Prevent Child to Clean Separator"); Debug.Log($"Blocked child object {child.name} from being added to {cleanObj.name}"); } } }; } }