using System; using System.Linq; using UnityEditor; using UnityEditor.IMGUI.Controls; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.UIElements; namespace Alchemy.Editor.Elements { /// /// Draw properties marked with SerializeReference attribute /// public sealed class SerializeReferenceField : VisualElement { public SerializeReferenceField(SerializedProperty property) { Assert.IsTrue(property.propertyType == SerializedPropertyType.ManagedReference); style.flexDirection = FlexDirection.Row; style.minHeight = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; foldout = new Foldout() { text = ObjectNames.NicifyVariableName(property.displayName) }; foldout.style.flexGrow = 1f; foldout.BindProperty(property); Add(foldout); buttonContainer = new IMGUIContainer(() => { var position = EditorGUILayout.GetControlRect(); var dropdownRect = position; dropdownRect.height = EditorGUIUtility.singleLineHeight; var buttonLabel = EditorIcons.CsScriptIcon; try { if (property != null) { buttonLabel.text = (property.managedReferenceValue == null ? "Null" : property.managedReferenceValue.GetType().Name) + $" ({property.GetManagedReferenceFieldTypeName()})"; } } catch (InvalidOperationException) { // Ignoring exceptions when disposed (bad solution) return; } if (GUI.Button(dropdownRect, buttonLabel, EditorStyles.objectField)) { const int MaxTypePopupLineCount = 13; var baseType = property.GetManagedReferenceFieldType(); SerializeReferenceDropdown dropdown = new( TypeCache.GetTypesDerivedFrom(baseType).Append(baseType).Where(t => (t.IsPublic || t.IsNestedPublic) && !t.IsAbstract && !t.IsGenericType && !typeof(UnityEngine.Object).IsAssignableFrom(t) && t.IsSerializable ), MaxTypePopupLineCount, new AdvancedDropdownState() ); dropdown.onItemSelected += item => { property.SetManagedReferenceType(item.type); property.isExpanded = true; property.serializedObject.ApplyModifiedProperties(); property.serializedObject.Update(); Rebuild(property); }; dropdown.Show(position); } }); schedule.Execute(() => { var visualTree = panel.visualTree; visualTree.RegisterCallback(x => { buttonContainer.style.width = GUIHelper.CalculateFieldWidth(buttonContainer, visualTree) - (buttonContainer.GetFirstAncestorOfType() != null ? 18f : 0f); }); buttonContainer.style.width = GUIHelper.CalculateFieldWidth(buttonContainer, visualTree) - (buttonContainer.GetFirstAncestorOfType() != null ? 18f : 0f); }); buttonContainer.style.position = Position.Absolute; buttonContainer.style.top = EditorGUIUtility.standardVerticalSpacing * 0.5f; buttonContainer.style.right = 0f; Add(buttonContainer); Rebuild(property); } public readonly Foldout foldout; public readonly IMGUIContainer buttonContainer; /// /// Rebuild child elements /// void Rebuild(SerializedProperty property) { foldout.Clear(); if (property.managedReferenceValue == null) { var helpbox = new HelpBox("No type assigned.", HelpBoxMessageType.Info); foldout.Add(helpbox); } else { InspectorHelper.BuildElements(property.serializedObject, foldout, property.managedReferenceValue, x => property.FindPropertyRelative(x)); } this.Bind(property.serializedObject); } } }