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 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); }
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."); } }
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); }
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 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(); }
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)); } }
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 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)); } }
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 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) { 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; } }
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(); }