using System; using System.Linq; using System.Reflection; using System.Collections.Generic; using UnityEngine; using UnityEngine.VFX; using UnityEngine.VFX.Utility; using UnityEditor.VFX; using UnityEditor; using UnityEditorInternal; namespace UnityEditor.Experimental.VFX.Utility { [CustomEditor(typeof(VFXPropertyBinder))] class VFXPropertyBinderEditor : Editor { [MenuItem("CONTEXT/VFXPropertyBinder/Remove Component", false, 5000)] public static void RemoveComponent(MenuCommand command) { var propertyBinder = (VFXPropertyBinder)command.context; propertyBinder.ClearPropertyBinders(); Undo.DestroyObjectImmediate(propertyBinder); } ReorderableList m_List; SerializedProperty m_Elements; SerializedProperty m_Component; SerializedProperty m_ExecuteInEditor; GenericMenu m_Menu; VFXBinderEditor m_ElementEditor; static readonly Color validColor = new Color(0.5f, 1.0f, 0.2f); static readonly Color invalidColor = new Color(1.0f, 0.5f, 0.2f); static readonly Color errorColor = new Color(1.0f, 0.2f, 0.2f); static class Styles { public static GUIStyle labelStyle; static Styles() { labelStyle = new GUIStyle(EditorStyles.label) { padding = new RectOffset(20, 0, 2, 0), richText = true }; } } private void OnEnable() { BuildMenu(); m_Elements = serializedObject.FindProperty("m_Bindings"); m_Component = serializedObject.FindProperty("m_VisualEffect"); m_ExecuteInEditor = serializedObject.FindProperty("m_ExecuteInEditor"); m_List = new ReorderableList(serializedObject, m_Elements, false, true, true, true); m_List.drawHeaderCallback = DrawHeader; m_List.drawElementCallback = DrawElement; m_List.onRemoveCallback = RemoveElement; m_List.onAddCallback = AddElement; m_List.onSelectCallback = SelectElement; } private void OnDisable() { } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUI.BeginChangeCheck(); EditorGUILayout.Space(); EditorGUILayout.PropertyField(m_ExecuteInEditor); EditorGUILayout.Space(); m_List.DoLayoutList(); EditorGUILayout.Space(); if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties(); if (m_ElementEditor != null && m_ElementEditor.target != null && m_ElementEditor.serializedObject.targetObject != null) { m_ElementEditor.serializedObject.Update(); EditorGUI.BeginChangeCheck(); var fieldAttribute = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic; var binding = m_ElementEditor.serializedObject.targetObject; var type = binding.GetType(); var fields = type.GetFields(fieldAttribute); foreach (var field in fields) { var property = m_ElementEditor.serializedObject.FindProperty(field.Name); if (property == null) continue; using (new GUILayout.HorizontalScope()) { var attrib = field.GetCustomAttributes(true).OfType().FirstOrDefault(); if (attrib != null) { var parameter = property.FindPropertyRelative("m_Name"); string parm = parameter.stringValue; parm = EditorGUILayout.TextField(ObjectNames.NicifyVariableName(property.name), parm); if (parm != parameter.stringValue) { parameter.stringValue = parm; serializedObject.ApplyModifiedProperties(); } if (GUILayout.Button("v", EditorStyles.toolbarButton, GUILayout.Width(14))) CheckTypeMenu(property, attrib, (m_Component.objectReferenceValue as VisualEffect).visualEffectAsset); } else { EditorGUILayout.PropertyField(property, true); } } } if (EditorGUI.EndChangeCheck()) m_ElementEditor.serializedObject.ApplyModifiedProperties(); var component = (m_Component.objectReferenceValue as VisualEffect); bool valid = (binding as VFXBinderBase).IsValid(component); if (!valid) { EditorGUILayout.HelpBox("This binding is not correctly configured, please ensure Property is valid and/or objects are not null", MessageType.Warning); } } } private class MenuPropertySetName { public SerializedProperty property; public string value; } public void CheckTypeMenu(SerializedProperty property, VFXPropertyBindingAttribute attribute, VisualEffectAsset asset) { VFXGraph graph = null; if (asset != null) { var resource = asset.GetResource(); if (resource != null) //If VisualEffectGraph is store in asset bundle, we can't use this following code { graph = resource.graph as VFXGraph; } } if (graph == null) return; var menu = new GenericMenu(); var parameters = graph.children.OfType(); foreach (var param in parameters) { string typeName = param.type.ToString(); if (attribute.EditorTypes.Contains(typeName)) { MenuPropertySetName set = new MenuPropertySetName { property = property, value = param.exposedName }; menu.AddItem(new GUIContent(param.exposedName), false, SetFieldName, set); } } menu.ShowAsContext(); } public void SetFieldName(object o) { var set = o as MenuPropertySetName; set.property.FindPropertyRelative("m_Name").stringValue = set.value; m_ElementEditor.serializedObject.ApplyModifiedProperties(); } private static Type[] s_ConcreteBinders = null; public void BuildMenu() { m_Menu = new GenericMenu(); if (s_ConcreteBinders == null) s_ConcreteBinders = VFXLibrary.FindConcreteSubclasses(typeof(VFXBinderBase), typeof(VFXBinderAttribute)).ToArray(); foreach (var type in s_ConcreteBinders) { string name = type.ToString(); var attrib = type.GetCustomAttributes(true).OfType().First(); name = attrib.MenuPath; m_Menu.AddItem(new GUIContent(name), false, AddBinding, type); } } public void AddBinding(object type) { Type t = type as Type; var obj = (serializedObject.targetObject as VFXPropertyBinder).gameObject; Undo.AddComponent(obj, t); } public void SelectElement(ReorderableList list) { UpdateSelection(list.index); } public void UpdateSelection(int selected) { if (selected >= 0) { Editor editor = null; CreateCachedEditor(m_Elements.GetArrayElementAtIndex(selected).objectReferenceValue, typeof(VFXBinderEditor), ref editor); m_ElementEditor = editor as VFXBinderEditor; } else m_ElementEditor = null; } public void AddElement(ReorderableList list) { m_Menu.ShowAsContext(); } public void DrawElement(Rect rect, int index, bool isActive, bool isFocused) { var target = m_Elements.GetArrayElementAtIndex(index).objectReferenceValue as VFXBinderBase; Rect iconRect = new Rect(rect.xMin + 4, rect.yMin + 4, 8, 8); if (target != null) { var element = target.ToString(); GUI.Label(rect, new GUIContent(element), Styles.labelStyle); var component = (m_Component.objectReferenceValue as VisualEffect); bool valid = target.IsValid(component); EditorGUI.DrawRect(iconRect, valid ? validColor : invalidColor); } else { EditorGUI.DrawRect(iconRect, errorColor); GUI.Label(rect, "(Missing or Null Property Binder)", Styles.labelStyle); } } public void RemoveElement(ReorderableList list) { int index = m_List.index; var element = m_Elements.GetArrayElementAtIndex(index).objectReferenceValue; if (element != null) { Undo.DestroyObjectImmediate(element); } else { Undo.RecordObject(serializedObject.targetObject, "Remove null entry"); } m_Elements.DeleteArrayElementAtIndex(index); // Remove list entry UpdateSelection(-1); } public void DrawHeader(Rect rect) { GUI.Label(rect, "Property Bindings"); } } }