public override void OnButtonClick() { var attribute = Attribute; var method = attribute.InstanceType.GetMethod(attribute.MethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { foreach (var target in Selection.gameObjects) { if (target == null) { continue; } var targetComponent = target.GetComponent(attribute.InstanceType); if (targetComponent == null) { ToolboxEditorLog.AttributeUsageWarning(attribute, attribute.InstanceType + " component not found in selected GameObject(" + target.name + ")."); continue; } method.Invoke(targetComponent, null); } } else { ToolboxEditorLog.AttributeUsageWarning(attribute, attribute.MethodName + " method not found inside " + attribute.InstanceType + " type."); } }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { label = EditorGUI.BeginProperty(position, label, property); label = property.name != "data" ? label : GUIContent.none; position = EditorGUI.PrefixLabel(position, label); var validAttribute = GetVerifiedAttribute(attribute); var addSearchField = validAttribute.AddTextSearchField; UpdateConstraint(validAttribute); UpdateAppearance(validAttribute); var referenceProperty = property.FindPropertyRelative("typeReference"); var activeType = SerializedType.GetReferenceType(referenceProperty.stringValue); typeField.OnGui(position, addSearchField, (type) => { try { referenceProperty.serializedObject.Update(); referenceProperty.stringValue = SerializedType.GetReferenceValue(type); referenceProperty.serializedObject.ApplyModifiedProperties(); } catch (Exception e) when(e is ArgumentNullException || e is NullReferenceException) { ToolboxEditorLog.LogWarning("Invalid attempt to update disposed property."); } }, activeType); EditorGUI.EndProperty(); }
protected bool IsConditionMet(SerializedProperty property) { var propertyName = Attribute.PropertyToCheck; var propertyValue = Attribute.CompareValue; var conditionProperty = property.GetSibiling(propertyName); if (conditionProperty != null) { if (conditionProperty.propertyType == SerializedPropertyType.Boolean) { var compareValue = propertyValue != null && propertyValue is bool?(bool)propertyValue : true; return(conditionProperty.boolValue == compareValue); } else { ToolboxEditorLog.AttributeUsageWarning(attribute, property, propertyName + " has to be a boolean value property."); } } else { ToolboxEditorLog.AttributeUsageWarning(attribute, property, propertyName + " does not exists."); } return(true); }
private static MethodInfo FindMethod(SerializedObject target, string methodName, Type expectedReturnType = null) { if (string.IsNullOrEmpty(methodName)) { return(null); } var methodInfo = ReflectionUtility.GetObjectMethod(methodName, target); if (methodInfo == null) { ToolboxEditorLog.AttributeUsageWarning(typeof(ReorderableListExposedAttribute), string.Format("{0} method not found.", methodName)); return(null); } var parameters = methodInfo.GetParameters(); if (parameters.Length > 0) { ToolboxEditorLog.AttributeUsageWarning(typeof(ReorderableListExposedAttribute), string.Format("{0} method not found.", methodName)); return(null); } if (expectedReturnType != null && expectedReturnType != methodInfo.ReturnType) { ToolboxEditorLog.AttributeUsageWarning(typeof(ReorderableListExposedAttribute), string.Format("{0} method returns invalid type. Expected - {1}.", methodName, expectedReturnType)); return(null); } return(methodInfo); }
private void CreateTypeProperty(SerializedProperty property) { property.GetFieldInfo(out Type propertyType); TypeUtilities.TryGetTypeFromManagedReferenceFullTypeName(property.managedReferenceFullTypename, out var currentType); var position = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight); position = EditorGUI.IndentedRect(position); typeField.OnGui(position, true, (type) => { try { if (!property.serializedObject.isEditingMultipleObjects) { UpdateTypeProperty(property, type); } else { var targets = property.serializedObject.targetObjects; foreach (var target in targets) { using (var so = new SerializedObject(target)) { SerializedProperty sp = so.FindProperty(property.propertyPath); UpdateTypeProperty(sp, type); } } } } catch (Exception e) when(e is ArgumentNullException || e is NullReferenceException) { ToolboxEditorLog.LogWarning("Invalid attempt to update disposed property."); } }, currentType, propertyType); }
protected override void OnGuiSafe(SerializedProperty property, GUIContent label, T attribute) { var minValueSource = attribute.MinValueSource; var maxValueSource = attribute.MaxValueSource; if (!ValueExtractionHelper.TryGetValue(minValueSource, property, out var minValueCandidate, out _) || !ValueExtractionHelper.TryGetValue(maxValueSource, property, out var maxValueCandidate, out _)) { ToolboxEditorLog.MemberNotFoundWarning(attribute, property, string.Format("{0} or {1}", minValueSource, maxValueSource)); base.OnGuiSafe(property, label, attribute); return; } float minValue; float maxValue; try { minValue = Convert.ToSingle(minValueCandidate); maxValue = Convert.ToSingle(maxValueCandidate); } catch (Exception e) when(e is InvalidCastException || e is FormatException) { ToolboxEditorLog.AttributeUsageWarning(attribute, property, string.Format("Invalid source types, cannot convert them to {0}", typeof(float))); base.OnGuiSafe(property, label, attribute); return; } OnGuiSafe(property, label, minValue, maxValue); }
protected override void OnGuiBeginSafe(ImageAreaAttribute attribute) { var url = attribute.Url; if (!textures.TryGetValue(url, out var texture)) { textures[url] = texture = new DownloadedTexture(true); EditorCoroutineUtility.StartCoroutineOwnerless(SendGetImageRequest(url, (b, t) => { textures[url] = new DownloadedTexture(false, t); if (b) { InspectorUtility.RepaintInspectors(); } else { ToolboxEditorLog.AttributeUsageWarning(attribute, "Cannot retrive image from the provided URL - " + url); } })); } if (texture.Texture2D != null) { EditorGUILayout.LabelField(new GUIContent(texture.Texture2D), Style.imageStyle, GUILayout.Height(attribute.Height)); } }
protected override PropertyCondition OnGuiValidateSafe(SerializedProperty property, T attribute) { var propertyToCheck = property.GetSibiling(attribute.ComparedPropertyName); if (propertyToCheck == null) { ToolboxEditorLog.PropertyNotFoundWarning(property, attribute.ComparedPropertyName); return(PropertyCondition.Valid); } //TODO: validate 'propertyToCheck' type with 'attribute.ComparedConditionValue' switch (propertyToCheck.propertyType) { case SerializedPropertyType.Boolean: return(OnComparisonResult(propertyToCheck.boolValue.Equals(attribute.ComparedConditionValue))); case SerializedPropertyType.String: return(OnComparisonResult(propertyToCheck.stringValue.Equals(attribute.ComparedConditionValue))); case SerializedPropertyType.Integer: return(OnComparisonResult(propertyToCheck.intValue.Equals(attribute.ComparedConditionValue))); case SerializedPropertyType.Float: return(OnComparisonResult(propertyToCheck.floatValue.Equals(attribute.ComparedConditionValue))); case SerializedPropertyType.Enum: return(OnComparisonResult(propertyToCheck.intValue.Equals((int)attribute.ComparedConditionValue))); default: ToolboxEditorLog.TypeNotSupportedWarning(property, propertyToCheck.type); return(PropertyCondition.Valid); } }
protected override void OnGuiCloseSafe(EditorButtonAttribute attribute) { var targetObjects = InspectorUtility.CurrentTargetObjects; if (targetObjects == null || targetObjects.Length == 0) { //NOTE: something went really wrong, internal bug or OnGuiBeginSafe was called out of the Toolbox scope return; } var disable = !IsClickable(attribute.ActivityType); using (new EditorGUI.DisabledScope(disable)) { var label = string.IsNullOrEmpty(attribute.ExtraLabel) ? attribute.MethodName : attribute.ExtraLabel; var tooltip = attribute.Tooltip; var content = new GUIContent(label, tooltip); if (GUILayout.Button(content, Style.buttonStyle)) { var targetType = targetObjects[0].GetType(); var method = targetType.GetMethod(attribute.MethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); //validate method name (check if method exists) if (method == null) { ToolboxEditorLog.AttributeUsageWarning(attribute, attribute.MethodName + " method not found."); return; } //validate parameters count and log warning var parameters = method.GetParameters(); if (parameters.Length > 0) { ToolboxEditorLog.AttributeUsageWarning(attribute, attribute.MethodName + " method has to be parameterless."); return; } //invoke method for all selected components var isCoroutine = IsCoroutine(targetType, method); for (var i = 0; i < targetObjects.Length; i++) { var target = targetObjects[i]; if (target == null) { continue; } var result = method.Invoke(target, null); //additionaly run Coroutine if possible if (isCoroutine) { EditorCoroutineUtility.StartCoroutineOwnerless((IEnumerator)result); } } } } }
protected override void OnGuiBeginSafe(ToolboxButtonAttribute attribute) { var targetObjects = InspectorUtility.CurrentTargetObjects; if (targetObjects == null || targetObjects.Length == 0) { return; } var isValid = true; var label = string.IsNullOrEmpty(attribute.Label) ? attribute.MethodName : attribute.Label; switch (attribute.Type) { case ButtonActivityType.Everything: isValid = true; break; case ButtonActivityType.Nothing: isValid = false; break; case ButtonActivityType.OnEditMode: isValid = !Application.isPlaying; break; case ButtonActivityType.OnPlayMode: isValid = Application.isPlaying; break; } EditorGUI.BeginDisabledGroup(!isValid); if (GUILayout.Button(label)) { var targetType = targetObjects[0].GetType(); var method = targetType.GetMethod(attribute.MethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (method != null) { for (var i = 0; i < targetObjects.Length; i++) { var target = targetObjects[i]; if (target == null) { continue; } method.Invoke(target, null); } } else { ToolboxEditorLog.AttributeUsageWarning(attribute, attribute.MethodName + " method not found inside " + targetType + " type."); } } EditorGUI.EndDisabledGroup(); }
/// <summary> /// Native call to draw the provided property. /// </summary> /// <param name="position"></param> /// <param name="property"></param> /// <param name="label"></param> public override sealed void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (IsPropertyValid(property)) { OnGUISafe(position, property, label); } else { var warningContent = new GUIContent(property.displayName + " has invalid property drawer"); ToolboxEditorLog.WrongAttributeUsageWarning(attribute, property); ToolboxEditorGui.DrawEmptyProperty(position, property, warningContent); } }
private static void ConnectCallbacks(ReorderableListBase list, ReorderableListExposedAttribute attribute) { var listTarget = list.SerializedObject; var fieldInfo = list.List.GetFieldInfo(); var returnType = fieldInfo.FieldType.GetEnumeratedType(); var methodName = attribute.OverrideNewElementMethodName; var methodInfo = RetriveValidMethod(listTarget, methodName, returnType); if (methodInfo == null) { return; } list.overrideNewElementCallback = (index) => { return(methodInfo.Invoke(listTarget.targetObject, null)); }; //TODO: add more useful callbacks to expose MethodInfo RetriveValidMethod(SerializedObject target, string methodName, Type expectedReturnType = null) { if (string.IsNullOrEmpty(methodName)) { return(null); } var methodInfo = ReflectionUtility.GetObjectMethod(methodName, target); if (methodInfo == null) { ToolboxEditorLog.AttributeUsageWarning(attribute, string.Format("{0} method not found.", methodName)); return(null); } var parameters = methodInfo.GetParameters(); if (parameters.Length > 0) { ToolboxEditorLog.AttributeUsageWarning(attribute, string.Format("{0} method not found.", methodName)); return(null); } if (expectedReturnType != null && expectedReturnType != methodInfo.ReturnType) { ToolboxEditorLog.AttributeUsageWarning(attribute, string.Format("{0} method returns invalid type. Expected - {1}.", methodName, expectedReturnType)); return(null); } return(methodInfo); } }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(position, property, label, property.isExpanded); if (EditorGUI.EndChangeCheck()) { var methodName = Attribute.CallbackMethodName; if (ReflectionUtility.TryInvokeMethod(methodName, property.serializedObject)) { return; } ToolboxEditorLog.AttributeUsageWarning(attribute, string.Format("{0} method is invalid.", methodName)); } }
/// <summary> /// Native call to draw the provided property. /// </summary> public override sealed void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (IsPropertyValid(property)) { OnGUISafe(position, property, label); return; } var warningContent = new GUIContent(property.displayName + " has invalid property drawer"); //create additional warning log to the console window ToolboxEditorLog.WrongAttributeUsageWarning(attribute, property); //create additional warning label based on the property name ToolboxEditorGui.DrawEmptyProperty(position, property, warningContent); }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(position, property, label); if (EditorGUI.EndChangeCheck()) { var objectValue = property.objectReferenceValue; if (objectValue == null || IsObjectValid(objectValue, property)) { return; } property.objectReferenceValue = null; ToolboxEditorLog.AttributeUsageWarning(attribute, property, GetWarningMessage()); } }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(position, property, label); if (!EditorGUI.EndChangeCheck() || property.objectReferenceValue == null) { return; } if (PrefabUtility.GetPrefabAssetType(property.objectReferenceValue) == PrefabAssetType.NotAPrefab) { ToolboxEditorLog.AttributeUsageWarning(attribute, property, "Assigned object has to be a prefab."); property.objectReferenceValue = null; } }
protected override PropertyCondition OnGuiValidateSafe(SerializedProperty property, T attribute) { var propertyToCheck = property.GetSibiling(attribute.PropertyName); if (propertyToCheck == null) { ToolboxEditorLog.PropertyNotFoundWarning(property, attribute.PropertyName); return(PropertyCondition.Valid); } //TODO: validate 'propertyToCheck' type with 'attribute.ValueToMatch' var result = true; switch (propertyToCheck.propertyType) { case SerializedPropertyType.Integer: result = ComparisionHelper.CheckInteger(propertyToCheck, attribute.ValueToMatch, attribute.TestMethod); break; case SerializedPropertyType.Boolean: result = ComparisionHelper.CheckBoolean(propertyToCheck, attribute.ValueToMatch, attribute.TestMethod); break; case SerializedPropertyType.Float: result = ComparisionHelper.CheckFloat(propertyToCheck, attribute.ValueToMatch, attribute.TestMethod); break; case SerializedPropertyType.String: result = ComparisionHelper.CheckString(propertyToCheck, attribute.ValueToMatch, attribute.TestMethod); break; case SerializedPropertyType.ObjectReference: result = ComparisionHelper.CheckObject(propertyToCheck, attribute.ValueToMatch, attribute.TestMethod); break; case SerializedPropertyType.Enum: result = ComparisionHelper.CheckEnum(propertyToCheck, attribute.ValueToMatch, attribute.TestMethod); break; default: ToolboxEditorLog.TypeNotSupportedWarning(property, propertyToCheck.type); break; } return(OnComparisonResult(result)); }
protected override void OnGuiBeginSafe(DynamicHelpAttribute attribute) { var sourceHandle = attribute.SourceHandle; var targetObjects = InspectorUtility.CurrentTargetObjects; if (ValueExtractionHelper.TryGetValue(sourceHandle, targetObjects, out var value, out var hasMixedValues)) { var messageText = hasMixedValues ? "-" : value?.ToString(); var messageType = (MessageType)attribute.Type; EditorGUILayout.HelpBox(messageText, messageType); return; } var targetType = targetObjects[0].GetType(); ToolboxEditorLog.MemberNotFoundWarning(attribute, targetType, sourceHandle); }
public void OnGui(SerializedProperty property, GUIContent label, T attribute) { if (attribute == null) { return; } if (IsPropertyValid(property)) { OnGuiSafe(property, label, attribute); } else { var warningContent = new GUIContent(property.displayName + " has invalid property drawer"); ToolboxEditorLog.WrongAttributeUsageWarning(attribute, property); ToolboxEditorGui.DrawLayoutEmptyProperty(property, warningContent); } }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { var propertyName = Attribute.ChildName; var childProperty = property.FindPropertyRelative(propertyName); //validate availability of the child property if (childProperty != null) { //set new label if found (unknown types will be ignored) label = GetLabelByValue(childProperty, label); } else { ToolboxEditorLog.AttributeUsageWarning(attribute, property, string.Format("{0} does not exists.", propertyName)); } EditorGUI.PropertyField(position, property, label, property.isExpanded); }
private static GUIContent GetContent(LabelAttribute attribute) { if (attribute.Content != null) { var content = EditorGUIUtility.TrIconContent(attribute.Content); if (content.image == null) { ToolboxEditorLog.AttributeUsageWarning(attribute, "Cannot find icon '" + attribute.Content + "'."); } content.text = attribute.Label; content.tooltip = string.Empty; return(content); } else { return(new GUIContent(attribute.Label)); } }
public void OnGui(SerializedProperty property, GUIContent label, T attribute) { if (attribute == null) { return; } if (IsPropertyValid(property)) { OnGuiSafe(property, label, attribute); } else { var warningContent = new GUIContent(property.displayName + " has invalid property drawer"); //create additional warning log to the console window ToolboxEditorLog.WrongAttributeUsageWarning(attribute, property); //create additional warning label based on the property name ToolboxEditorGui.DrawEmptyProperty(property, warningContent); } }
private static GUIContent GetContent(LabelAttribute attribute) { if (attribute.Asset != null) { //try to find associated image content var content = EditorGUIUtility.TrIconContent(attribute.Asset); if (content.image == null) { ToolboxEditorLog.AttributeUsageWarning(attribute, string.Format("Cannot find icon asset '{0}'.", attribute.Asset)); } content.text = attribute.Label; content.tooltip = string.Empty; return(content); } else { return(new GUIContent(attribute.Label)); } }
private void CallMethods(EditorButtonAttribute attribute, Object[] targetObjects) { var methodInfo = ReflectionUtility.GetObjectMethod(attribute.MethodName, targetObjects); //validate method name (check if method exists) if (methodInfo == null) { ToolboxEditorLog.AttributeUsageWarning(attribute, string.Format("{0} method not found.", attribute.MethodName)); return; } //validate parameters count and log warning var parameters = methodInfo.GetParameters(); if (parameters.Length > 0) { ToolboxEditorLog.AttributeUsageWarning(attribute, string.Format("{0} method has to be parameterless.", attribute.MethodName)); return; } //invoke method for all selected components var isCoroutine = IsCoroutine(methodInfo); for (var i = 0; i < targetObjects.Length; i++) { var target = targetObjects[i]; if (target == null) { continue; } var result = methodInfo.Invoke(target, null); //additionaly run Coroutine if possible if (isCoroutine) { EditorCoroutineUtility.StartCoroutineOwnerless((IEnumerator)result); } } }
protected override PropertyCondition OnGuiValidateSafe(SerializedProperty property, T attribute) { var sourceHandle = attribute.SourceHandle; if (!ValueExtractionHelper.TryGetValue(sourceHandle, property, out var value, out var hasMixedValues)) { ToolboxEditorLog.MemberNotFoundWarning(attribute, property, sourceHandle); return(PropertyCondition.Valid); } var comparison = (ValueComparisonMethod)attribute.Comparison; var targetValue = attribute.ValueToMatch; if (!ValueComparisonHelper.TryCompare(value, targetValue, comparison, out var result)) { ToolboxEditorLog.AttributeUsageWarning(attribute, property, string.Format("Invalid comparison input: source:{0}, target:{1}, method:{2}.", value?.GetType(), targetValue?.GetType(), comparison)); return(PropertyCondition.Valid); } return(OnComparisonResult(hasMixedValues ? false : result)); }
protected bool IsConditionMet(SerializedProperty property, T attribute) { if (attribute == null) { return(true); } var propertyToCheck = property.serializedObject.FindProperty(attribute.ComparedPropertyName); if (propertyToCheck == null) { ToolboxEditorLog.PropertyNotFoundWarning(property, attribute.ComparedPropertyName); return(true); } switch (propertyToCheck.propertyType) { case SerializedPropertyType.Boolean: return(propertyToCheck.boolValue.Equals(attribute.ComparedConditionValue)); case SerializedPropertyType.String: return(propertyToCheck.stringValue.Equals(attribute.ComparedConditionValue)); case SerializedPropertyType.Integer: return(propertyToCheck.intValue.Equals(attribute.ComparedConditionValue)); case SerializedPropertyType.Float: return(propertyToCheck.floatValue.Equals(attribute.ComparedConditionValue)); case SerializedPropertyType.Enum: return(propertyToCheck.intValue.Equals((int)attribute.ComparedConditionValue)); default: ToolboxEditorLog.TypeNotSupportedWarning(property, propertyToCheck.type); return(true); } }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { var key = property.GetPropertyHashKey(); ApplyControlName(key); EditorGUI.PropertyField(position, property, label); if (IsControlEditing(key)) { position.width = 0; position.height = 0; } else { #if UNITY_2019_2_OR_NEWER position.xMin += EditorGUIUtility.labelWidth + EditorGUIUtility.standardVerticalSpacing; #else position.xMin += EditorGUIUtility.labelWidth; #endif } var targetAttribute = attribute as FormattedNumberAttribute; var single = GetSingle(property); var format = GetFormat(property, targetAttribute); try { using (new ZeroIndentScope()) { EditorGUI.TextField(position, single.ToString(format, formatInfo)); } } catch (FormatException) { ToolboxEditorLog.AttributeUsageWarning(attribute, property, string.Format("{0} format is not supported.", format)); } }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { //NOTE: this implementation does not support multiple different sources var sourceHandle = Attribute.SourceHandle; var declaringObject = property.GetDeclaringObject(); //extract (if available) the real preset value if (!ValueExtractionHelper.TryGetValue(sourceHandle, declaringObject, out var sourceValue)) { ToolboxEditorLog.MemberNotFoundWarning(attribute, property, sourceHandle); EditorGUI.PropertyField(position, property, label); return; } if (!(sourceValue is IList presetList)) { ToolboxEditorLog.AttributeUsageWarning(attribute, property, string.Format("Preset ({0}) has to be a one-dimensional collection (array or list).", sourceHandle)); EditorGUI.PropertyField(position, property, label); return; } var sourceType = sourceValue.GetType(); var targetType = property.GetProperType(fieldInfo); //check if types match between property and provided preset if (targetType != (sourceType.IsGenericType ? sourceType.GetGenericArguments()[0] : sourceType.GetElementType())) { ToolboxEditorLog.AttributeUsageWarning(attribute, property, "Type mismatch between serialized property and given Preset."); EditorGUI.PropertyField(position, property, label); return; } var itemsCount = presetList.Count; var objects = new object[itemsCount]; var options = new string[itemsCount]; for (var i = 0; i < itemsCount; i++) { objects[i] = presetList[i]; options[i] = presetList[i]?.ToString(); } var value = property.GetProperValue(fieldInfo, declaringObject); var index = Array.IndexOf(objects, value); //begin the true property label = EditorGUI.BeginProperty(position, label, property); EditorGUI.BeginChangeCheck(); //get selected preset value index = EditorGUI.Popup(position, label, index, EditorGUIUtility.TrTempContent(options)); index = Mathf.Clamp(index, 0, itemsCount - 1); if (EditorGUI.EndChangeCheck()) { //udpate property value using previously cached FieldInfo and picked value //there is no cleaner way to do it, since we don't really know what kind of //serialized property we are updating property.serializedObject.Update(); property.SetProperValue(fieldInfo, objects[index]); property.serializedObject.ApplyModifiedProperties(); //handle situation when updating multiple different targets property.serializedObject.SetIsDifferentCacheDirty(); } EditorGUI.EndProperty(); }
private static void LogMethodNotSupported(ComparisionTestMethod testMethod, object valueToMatch) { ToolboxEditorLog.LogWarning(string.Format("{0} comparision method is not supported for type: {1}.", testMethod, valueToMatch.GetType())); }
protected override void OnGUISafe(Rect position, SerializedProperty property, GUIContent label) { var targetObject = property.GetDeclaringObject(); var presetValues = targetObject.GetType().GetField(Attribute.PresetFieldName, presetBinding); if (presetValues == null) { ToolboxEditorLog.AttributeUsageWarning(attribute, property, "Cannot find relative preset field (" + Attribute.PresetFieldName + ")."); EditorGUI.PropertyField(position, property, label); return; } var presetObject = presetValues.GetValue(targetObject); if (presetObject is IList list) { var propertyType = property.GetProperType(fieldInfo, targetObject); //check if types match between property and provided preset if (propertyType == (presetValues.FieldType.IsGenericType ? presetValues.FieldType.GetGenericArguments()[0] : presetValues.FieldType.GetElementType())) { var objects = new object[list.Count]; var options = new string[list.Count]; for (var i = 0; i < list.Count; i++) { objects[i] = list[i]; options[i] = list[i]?.ToString(); } var index = Array.IndexOf(objects, property.GetProperValue(fieldInfo, targetObject)); //begin the true property label = EditorGUI.BeginProperty(position, label, property); //draw the prefix label position = EditorGUI.PrefixLabel(position, label); EditorGUI.BeginChangeCheck(); //get selected preset value index = EditorGUI.Popup(position, index, options); //validate index before set index = Mathf.Clamp(index, 0, list.Count - 1); if (EditorGUI.EndChangeCheck()) { //udpate property value using previously cached FieldInfo and picked value //there is no cleaner way to do it, since we don't really know what kind of //serialized property we are updating property.serializedObject.Update(); property.SetProperValue(fieldInfo, objects[index]); property.serializedObject.ApplyModifiedProperties(); //handle situation when updating multiple different targets property.serializedObject.SetIsDifferentCacheDirty(); } EditorGUI.EndProperty(); } else { ToolboxEditorLog.AttributeUsageWarning(attribute, property, "Type mismatch between serialized property and provided preset field."); EditorGUI.PropertyField(position, property, label); return; } } else { ToolboxEditorLog.AttributeUsageWarning(attribute, property, "Preset field (" + Attribute.PresetFieldName + ") has to be a one-dimensional collection (array or list)."); EditorGUI.PropertyField(position, property, label); return; } }