1285 lines
50 KiB
C#
1285 lines
50 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using UnityEditor.Search;
|
|
using System.Collections;
|
|
using UnityEngine.UIElements;
|
|
using UnityEditor.UIElements;
|
|
using System.Collections.Generic;
|
|
using EditorAttributes.Editor.Utility;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace EditorAttributes.Editor
|
|
{
|
|
public class PropertyDrawerBase : PropertyDrawer
|
|
{
|
|
internal const string GROUPED_PROPERTY_ID = "GroupedProperty";
|
|
|
|
protected bool CanApplyGlobalColor => EditorExtension.GLOBAL_COLOR != EditorExtension.DEFAULT_GLOBAL_COLOR;
|
|
|
|
public override VisualElement CreatePropertyGUI(SerializedProperty property) => CreatePropertyField(property);
|
|
|
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
|
{
|
|
EditorGUI.PropertyField(position, property, label, true);
|
|
|
|
var helpBoxStyle = GUI.skin.GetStyle("HelpBox");
|
|
helpBoxStyle.richText = true;
|
|
|
|
EditorGUILayout.HelpBox("You cannot use <b>EditorAttributes</b> with <b>ImGUI</b> based editors. " +
|
|
"Convert your editor to <b>UI Toolkit</b> for attributes to work, or remove the attributes from properties drawn by the editor script.", MessageType.Warning);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this function to customize the copied value from an element with using <see cref="AddPropertyContextMenu(VisualElement, SerializedProperty)"/>
|
|
/// </summary>
|
|
/// <param name="element">The element on which the context menu was added</param>
|
|
/// <param name="property">The attached serialized property</param>
|
|
/// <returns>The string that will be copied into the clipboard</returns>
|
|
protected virtual string CopyValue(VisualElement element, SerializedProperty property) => GetCopyPropertyValue(property);
|
|
|
|
/// <summary>
|
|
/// Override this function to customize the paste behaivour for an element with using <see cref="AddPropertyContextMenu(VisualElement, SerializedProperty)"/>
|
|
/// </summary>
|
|
/// <param name="element">The element on which the context menu was added</param>
|
|
/// <param name="property">The attached serialized property</param>
|
|
/// <param name="clipboardValue">The current clipboard value</param>
|
|
protected virtual void PasteValue(VisualElement element, SerializedProperty property, string clipboardValue) => SetPropertyValueFromString(clipboardValue, property);
|
|
|
|
/// <summary>
|
|
/// Creates a properly binded property field from a serialized property
|
|
/// </summary>
|
|
/// <param name="property">The serialized property</param>
|
|
/// <returns>The binded property field</returns>
|
|
public static PropertyField CreatePropertyField(SerializedProperty property, string label = "")
|
|
{
|
|
var propertyField = new PropertyField(property, string.IsNullOrWhiteSpace(label) ? property.displayName : label);
|
|
propertyField.BindProperty(property.serializedObject);
|
|
|
|
return propertyField;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the value of a property from a string
|
|
/// </summary>
|
|
/// <param name="value">The string value to convert</param>
|
|
/// <param name="property">The serialized property to assign the value to</param>
|
|
protected void SetPropertyValueFromString(string value, SerializedProperty property)
|
|
{
|
|
try
|
|
{
|
|
switch (property.propertyType)
|
|
{
|
|
case SerializedPropertyType.Integer:
|
|
property.intValue = Convert.ToInt32(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.Float:
|
|
property.floatValue = Convert.ToSingle(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.Boolean:
|
|
property.boolValue = Convert.ToBoolean(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.String:
|
|
property.stringValue = value;
|
|
break;
|
|
|
|
case SerializedPropertyType.Character:
|
|
property.intValue = Convert.ToChar(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.Color:
|
|
property.colorValue = ColorUtility.TryParseHtmlString(value, out Color color) ? color : Color.white;
|
|
break;
|
|
|
|
case SerializedPropertyType.Vector2:
|
|
property.vector2Value = VectorUtils.ParseVector2(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.Vector3:
|
|
property.vector3Value = VectorUtils.ParseVector3(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.Vector4:
|
|
property.vector4Value = VectorUtils.ParseVector4(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.Vector2Int:
|
|
property.vector2IntValue = VectorUtils.ParseVector2Int(value);
|
|
break;
|
|
|
|
case SerializedPropertyType.Vector3Int:
|
|
property.vector3IntValue = VectorUtils.ParseVector3Int(value);
|
|
break;
|
|
|
|
default:
|
|
Debug.LogWarning($"The type <b>{property.propertyType}</b> is not supported", property.serializedObject.targetObject);
|
|
break;
|
|
}
|
|
|
|
property.serializedObject.ApplyModifiedProperties();
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
Debug.LogError($"Could not convert the value \"{value}\" to <b>{property.propertyType}</b>", property.serializedObject.targetObject);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of a serialzied property and returns it as a string
|
|
/// </summary>
|
|
/// <param name="property">The serialized property to get the value from</param>
|
|
/// <returns>The serialized property value as a string</returns>
|
|
protected string GetPropertyValueAsString(SerializedProperty property) => property.propertyType switch
|
|
{
|
|
SerializedPropertyType.String => property.stringValue,
|
|
SerializedPropertyType.Integer or SerializedPropertyType.LayerMask or SerializedPropertyType.Character => property.intValue.ToString(),
|
|
SerializedPropertyType.Enum => IsPropertyEnumFlag() ? property.enumValueFlag.ToString() : property.enumDisplayNames[property.enumValueIndex],
|
|
SerializedPropertyType.Float => property.floatValue.ToString(),
|
|
SerializedPropertyType.Boolean => property.boolValue.ToString(),
|
|
SerializedPropertyType.Vector2 => property.vector2Value.ToString(),
|
|
SerializedPropertyType.Vector3 => property.vector3Value.ToString(),
|
|
SerializedPropertyType.Vector4 => property.vector4Value.ToString(),
|
|
SerializedPropertyType.Rect => property.vector4Value.ToString(),
|
|
SerializedPropertyType.Bounds => property.boundsValue.ToString(),
|
|
SerializedPropertyType.Color => property.colorValue.ToString(),
|
|
SerializedPropertyType.Gradient => property.gradientValue.ToString(),
|
|
SerializedPropertyType.AnimationCurve => property.animationCurveValue.ToString(),
|
|
SerializedPropertyType.Quaternion => property.quaternionValue.ToString(),
|
|
SerializedPropertyType.Vector2Int => property.vector2IntValue.ToString(),
|
|
SerializedPropertyType.Vector3Int => property.vector3IntValue.ToString(),
|
|
SerializedPropertyType.RectInt => property.rectIntValue.ToString(),
|
|
SerializedPropertyType.BoundsInt => property.boundsIntValue.ToString(),
|
|
SerializedPropertyType.Hash128 => property.hash128Value.ToString(),
|
|
SerializedPropertyType.ArraySize => property.arraySize.ToString(),
|
|
SerializedPropertyType.FixedBufferSize => property.fixedBufferSize.ToString(),
|
|
SerializedPropertyType.ObjectReference => property.objectReferenceValue.ToString(),
|
|
SerializedPropertyType.ExposedReference => property.exposedReferenceValue.ToString(),
|
|
SerializedPropertyType.ManagedReference => property.managedReferenceValue.ToString(),
|
|
_ => string.Empty
|
|
};
|
|
|
|
/// <summary>
|
|
/// Converts the values of a collection into strings
|
|
/// </summary>
|
|
/// <param name="collectionName">The name of the collection to convert</param>
|
|
/// <param name="serializedProperty">The serialized property</param>
|
|
/// <param name="memberInfo">The member info of the collection</param>
|
|
/// <param name="errorBox">The error box to display any errors to</param>
|
|
/// <returns>The values of the collection in a list of strings</returns>
|
|
protected static List<string> ConvertCollectionValuesToStrings(string collectionName, SerializedProperty serializedProperty, MemberInfo memberInfo, HelpBox errorBox)
|
|
{
|
|
var stringList = new List<string>();
|
|
var memberInfoValue = ReflectionUtility.GetMemberInfoValue(memberInfo, serializedProperty);
|
|
|
|
if (memberInfoValue is Array array)
|
|
{
|
|
foreach (var item in array)
|
|
stringList.Add(item == null ? "NULL" : item.ToString());
|
|
}
|
|
else if (memberInfoValue is IList list)
|
|
{
|
|
foreach (var item in list)
|
|
stringList.Add(item == null ? "NULL" : item.ToString());
|
|
}
|
|
else if (memberInfoValue is IDictionary dictionary)
|
|
{
|
|
foreach (DictionaryEntry item in dictionary)
|
|
stringList.Add(item.Value == null ? "NULL" : item.Value.ToString());
|
|
}
|
|
else
|
|
{
|
|
errorBox.text = $"Could not find the collection <b>{collectionName}</b>";
|
|
}
|
|
|
|
return stringList;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a nested serialized property
|
|
/// </summary>
|
|
/// <param name="property">The serialized property</param>
|
|
/// <param name="propertyName">The name of the property to find</param>
|
|
/// <returns>The nested serialized property</returns>
|
|
protected static SerializedProperty FindNestedProperty(SerializedProperty property, string propertyName)
|
|
{
|
|
var propertyPath = property.propertyPath;
|
|
var cutPathIndex = propertyPath.LastIndexOf('.');
|
|
|
|
if (cutPathIndex == -1)
|
|
{
|
|
return property.serializedObject.FindProperty(propertyName);
|
|
}
|
|
else
|
|
{
|
|
propertyPath = propertyPath[..cutPathIndex];
|
|
|
|
return property.serializedObject.FindProperty(propertyPath).FindPropertyRelative(propertyName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks to see if a seralized property is a list or array
|
|
/// </summary>
|
|
/// <param name="property">The serialized property to check</param>
|
|
/// <returns>True if the property is a list or array, false otherwise</returns>
|
|
public static bool IsPropertyCollection(SerializedProperty property) => property.propertyPath.Contains("Array");
|
|
|
|
/// <summary>
|
|
/// Gets the collection property from a collection item property
|
|
/// </summary>
|
|
/// <param name="property">The collection item property</param>
|
|
/// <returns>The collection property</returns>
|
|
public static SerializedProperty GetCollectionProperty(SerializedProperty property)
|
|
{
|
|
string path = property.propertyPath;
|
|
|
|
int index = path.LastIndexOf(".Array.data[");
|
|
|
|
if (index >= 0)
|
|
{
|
|
string collectionPath = path[..index];
|
|
return property.serializedObject.FindProperty(collectionPath);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name of a serialized property accounting for C# properties
|
|
/// </summary>
|
|
/// <param name="propertyName">The name of the property to look for</param>
|
|
/// <param name="property">The serialized property</param>
|
|
/// <returns>The name of the serialized property</returns>
|
|
public static string GetSerializedPropertyName(string propertyName, SerializedProperty property)
|
|
{
|
|
var memberInfo = ReflectionUtility.GetValidMemberInfo(propertyName, property);
|
|
|
|
return memberInfo is PropertyInfo ? $"<{propertyName}>k__BackingField" : propertyName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks to see if the serialized property is a flagged enum
|
|
/// </summary>
|
|
/// <returns>True if the serialized property type is a flagged enum</returns>
|
|
protected bool IsPropertyEnumFlag() => fieldInfo.FieldType.IsDefined(typeof(FlagsAttribute), false);
|
|
|
|
/// <summary>
|
|
/// Displays an error box in the inspector
|
|
/// </summary>
|
|
/// <param name="root">The root visual element</param>
|
|
/// <param name="errorBox">The help box to displaying the errors</param>
|
|
public static void DisplayErrorBox(VisualElement root, HelpBox errorBox)
|
|
{
|
|
errorBox.messageType = HelpBoxMessageType.Error;
|
|
|
|
if (!string.IsNullOrEmpty(errorBox.text))
|
|
{
|
|
AddElement(root, errorBox);
|
|
}
|
|
else
|
|
{
|
|
RemoveElement(root, errorBox);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Schedules an action to update
|
|
/// </summary>
|
|
/// <param name="visualElement">The visual element to schedule the update</param>
|
|
/// <param name="logicToUpdate">The logic to execute on the specified element</param>
|
|
/// <param name="intervalMs">The update interval in milliseconds</param>
|
|
/// <returns>The scheduled visual element item</returns>
|
|
public static IVisualElementScheduledItem UpdateVisualElement(VisualElement visualElement, Action logicToUpdate, long intervalMs = 60)
|
|
{
|
|
logicToUpdate.Invoke(); // Execute the logic once so we don't have to wait for the first execution of the scheduler
|
|
|
|
return visualElement.schedule.Execute(logicToUpdate).Every(intervalMs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Schedules an action to execute after a delay
|
|
/// </summary>
|
|
/// <param name="visualElement">The visual element to schedule the execution</param>
|
|
/// <param name="logicToExecute">The logic to execute on the specified element</param>
|
|
/// <param name="delayMs">The execution delay in milliseconds</param>
|
|
/// <returns>The scheduled visual element item</returns>
|
|
public static IVisualElementScheduledItem ExecuteLater(VisualElement visualElement, Action logicToExecute, long delayMs = 1) => visualElement.schedule.Execute(logicToExecute).StartingIn(delayMs);
|
|
|
|
/// <summary>
|
|
/// Add an element from another visual element if it doesn't exist
|
|
/// </summary>
|
|
/// <param name="root">The root to add the element on</param>
|
|
/// <param name="element">The element to add</param>
|
|
public static void AddElement(VisualElement root, VisualElement element)
|
|
{
|
|
if (!root.Contains(element))
|
|
root.Add(element);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an element from another visual element if it exists
|
|
/// </summary>
|
|
/// <param name="owner">The owner containing the element</param>
|
|
/// <param name="element">The element to remove</param>
|
|
public static void RemoveElement(VisualElement owner, VisualElement element)
|
|
{
|
|
if (owner.Contains(element))
|
|
owner.Remove(element);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value of a condition for a conditional attribute
|
|
/// </summary>
|
|
/// <param name="memberInfo">The member info of the condition</param>
|
|
/// <param name="conditionalAttribute">The conditional attribute</param>
|
|
/// <param name="serializedProperty">The serialized property</param>
|
|
/// <param name="errorBox">The error box to display any errors to</param>
|
|
/// <returns>True if the condition is satisfied</returns>
|
|
public static bool GetConditionValue(MemberInfo memberInfo, IConditionalAttribute conditionalAttribute, SerializedProperty serializedProperty, HelpBox errorBox)
|
|
{
|
|
var memberInfoType = ReflectionUtility.GetMemberInfoType(memberInfo);
|
|
|
|
if (memberInfoType == null)
|
|
{
|
|
errorBox.text = $"The provided condition \"{conditionalAttribute.ConditionName}\" could not be found";
|
|
return false;
|
|
}
|
|
|
|
if (memberInfoType == typeof(bool))
|
|
{
|
|
var memberInfoValue = ReflectionUtility.GetMemberInfoValue(memberInfo, serializedProperty);
|
|
|
|
if (memberInfoValue == null)
|
|
return false;
|
|
|
|
return (bool)memberInfoValue;
|
|
}
|
|
else if (memberInfoType.IsEnum)
|
|
{
|
|
var memberInfoValue = ReflectionUtility.GetMemberInfoValue(memberInfo, serializedProperty);
|
|
|
|
if (memberInfoValue == null)
|
|
return false;
|
|
|
|
return (int)memberInfoValue == conditionalAttribute.EnumValue;
|
|
}
|
|
|
|
errorBox.text = $"The provided condition \"{conditionalAttribute.ConditionName}\" is not a valid boolean or an enum";
|
|
|
|
return false;
|
|
}
|
|
|
|
internal static bool GetConditionValue(MemberInfo memberInfo, IConditionalAttribute conditionalAttribute, object targetObject, HelpBox errorBox) // Internal function used for the button drawer
|
|
{
|
|
var memberInfoType = ReflectionUtility.GetMemberInfoType(memberInfo);
|
|
|
|
if (memberInfoType == null)
|
|
{
|
|
errorBox.text = $"The provided condition \"{conditionalAttribute.ConditionName}\" could not be found";
|
|
return false;
|
|
}
|
|
|
|
if (memberInfoType == typeof(bool))
|
|
{
|
|
return (bool)ReflectionUtility.GetMemberInfoValue(memberInfo, targetObject);
|
|
}
|
|
else if (memberInfoType.IsEnum)
|
|
{
|
|
return (int)ReflectionUtility.GetMemberInfoValue(memberInfo, targetObject) == conditionalAttribute.EnumValue;
|
|
}
|
|
|
|
errorBox.text = $"The provided condition \"{conditionalAttribute.ConditionName}\" is not a valid boolean or an enum";
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the string value from a member if the input mode is set to Dynamic
|
|
/// </summary>
|
|
/// <param name="inputText">The string input that may contain the member name</param>
|
|
/// <param name="property">The serialized property</param>
|
|
/// <param name="dynamicStringAttribute">The dynamic string attribute</param>
|
|
/// <param name="errorBox">The error box to display any errors to</param>
|
|
/// <returns>If the input mode is Constant will return the base input string, if is Dynamic will return the string value of the member</returns>
|
|
public static string GetDynamicString(string inputText, SerializedProperty property, IDynamicStringAttribute dynamicStringAttribute, HelpBox errorBox)
|
|
{
|
|
switch (dynamicStringAttribute.StringInputMode)
|
|
{
|
|
default:
|
|
case StringInputMode.Constant:
|
|
return inputText;
|
|
|
|
case StringInputMode.Dynamic:
|
|
var memberInfo = ReflectionUtility.GetValidMemberInfo(inputText, property);
|
|
|
|
if (memberInfo == null)
|
|
{
|
|
errorBox.text = $"The member <b>{inputText}</b> could not be found";
|
|
return inputText;
|
|
}
|
|
|
|
var memberValue = ReflectionUtility.GetMemberInfoValue(memberInfo, property);
|
|
var memberType = ReflectionUtility.GetMemberInfoType(memberInfo);
|
|
|
|
if (memberValue == null)
|
|
return inputText;
|
|
|
|
if (memberType == typeof(string))
|
|
return memberValue.ToString();
|
|
|
|
errorBox.text = $"The member <b>{inputText}</b> needs to return a string";
|
|
return inputText;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the property context menu to a non property element
|
|
/// </summary>
|
|
/// <param name="element">The element to add the context menu to</param>
|
|
/// <param name="property">The serialized property</param>
|
|
protected void AddPropertyContextMenu(VisualElement element, SerializedProperty property)
|
|
{
|
|
if (element is PropertyField)
|
|
Debug.LogError("Can't add the property context menu to a property field since it already has one by default.");
|
|
|
|
element.AddManipulator(new ContextualMenuManipulator((@event) =>
|
|
{
|
|
string searchText = $"h:#{property.serializedObject.targetObject.GetType().Name}.{property.propertyPath}={GetPropertyValueAsString(property).Replace(" ", "")}";
|
|
|
|
@event.menu.AppendAction("Copy Property Path", (action) => EditorGUIUtility.systemCopyBuffer = property.propertyPath);
|
|
@event.menu.AppendAction("Search Same Property Value", (action) => SearchService.ShowWindow().SetSearchText(searchText));
|
|
|
|
@event.menu.AppendSeparator();
|
|
|
|
@event.menu.AppendAction("Copy", (action) => EditorGUIUtility.systemCopyBuffer = CopyValue(element, property));
|
|
@event.menu.AppendAction("Paste", (action) => PasteValue(element, property, ParsePropertyClipboardValue(property, EditorGUIUtility.systemCopyBuffer)));
|
|
|
|
@event.menu.AppendSeparator();
|
|
}));
|
|
}
|
|
|
|
private string GetCopyPropertyValue(SerializedProperty property)
|
|
{
|
|
string propertyValue = GetPropertyValueAsString(property);
|
|
|
|
return property.propertyType switch
|
|
{
|
|
SerializedPropertyType.Vector2 or SerializedPropertyType.Vector2Int => $"Vector2{propertyValue}",
|
|
SerializedPropertyType.Vector3 or SerializedPropertyType.Vector3Int => $"Vector3{propertyValue}",
|
|
SerializedPropertyType.Rect or SerializedPropertyType.RectInt => $"Rect{propertyValue}",
|
|
SerializedPropertyType.Bounds or SerializedPropertyType.BoundsInt => $"Bounds{propertyValue}",
|
|
SerializedPropertyType.Vector4 or SerializedPropertyType.Quaternion => property.type + propertyValue,
|
|
SerializedPropertyType.LayerMask => $"LayerMask({propertyValue})",
|
|
SerializedPropertyType.Enum => $"Enum:{(IsPropertyEnumFlag() ? Convert.ToString(property.enumValueFlag, 2) : propertyValue)}",
|
|
_ => propertyValue
|
|
};
|
|
}
|
|
|
|
private string ParsePropertyClipboardValue(SerializedProperty property, string clipboardValue) => property.propertyType switch
|
|
{
|
|
SerializedPropertyType.Vector2 or SerializedPropertyType.Vector2Int => clipboardValue.Replace("Vector2", ""),
|
|
SerializedPropertyType.Vector3 or SerializedPropertyType.Vector3Int => clipboardValue.Replace("Vector3", ""),
|
|
SerializedPropertyType.Rect or SerializedPropertyType.RectInt => clipboardValue.Replace("Rect", ""),
|
|
SerializedPropertyType.Bounds or SerializedPropertyType.BoundsInt => clipboardValue.Replace("Bounds", ""),
|
|
SerializedPropertyType.Vector4 or SerializedPropertyType.Quaternion => clipboardValue.Replace(property.type, ""),
|
|
SerializedPropertyType.LayerMask => clipboardValue.Replace("LayerMask", ""),
|
|
SerializedPropertyType.Enum => clipboardValue.Replace("Enum:", ""),
|
|
_ => clipboardValue
|
|
};
|
|
|
|
private protected string CreatePropertySaveKey(SerializedProperty property, string key) => $"{property.serializedObject.targetObject.GetInstanceID()}_{property.propertyPath}_{key}";
|
|
|
|
/// <summary>
|
|
/// Invokes a function on all specified targets
|
|
/// </summary>
|
|
/// <param name="targets">The property to get the targets from</param>
|
|
/// <param name="functionName">The name of the function to invoke</param>
|
|
/// <param name="parameterValues">Parameter values for the function</param>
|
|
public static void InvokeFunctionOnAllTargets(Object[] targets, string functionName, object[] parameterValues = null)
|
|
{
|
|
foreach (var target in targets)
|
|
{
|
|
var methodInfo = ReflectionUtility.FindFunction(functionName, target);
|
|
|
|
Undo.RecordObject(target, $"Invoke {functionName}");
|
|
|
|
methodInfo.Invoke(target, parameterValues);
|
|
|
|
EditorUtility.SetDirty(target);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies the help box style to a visual element
|
|
/// </summary>
|
|
/// <param name="visualElement">The element to apply the style to</param>
|
|
public static void ApplyBoxStyle(VisualElement visualElement)
|
|
{
|
|
visualElement.style.borderTopLeftRadius = 3f;
|
|
visualElement.style.borderTopRightRadius = 3f;
|
|
visualElement.style.borderBottomLeftRadius = 3f;
|
|
visualElement.style.borderBottomRightRadius = 3f;
|
|
|
|
visualElement.style.borderBottomWidth = 1f;
|
|
visualElement.style.borderTopWidth = 1f;
|
|
visualElement.style.borderLeftWidth = 1f;
|
|
visualElement.style.borderRightWidth = 1f;
|
|
|
|
visualElement.style.borderTopColor = new Color(26f / 255f, 26f / 255f, 26f / 255f);
|
|
visualElement.style.borderBottomColor = new Color(26f / 255f, 26f / 255f, 26f / 255f);
|
|
visualElement.style.borderLeftColor = new Color(26f / 255f, 26f / 255f, 26f / 255f);
|
|
visualElement.style.borderRightColor = new Color(26f / 255f, 26f / 255f, 26f / 255f);
|
|
|
|
visualElement.style.backgroundColor = EditorExtension.GLOBAL_COLOR != EditorExtension.DEFAULT_GLOBAL_COLOR ? EditorExtension.GLOBAL_COLOR / 2f : new Color(63f / 255f, 63f / 255f, 63f / 255f);
|
|
|
|
visualElement.style.paddingTop = 3f;
|
|
visualElement.style.paddingBottom = 3f;
|
|
visualElement.style.paddingLeft = 3f;
|
|
visualElement.style.paddingRight = 3f;
|
|
|
|
visualElement.style.marginTop = 1f;
|
|
visualElement.style.marginBottom = 1f;
|
|
visualElement.style.marginRight = 3f;
|
|
visualElement.style.marginLeft = 3f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies all of the style values from a <see cref="VisualElement"/> to another
|
|
/// </summary>
|
|
/// <param name="copyFrom">The element to copy the style from</param>
|
|
/// <param name="copyTo">The element to copy the style to</param>
|
|
public void CopyStyle(VisualElement copyFrom, VisualElement copyTo)
|
|
{
|
|
copyTo.style.position = copyFrom.style.position;
|
|
copyTo.style.top = copyFrom.style.top;
|
|
copyTo.style.bottom = copyFrom.style.bottom;
|
|
copyTo.style.left = copyFrom.style.left;
|
|
copyTo.style.right = copyFrom.style.right;
|
|
copyTo.style.paddingTop = copyFrom.style.paddingTop;
|
|
copyTo.style.paddingBottom = copyFrom.style.paddingBottom;
|
|
copyTo.style.paddingLeft = copyFrom.style.paddingLeft;
|
|
copyTo.style.paddingRight = copyFrom.style.paddingRight;
|
|
copyTo.style.alignContent = copyFrom.style.alignContent;
|
|
copyTo.style.alignItems = copyFrom.style.alignItems;
|
|
copyTo.style.alignSelf = copyFrom.style.alignSelf;
|
|
copyTo.style.flexBasis = copyFrom.style.flexBasis;
|
|
copyTo.style.flexDirection = copyFrom.style.flexDirection;
|
|
copyTo.style.flexWrap = copyFrom.style.flexWrap;
|
|
copyTo.style.width = copyFrom.style.width;
|
|
copyTo.style.height = copyFrom.style.height;
|
|
copyTo.style.justifyContent = copyFrom.style.justifyContent;
|
|
copyTo.style.marginTop = copyFrom.style.marginTop;
|
|
copyTo.style.marginBottom = copyFrom.style.marginBottom;
|
|
copyTo.style.marginLeft = copyFrom.style.marginLeft;
|
|
copyTo.style.marginRight = copyFrom.style.marginRight;
|
|
copyTo.style.transformOrigin = copyFrom.style.transformOrigin;
|
|
copyTo.style.translate = copyFrom.style.translate;
|
|
copyTo.style.rotate = copyFrom.style.rotate;
|
|
copyTo.style.scale = copyFrom.style.scale;
|
|
copyTo.style.transitionDelay = copyFrom.style.transitionDelay;
|
|
copyTo.style.transitionDuration = copyFrom.style.transitionDuration;
|
|
copyTo.style.transitionProperty = copyFrom.style.transitionProperty;
|
|
copyTo.style.transitionTimingFunction = copyFrom.style.transitionTimingFunction;
|
|
copyTo.style.color = copyFrom.style.color;
|
|
copyTo.style.backgroundColor = copyFrom.style.backgroundColor;
|
|
copyTo.style.unityBackgroundImageTintColor = copyFrom.style.unityBackgroundImageTintColor;
|
|
copyTo.style.backgroundImage = copyFrom.style.backgroundImage;
|
|
copyTo.style.backgroundPositionX = copyFrom.style.backgroundPositionX;
|
|
copyTo.style.backgroundPositionY = copyFrom.style.backgroundPositionY;
|
|
copyTo.style.backgroundRepeat = copyFrom.style.backgroundRepeat;
|
|
copyTo.style.backgroundSize = copyFrom.style.backgroundSize;
|
|
copyTo.style.opacity = copyFrom.style.opacity;
|
|
copyTo.style.unityOverflowClipBox = copyFrom.style.unityOverflowClipBox;
|
|
copyTo.style.minWidth = copyFrom.style.minWidth;
|
|
copyTo.style.maxWidth = copyFrom.style.maxWidth;
|
|
copyTo.style.minHeight = copyFrom.style.minHeight;
|
|
copyTo.style.maxHeight = copyFrom.style.maxHeight;
|
|
copyTo.style.borderTopColor = copyFrom.style.borderTopColor;
|
|
copyTo.style.borderBottomColor = copyFrom.style.borderBottomColor;
|
|
copyTo.style.borderLeftColor = copyFrom.style.borderLeftColor;
|
|
copyTo.style.borderRightColor = copyFrom.style.borderRightColor;
|
|
copyTo.style.fontSize = copyFrom.style.fontSize;
|
|
copyTo.style.unityFont = copyFrom.style.unityFont;
|
|
copyTo.style.unityFontStyleAndWeight = copyFrom.style.unityFontStyleAndWeight;
|
|
copyTo.style.unityFontDefinition = copyFrom.style.unityFontDefinition;
|
|
copyTo.style.unityTextAlign = copyFrom.style.unityTextAlign;
|
|
copyTo.style.textShadow = copyFrom.style.textShadow;
|
|
copyTo.style.unityTextOutlineColor = copyFrom.style.unityTextOutlineColor;
|
|
copyTo.style.unityTextOverflowPosition = copyFrom.style.unityTextOverflowPosition;
|
|
copyTo.style.textOverflow = copyFrom.style.textOverflow;
|
|
copyTo.style.unityTextOutlineWidth = copyFrom.style.unityTextOutlineWidth;
|
|
copyTo.style.wordSpacing = copyFrom.style.wordSpacing;
|
|
copyTo.style.unityParagraphSpacing = copyFrom.style.unityParagraphSpacing;
|
|
copyTo.style.whiteSpace = copyFrom.style.whiteSpace;
|
|
copyTo.style.cursor = copyFrom.style.cursor;
|
|
copyTo.style.overflow = copyFrom.style.overflow;
|
|
|
|
#if UNITY_6000_0_OR_NEWER
|
|
copyTo.style.unityTextGenerator = copyFrom.style.unityTextGenerator;
|
|
copyTo.style.unityEditorTextRenderingMode = copyFrom.style.unityEditorTextRenderingMode;
|
|
#endif
|
|
foreach (var @class in copyFrom.GetClasses())
|
|
copyTo.AddToClassList(@class);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a field for a specific type
|
|
/// </summary>
|
|
/// <typeparam name="T"> The type of the field to create</typeparam>
|
|
/// <param name="fieldName">The name of the field</param>
|
|
/// <param name="fieldValue">The default value of the field</param>
|
|
/// <param name="showMixedValue">Whether to show the mixed value state for the field</param>
|
|
/// <returns>A visual element of the appropriate field</returns>
|
|
public static VisualElement CreateFieldForType<T>(string fieldName, object fieldValue, bool showMixedValue = false) => CreateFieldForType(typeof(T), fieldName, fieldValue, showMixedValue);
|
|
|
|
/// <summary>
|
|
/// Creates a field for a specific type
|
|
/// </summary>
|
|
/// <param name="fieldType">The type of the field to create</param>
|
|
/// <param name="fieldName">The name of the field</param>
|
|
/// <param name="fieldValue">The default value of the field</param>
|
|
/// <param name="showMixedValue">Whether to show the mixed value state for the field</param>
|
|
/// <returns>A visual element of the appropriate field</returns>
|
|
public static VisualElement CreateFieldForType(Type fieldType, string fieldName, object fieldValue, bool showMixedValue = false)
|
|
{
|
|
fieldName = ObjectNames.NicifyVariableName(fieldName);
|
|
|
|
if (fieldType == typeof(string))
|
|
{
|
|
return new TextField(fieldName) { value = (string)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(char))
|
|
{
|
|
return new TextField(fieldName) { value = fieldValue.ToString(), showMixedValue = showMixedValue, maxLength = 1 };
|
|
}
|
|
else if (fieldType == typeof(int))
|
|
{
|
|
return new IntegerField(fieldName) { value = (int)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(uint))
|
|
{
|
|
return new UnsignedIntegerField(fieldName) { value = (uint)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(long))
|
|
{
|
|
return new LongField(fieldName) { value = (long)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(ulong))
|
|
{
|
|
return new UnsignedLongField(fieldName) { value = (ulong)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(float))
|
|
{
|
|
return new FloatField(fieldName) { value = (float)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(double))
|
|
{
|
|
return new DoubleField(fieldName) { value = (double)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(bool))
|
|
{
|
|
return new Toggle(fieldName) { value = (bool)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType.IsEnum)
|
|
{
|
|
return new EnumField(fieldName, (Enum)fieldValue) { showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Vector2))
|
|
{
|
|
return new Vector2Field(fieldName) { value = (Vector2)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Vector2Int))
|
|
{
|
|
return new Vector2IntField(fieldName) { value = (Vector2Int)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Vector3))
|
|
{
|
|
return new Vector3Field(fieldName) { value = (Vector3)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Vector3Int))
|
|
{
|
|
return new Vector3IntField(fieldName) { value = (Vector3Int)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Vector4))
|
|
{
|
|
return new Vector4Field(fieldName) { value = (Vector4)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Color))
|
|
{
|
|
return new ColorField(fieldName) { value = (Color)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Gradient))
|
|
{
|
|
return new GradientField(fieldName) { value = (Gradient)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(AnimationCurve))
|
|
{
|
|
return new CurveField(fieldName) { value = (AnimationCurve)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(LayerMask))
|
|
{
|
|
return new LayerMaskField(fieldName, (LayerMask)fieldValue) { showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Rect))
|
|
{
|
|
return new RectField(fieldName) { value = (Rect)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(RectInt))
|
|
{
|
|
return new RectIntField(fieldName) { value = (RectInt)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(Bounds))
|
|
{
|
|
return new BoundsField(fieldName) { value = (Bounds)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType == typeof(BoundsInt))
|
|
{
|
|
return new BoundsIntField(fieldName) { value = (BoundsInt)fieldValue, showMixedValue = showMixedValue };
|
|
}
|
|
else if (fieldType.IsSerializable && !ReflectionUtility.IsTypeCollection(fieldType) && !fieldType.IsPrimitive)
|
|
{
|
|
var serializedObjectFoldout = new Foldout { text = fieldName };
|
|
|
|
var nestedFields = fieldType.GetFields();
|
|
|
|
foreach (var field in nestedFields)
|
|
{
|
|
var createdField = CreateFieldForType(field.FieldType, field.Name, field.GetValue(fieldValue));
|
|
|
|
createdField.AddToClassList(BaseField<Void>.alignedFieldUssClassName);
|
|
|
|
serializedObjectFoldout.Add(createdField);
|
|
}
|
|
|
|
return serializedObjectFoldout;
|
|
}
|
|
else
|
|
{
|
|
return new HelpBox($"The type <b>{fieldType}</b> is not supported", HelpBoxMessageType.Error);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers a value changed callback for field of a specific type.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the value</typeparam>
|
|
/// <param name="field">The visual element of the field</param>
|
|
/// <param name="valueCallback">The callback action</param>
|
|
/// <param name="objectValue">The value of the registered serialized object. This parameter is only required if you need to register value callbacks to serialized objects</param>
|
|
public static void RegisterValueChangedCallbackByType<T>(VisualElement field, Action<object> valueCallback, object objectValue = null) => RegisterValueChangedCallbackByType(typeof(T), field, valueCallback, objectValue);
|
|
|
|
/// <summary>
|
|
/// Registers a value changed callback for field of a specific type.
|
|
/// </summary>
|
|
/// <param name="fieldType">The type of the value</param>
|
|
/// <param name="field">The visual element of the field</param>
|
|
/// <param name="valueCallback">The callback action</param>
|
|
/// <param name="objectValue">The value of the registered serialized object. This parameter is only required if you need to register value callbacks to serialized objects</param>
|
|
public static void RegisterValueChangedCallbackByType(Type fieldType, VisualElement field, Action<object> valueCallback, object objectValue = null)
|
|
{
|
|
if (fieldType == typeof(string) || fieldType == typeof(char))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<string>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(int))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<int>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(uint))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<uint>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(long))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<long>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(ulong))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<ulong>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(float))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<float>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(double))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<double>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(bool))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<bool>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType.IsEnum)
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Enum>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Vector2))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Vector2>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Vector2Int))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Vector2Int>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Vector3))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Vector3>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Vector3Int))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Vector3Int>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Vector4))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Vector4>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Color))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Color>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Gradient))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Gradient>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(AnimationCurve))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<AnimationCurve>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(LayerMask))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<int>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Rect))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Rect>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(RectInt))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<RectInt>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(Bounds))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<Bounds>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType == typeof(BoundsInt))
|
|
{
|
|
field.RegisterCallback<ChangeEvent<BoundsInt>>((callback) => valueCallback.Invoke(callback.newValue));
|
|
}
|
|
else if (fieldType.IsSerializable && !ReflectionUtility.IsTypeCollection(fieldType) && !fieldType.IsPrimitive)
|
|
{
|
|
if (objectValue == null)
|
|
{
|
|
Debug.LogError("You are attempting to register a value on a custom serialized object but the <b>objectValue</b> parameter is not assigned");
|
|
return;
|
|
}
|
|
|
|
var nestedFields = fieldType.GetFields();
|
|
|
|
foreach (var nestedField in nestedFields)
|
|
{
|
|
RegisterValueChangedCallbackByType(nestedField.FieldType, field, (value) =>
|
|
{
|
|
nestedField.SetValue(objectValue, value);
|
|
|
|
valueCallback.Invoke(objectValue);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the label of the appropriate field
|
|
/// </summary>
|
|
/// <param name="field">The visual element of the field</param>
|
|
/// <returns>The field label</returns>
|
|
public static string GetFieldLabel(VisualElement field) => field switch
|
|
{
|
|
TextField textField => textField.label,
|
|
IntegerField integerField => integerField.label,
|
|
UnsignedIntegerField unsignedIntegerField => unsignedIntegerField.label,
|
|
LongField longField => longField.label,
|
|
UnsignedLongField unsignedLongField => unsignedLongField.label,
|
|
FloatField floatField => floatField.label,
|
|
DoubleField doubleField => doubleField.label,
|
|
Toggle toggle => toggle.label,
|
|
EnumField enumField => enumField.label,
|
|
Vector2Field vector2Field => vector2Field.label,
|
|
Vector2IntField vector2IntField => vector2IntField.label,
|
|
Vector3Field vector3Field => vector3Field.label,
|
|
Vector3IntField vector3IntField => vector3IntField.label,
|
|
Vector4Field vector4Field => vector4Field.label,
|
|
ColorField colorField => colorField.label,
|
|
GradientField gradientField => gradientField.label,
|
|
CurveField curveField => curveField.label,
|
|
LayerMaskField layerMaskField => layerMaskField.label,
|
|
RectField rectField => rectField.label,
|
|
RectIntField rectIntField => rectIntField.label,
|
|
BoundsField boundsField => boundsField.label,
|
|
BoundsIntField boundsIntField => boundsIntField.label,
|
|
_ => null,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Gets the value of the appropriate field
|
|
/// </summary>
|
|
/// <param name="field">The visual element of the field</param>
|
|
/// <returns>The field value</returns>
|
|
public static object GetFieldValue(VisualElement field) => field switch
|
|
{
|
|
TextField textField => textField.value,
|
|
IntegerField integerField => integerField.value,
|
|
UnsignedIntegerField unsignedIntegerField => unsignedIntegerField.value,
|
|
LongField longField => longField.value,
|
|
UnsignedLongField unsignedLongField => unsignedLongField.value,
|
|
FloatField floatField => floatField.value,
|
|
DoubleField doubleField => doubleField.value,
|
|
Toggle toggle => toggle.value,
|
|
EnumField enumField => enumField.value,
|
|
Vector2Field vector2Field => vector2Field.value,
|
|
Vector2IntField vector2IntField => vector2IntField.value,
|
|
Vector3Field vector3Field => vector3Field.value,
|
|
Vector3IntField vector3IntField => vector3IntField.value,
|
|
Vector4Field vector4Field => vector4Field.value,
|
|
ColorField colorField => colorField.value,
|
|
GradientField gradientField => gradientField.value,
|
|
CurveField curveField => curveField.value,
|
|
LayerMaskField layerMaskField => layerMaskField.value,
|
|
RectField rectField => rectField.value,
|
|
RectIntField rectIntField => rectIntField.value,
|
|
BoundsField boundsField => boundsField.value,
|
|
BoundsIntField boundsIntField => boundsIntField.value,
|
|
_ => null,
|
|
};
|
|
|
|
/// <summary>
|
|
/// Sets the value of the appropriate field
|
|
/// </summary>
|
|
/// <param name="field">The visual element of the field</param>
|
|
/// <param name="value">The value to set</param>
|
|
/// <param name="notify">Whether to call the value change callback when setting the value</param>
|
|
public static void SetFieldValue(VisualElement field, object value, bool notify = false)
|
|
{
|
|
if (field is TextField textField)
|
|
{
|
|
if (notify)
|
|
{
|
|
textField.value = value.ToString();
|
|
}
|
|
else
|
|
{
|
|
textField.SetValueWithoutNotify(value.ToString());
|
|
}
|
|
}
|
|
else if (field is IntegerField integerField)
|
|
{
|
|
if (notify)
|
|
{
|
|
integerField.value = (int)value;
|
|
}
|
|
else
|
|
{
|
|
integerField.SetValueWithoutNotify((int)value);
|
|
}
|
|
}
|
|
else if (field is UnsignedIntegerField unsignedIntegerField)
|
|
{
|
|
if (notify)
|
|
{
|
|
unsignedIntegerField.value = (uint)value;
|
|
}
|
|
else
|
|
{
|
|
unsignedIntegerField.SetValueWithoutNotify((uint)value);
|
|
}
|
|
}
|
|
else if (field is LongField longField)
|
|
{
|
|
if (notify)
|
|
{
|
|
longField.value = (long)value;
|
|
}
|
|
else
|
|
{
|
|
longField.SetValueWithoutNotify((long)value);
|
|
}
|
|
}
|
|
else if (field is UnsignedLongField unsignedLongField)
|
|
{
|
|
if (notify)
|
|
{
|
|
unsignedLongField.value = (ulong)value;
|
|
}
|
|
else
|
|
{
|
|
unsignedLongField.SetValueWithoutNotify((ulong)value);
|
|
}
|
|
}
|
|
else if (field is FloatField floatField)
|
|
{
|
|
if (notify)
|
|
{
|
|
floatField.value = (float)value;
|
|
}
|
|
else
|
|
{
|
|
floatField.SetValueWithoutNotify((float)value);
|
|
}
|
|
}
|
|
else if (field is DoubleField doubleField)
|
|
{
|
|
if (notify)
|
|
{
|
|
doubleField.value = (double)value;
|
|
}
|
|
else
|
|
{
|
|
doubleField.SetValueWithoutNotify((double)value);
|
|
}
|
|
}
|
|
else if (field is Toggle toggle)
|
|
{
|
|
if (notify)
|
|
{
|
|
toggle.value = (bool)value;
|
|
}
|
|
else
|
|
{
|
|
toggle.SetValueWithoutNotify((bool)value);
|
|
}
|
|
}
|
|
else if (field is EnumField enumField)
|
|
{
|
|
if (notify)
|
|
{
|
|
enumField.value = (Enum)value;
|
|
}
|
|
else
|
|
{
|
|
enumField.SetValueWithoutNotify((Enum)value);
|
|
}
|
|
}
|
|
else if (field is Vector2Field vector2Field)
|
|
{
|
|
if (notify)
|
|
{
|
|
vector2Field.value = (Vector2)value;
|
|
}
|
|
else
|
|
{
|
|
vector2Field.SetValueWithoutNotify((Vector2)value);
|
|
}
|
|
}
|
|
else if (field is Vector2IntField vector2IntField)
|
|
{
|
|
if (notify)
|
|
{
|
|
vector2IntField.value = (Vector2Int)value;
|
|
}
|
|
else
|
|
{
|
|
vector2IntField.SetValueWithoutNotify((Vector2Int)value);
|
|
}
|
|
}
|
|
else if (field is Vector3Field vector3Field)
|
|
{
|
|
if (notify)
|
|
{
|
|
vector3Field.value = (Vector3)value;
|
|
}
|
|
else
|
|
{
|
|
vector3Field.SetValueWithoutNotify((Vector3)value);
|
|
}
|
|
}
|
|
else if (field is Vector3IntField vector3IntField)
|
|
{
|
|
if (notify)
|
|
{
|
|
vector3IntField.value = (Vector3Int)value;
|
|
}
|
|
else
|
|
{
|
|
vector3IntField.SetValueWithoutNotify((Vector3Int)value);
|
|
}
|
|
}
|
|
else if (field is Vector4Field vector4Field)
|
|
{
|
|
if (notify)
|
|
{
|
|
vector4Field.value = (Vector4)value;
|
|
}
|
|
else
|
|
{
|
|
vector4Field.SetValueWithoutNotify((Vector4)value);
|
|
}
|
|
}
|
|
else if (field is ColorField colorField)
|
|
{
|
|
if (notify)
|
|
{
|
|
colorField.value = (Color)value;
|
|
}
|
|
else
|
|
{
|
|
colorField.SetValueWithoutNotify((Color)value);
|
|
}
|
|
}
|
|
else if (field is GradientField gradientField)
|
|
{
|
|
if (notify)
|
|
{
|
|
gradientField.value = (Gradient)value;
|
|
}
|
|
else
|
|
{
|
|
gradientField.SetValueWithoutNotify((Gradient)value);
|
|
}
|
|
}
|
|
else if (field is CurveField curveField)
|
|
{
|
|
if (notify)
|
|
{
|
|
curveField.value = (AnimationCurve)value;
|
|
}
|
|
else
|
|
{
|
|
curveField.SetValueWithoutNotify((AnimationCurve)value);
|
|
}
|
|
}
|
|
else if (field is LayerMaskField layerMaskField)
|
|
{
|
|
if (notify)
|
|
{
|
|
layerMaskField.value = (LayerMask)value;
|
|
}
|
|
else
|
|
{
|
|
layerMaskField.SetValueWithoutNotify((LayerMask)value);
|
|
}
|
|
}
|
|
else if (field is RectField rectField)
|
|
{
|
|
if (notify)
|
|
{
|
|
rectField.value = (Rect)value;
|
|
}
|
|
else
|
|
{
|
|
rectField.SetValueWithoutNotify((Rect)value);
|
|
}
|
|
}
|
|
else if (field is RectIntField rectIntField)
|
|
{
|
|
if (notify)
|
|
{
|
|
rectIntField.value = (RectInt)value;
|
|
}
|
|
else
|
|
{
|
|
rectIntField.SetValueWithoutNotify((RectInt)value);
|
|
}
|
|
}
|
|
else if (field is BoundsField boundsField)
|
|
{
|
|
if (notify)
|
|
{
|
|
boundsField.value = (Bounds)value;
|
|
}
|
|
else
|
|
{
|
|
boundsField.SetValueWithoutNotify((Bounds)value);
|
|
}
|
|
}
|
|
else if (field is BoundsIntField boundsIntField)
|
|
{
|
|
if (notify)
|
|
{
|
|
boundsIntField.value = (BoundsInt)value;
|
|
}
|
|
else
|
|
{
|
|
boundsIntField.SetValueWithoutNotify((BoundsInt)value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a field to the target member value
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the field</typeparam>
|
|
/// <param name="field">The field visual element</param>
|
|
/// <param name="memberInfo">The member to bind</param>
|
|
/// <param name="targetObject">The target object of the member</param>
|
|
public static void BindFieldToMember<T>(VisualElement field, MemberInfo memberInfo, object targetObject) => BindFieldToMember(typeof(T), field, memberInfo, targetObject);
|
|
|
|
/// <summary>
|
|
/// Bind a field to the target member value
|
|
/// </summary>
|
|
/// <param name="fieldType">The type of the field</param>
|
|
/// <param name="field">The field visual element</param>
|
|
/// <param name="memberInfo">The member to bind</param>
|
|
/// <param name="targetObject">The target object of the member</param>
|
|
public static void BindFieldToMember(Type fieldType, VisualElement field, MemberInfo memberInfo, object targetObject)
|
|
{
|
|
UpdateVisualElement(field, () =>
|
|
{
|
|
var memberValue = ReflectionUtility.GetMemberInfoValue(memberInfo, targetObject);
|
|
|
|
if (IsTypeValid(fieldType))
|
|
{
|
|
SetFieldValue(field, memberValue);
|
|
}
|
|
else if (!fieldType.IsPrimitive && fieldType.IsSerializable && !ReflectionUtility.IsTypeCollection(fieldType))
|
|
{
|
|
var childFields = field.contentContainer.Children().ToArray();
|
|
var nestedFields = fieldType.GetFields();
|
|
|
|
for (int i = 0; i < nestedFields.Length; i++)
|
|
{
|
|
var nestedField = nestedFields[i];
|
|
|
|
SetFieldValue(childFields[i], nestedField.GetValue(memberValue));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"Cannot bind to the field to <b>{fieldType}</b>, this type is not supported", (Object)targetObject);
|
|
}
|
|
});
|
|
|
|
static bool IsTypeValid(Type type) => type.IsEnum || type == typeof(string) || type == typeof(char)
|
|
|| type == typeof(int) || type == typeof(uint) || type == typeof(long) || type == typeof(ulong) || type == typeof(float) || type == typeof(double) || type == typeof(bool)
|
|
|| type == typeof(Vector2) || type == typeof(Vector2Int) || type == typeof(Vector3) || type == typeof(Vector3Int) || type == typeof(Vector4) || type == typeof(Color)
|
|
|| type == typeof(LayerMask) || type == typeof(Rect) || type == typeof(RectInt) || type == typeof(Bounds) || type == typeof(BoundsInt) || type == typeof(Gradient) || type == typeof(AnimationCurve);
|
|
}
|
|
|
|
#region NON_GUI_RELATED_UTILITY_FUNCITONS
|
|
|
|
/// <summary>
|
|
/// A short handy version of Debug.Log
|
|
/// </summary>
|
|
/// <param name="message">The message to print</param>
|
|
protected static void Print(object message) => Debug.Log(message);
|
|
|
|
/// <summary>
|
|
/// Checks if a collection is null or has no members
|
|
/// </summary>
|
|
/// <param name="collection">The collection to check</param>
|
|
/// <returns>False is the collection is null or has no members, true otherwise</returns>
|
|
public static bool IsCollectionValid(ICollection collection) => collection != null && collection.Count != 0;
|
|
|
|
/// <summary>
|
|
/// Gets the size of a 2D texture
|
|
/// </summary>
|
|
/// <param name="texture">The texture to get the size from</param>
|
|
/// <returns>The width and height of the texture as a Vector2</returns>
|
|
public static Vector2 GetTextureSize(Texture2D texture) => new(texture.width, texture.height);
|
|
|
|
#endregion
|
|
}
|
|
}
|