using System.Linq; using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; using UnityEditor; using System; using System.Reflection; namespace NanoBrain.Unity { [CustomPropertyDrawer(typeof(ClusterPrefab))] class ClusterPrefab_Drawer : PropertyDrawer { public static void Insepctor(SerializedObject serializedObject, string propertyName) { EditorGUILayout.PropertyField(serializedObject.FindProperty(propertyName)); } // Cache VisualElement per property path to avoid recreating every frame static Dictionary s_cache = new Dictionary(); const float padding = 4f; const float elementHeight = 64f; // height reserved for the VisualElement public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { float height = EditorGUIUtility.singleLineHeight + padding; string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId(); s_foldouts.TryGetValue(key, out bool isOpen); if (property.objectReferenceValue != null && isOpen) { height += padding + elementHeight; height = 500; } else height = 36; return height; } static Dictionary s_foldouts = new Dictionary(); public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { label = EditorGUI.BeginProperty(position, label, property); // Begin indent block int indent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; // Draw the object field on the top line Rect fieldRect = new(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight); EditorGUI.PropertyField(fieldRect, property, label); if (property.objectReferenceValue is ClusterPrefab prefab) { // key per field instance string key = property.propertyPath + "_" + property.serializedObject.targetObject.GetEntityId(); if (!s_foldouts.TryGetValue(key, out bool isOpen)) isOpen = true; // foldout header rect Rect headerRect = new(fieldRect.x, fieldRect.yMax + 4f, fieldRect.width, EditorGUIUtility.singleLineHeight); isOpen = EditorGUI.Foldout(headerRect, isOpen, "Graph", true); s_foldouts[key] = isOpen; if (isOpen) { // content rect below header Rect drawRect = new(fieldRect.x, headerRect.yMax + 2f, fieldRect.width, 450f); ClusterView.Render(drawRect, prefab.cluster, property); //Debug.Log(prefab.cluster.defaultOutput.outputMagnitude); } } EditorGUI.indentLevel = indent; EditorGUI.EndProperty(); } } /* [InitializeOnLoad] static class ClusterPrefabInspectorRepaints { static ClusterPrefabInspectorRepaints() { EditorApplication.update += OnEditorUpdate; } static double lastRepaint = 0; const double repaintInterval = 1.0 / 15.0; // up to 15 FPS in inspector static void OnEditorUpdate() { if (!Application.isPlaying) return; // throttle repaint frequency if (EditorApplication.timeSinceStartup - lastRepaint < repaintInterval) return; lastRepaint = EditorApplication.timeSinceStartup; // Find all open inspectors (Editors) that target objects containing ClusterPrefab fields var editors = Resources.FindObjectsOfTypeAll(); foreach (var ed in editors) { var targets = ed.targets; if (targets == null) continue; bool shouldRepaint = targets.Any(t => ObjectHasClusterPrefabField(t)); if (shouldRepaint) { try { ed.Repaint(); } catch { // ignore } } } } static bool ObjectHasClusterPrefabField(UnityEngine.Object obj) { if (obj == null) return false; Type type = obj.GetType(); // search fields (instance, non-public/public) FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo f in fields) { if (f.FieldType == typeof(ClusterPrefab)) return true; // also handle arrays/lists of ClusterPrefab or serializable wrappers: if (f.FieldType.IsArray && f.FieldType.GetElementType() == typeof(ClusterPrefab)) return true; if (f.FieldType.IsGenericType) { Type[] gen = f.FieldType.GetGenericArguments(); if (gen.Length == 1 && gen[0] == typeof(ClusterPrefab)) return true; } } return false; } } */ }