// This code is part of the Fungus library (http://fungusgames.com) maintained by Chris Gregan (http://twitter.com/gofungus). // It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE) using UnityEditor; using UnityEngine; using System; using System.Collections.Generic; using System.Linq; namespace Fungus.EditorUtils { [CustomEditor (typeof(Variable), true)] public class VariableEditor : CommandEditor { public override void OnEnable() { base.OnEnable(); Variable t = target as Variable; t.hideFlags = HideFlags.HideInInspector; } public static VariableInfoAttribute GetVariableInfo(System.Type variableType) { object[] attributes = variableType.GetCustomAttributes(typeof(VariableInfoAttribute), false); foreach (object obj in attributes) { VariableInfoAttribute variableInfoAttr = obj as VariableInfoAttribute; if (variableInfoAttr != null) { return variableInfoAttr; } } return null; } public static void VariableField(SerializedProperty property, GUIContent label, Flowchart flowchart, string defaultText, Func filter, Func drawer = null) { List variableKeys = new List(); List variableObjects = new List(); variableKeys.Add(defaultText); variableObjects.Add(null); List variables = flowchart.Variables; int index = 0; int selectedIndex = 0; Variable selectedVariable = property.objectReferenceValue as Variable; // When there are multiple Flowcharts in a scene with variables, switching // between the Flowcharts can cause the wrong variable property // to be inspected for a single frame. This has the effect of causing private // variable references to be set to null when inspected. When this condition // occurs we just skip displaying the property for this frame. if (selectedVariable != null && selectedVariable.gameObject != flowchart.gameObject && selectedVariable.Scope == VariableScope.Private) { property.objectReferenceValue = null; return; } foreach (Variable v in variables) { if (filter != null) { if (!filter(v)) { continue; } } variableKeys.Add(v.Key); variableObjects.Add(v); index++; if (v == selectedVariable) { selectedIndex = index; } } List fsList = Flowchart.CachedFlowcharts; foreach (Flowchart fs in fsList) { if (fs == flowchart) { continue; } List publicVars = fs.GetPublicVariables(); foreach (Variable v in publicVars) { if (filter != null) { if (!filter(v)) { continue; } } variableKeys.Add(fs.name + "/" + v.Key); variableObjects.Add(v); index++; if (v == selectedVariable) { selectedIndex = index; } } } if (drawer == null) { selectedIndex = EditorGUILayout.Popup(label.text, selectedIndex, variableKeys.ToArray()); } else { selectedIndex = drawer(label.text, selectedIndex, variableKeys.ToArray()); } property.objectReferenceValue = variableObjects[selectedIndex]; } } [CustomPropertyDrawer(typeof(VariablePropertyAttribute))] public class VariableDrawer : PropertyDrawer { public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { VariablePropertyAttribute variableProperty = attribute as VariablePropertyAttribute; if (variableProperty == null) { return; } EditorGUI.BeginProperty(position, label, property); // Filter the variables by the types listed in the VariableProperty attribute Func compare = v => { if (v == null) { return false; } if (variableProperty.VariableTypes.Length == 0) { return true; } return variableProperty.VariableTypes.Contains(v.GetType()); }; VariableEditor.VariableField(property, label, FlowchartWindow.GetFlowchart(), variableProperty.defaultText, compare, (s,t,u) => (EditorGUI.Popup(position, s, t, u))); EditorGUI.EndProperty(); } } public class VariableDataDrawer : PropertyDrawer where T : Variable { public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); // The variable reference and data properties must follow the naming convention 'typeRef', 'typeVal' VariableInfoAttribute typeInfo = VariableEditor.GetVariableInfo(typeof(T)); if (typeInfo == null) { return; } string propNameBase = typeInfo.VariableType; propNameBase = Char.ToLowerInvariant(propNameBase[0]) + propNameBase.Substring(1); SerializedProperty referenceProp = property.FindPropertyRelative(propNameBase + "Ref"); SerializedProperty valueProp = property.FindPropertyRelative(propNameBase + "Val"); if (referenceProp == null || valueProp == null) { return; } Command command = property.serializedObject.targetObject as Command; if (command == null) { return; } var flowchart = command.GetFlowchart() as Flowchart; if (flowchart == null) { return; } var origLabel = new GUIContent(label); if (EditorGUI.GetPropertyHeight(valueProp, label) > EditorGUIUtility.singleLineHeight) { DrawMultiLineProperty(position, origLabel, referenceProp, valueProp, flowchart); } else { DrawSingleLineProperty(position, origLabel, referenceProp, valueProp, flowchart); } EditorGUI.EndProperty(); } protected virtual void DrawSingleLineProperty(Rect rect, GUIContent label, SerializedProperty referenceProp, SerializedProperty valueProp, Flowchart flowchart) { const int popupWidth = 17; Rect controlRect = EditorGUI.PrefixLabel(rect, label); Rect valueRect = controlRect; valueRect.width = controlRect.width - popupWidth - 5; Rect popupRect = controlRect; if (referenceProp.objectReferenceValue == null) { EditorGUI.PropertyField(valueRect, valueProp, new GUIContent("")); popupRect.x += valueRect.width + 5; popupRect.width = popupWidth; } EditorGUI.PropertyField(popupRect, referenceProp, new GUIContent("")); } protected virtual void DrawMultiLineProperty(Rect rect, GUIContent label, SerializedProperty referenceProp, SerializedProperty valueProp, Flowchart flowchart) { const int popupWidth = 100; Rect controlRect = rect; Rect valueRect = controlRect; valueRect.width = controlRect.width - 5; Rect popupRect = controlRect; if (referenceProp.objectReferenceValue == null) { EditorGUI.PropertyField(valueRect, valueProp, label); popupRect.x = rect.width - popupWidth + 5; popupRect.width = popupWidth; } else { popupRect = EditorGUI.PrefixLabel(rect, label); } EditorGUI.PropertyField(popupRect, referenceProp, new GUIContent("")); } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { VariableInfoAttribute typeInfo = VariableEditor.GetVariableInfo(typeof(T)); if (typeInfo == null) { return EditorGUIUtility.singleLineHeight; } string propNameBase = typeInfo.VariableType; propNameBase = Char.ToLowerInvariant(propNameBase[0]) + propNameBase.Substring(1); SerializedProperty referenceProp = property.FindPropertyRelative(propNameBase + "Ref"); if (referenceProp.objectReferenceValue != null) { return EditorGUIUtility.singleLineHeight; } SerializedProperty valueProp = property.FindPropertyRelative(propNameBase + "Val"); return EditorGUI.GetPropertyHeight(valueProp, label); } } [CustomPropertyDrawer (typeof(BooleanData))] public class BooleanDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(IntegerData))] public class IntegerDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(FloatData))] public class FloatDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(StringData))] public class StringDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(StringDataMulti))] public class StringDataMultiDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(ColorData))] public class ColorDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(Vector2Data))] public class Vector2DataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(Vector3Data))] public class Vector3DataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(MaterialData))] public class MaterialDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(TextureData))] public class TextureDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(SpriteData))] public class SpriteDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(GameObjectData))] public class GameObjectDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(ObjectData))] public class ObjectDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(AnimatorData))] public class AnimatorDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(TransformData))] public class TransformDataDrawer : VariableDataDrawer {} [CustomPropertyDrawer (typeof(AudioSourceData))] public class AudioSourceDrawer : VariableDataDrawer { } [CustomPropertyDrawer(typeof(Rigidbody2DData))] public class Rigidbody2DDataDrawer : VariableDataDrawer { } }