417 lines
18 KiB
C#
417 lines
18 KiB
C#
#if UNITY_EDITOR
|
|
using System.Collections.Generic;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace AlicizaX.UI
|
|
{
|
|
[CustomEditor(typeof(UXBinding))]
|
|
public sealed class UXBindingEditor : UnityEditor.Editor
|
|
{
|
|
private SerializedProperty _controllerProp;
|
|
private SerializedProperty _entriesProp;
|
|
private readonly Dictionary<int, bool> _foldouts = new Dictionary<int, bool>();
|
|
private readonly List<UXBindingProperty> _supportedProperties = new List<UXBindingProperty>();
|
|
|
|
private void OnEnable()
|
|
{
|
|
_controllerProp = serializedObject.FindProperty("_controller");
|
|
_entriesProp = serializedObject.FindProperty("_entries");
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
var binding = (UXBinding)target;
|
|
|
|
DrawHeader(binding);
|
|
EditorGUILayout.Space(6f);
|
|
DrawControllerField(binding);
|
|
EditorGUILayout.Space(6f);
|
|
DrawEntries(binding);
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
private void DrawHeader(UXBinding binding)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.LabelField("UX Binding", EditorStyles.boldLabel);
|
|
EditorGUILayout.LabelField($"Target: {binding.gameObject.name}", EditorStyles.miniLabel);
|
|
EditorGUILayout.LabelField($"Rules: {_entriesProp.arraySize}", EditorStyles.miniLabel);
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Add Rule"))
|
|
{
|
|
AddEntry(binding);
|
|
}
|
|
|
|
if (GUILayout.Button("Capture Defaults"))
|
|
{
|
|
binding.CaptureDefaults();
|
|
EditorUtility.SetDirty(binding);
|
|
}
|
|
|
|
if (GUILayout.Button("Reset To Defaults"))
|
|
{
|
|
binding.ResetToDefaults();
|
|
EditorUtility.SetDirty(binding);
|
|
SceneView.RepaintAll();
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawControllerField(UXBinding binding)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.LabelField("Controller", EditorStyles.boldLabel);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
UXController newController = (UXController)EditorGUILayout.ObjectField("Reference", binding.Controller, typeof(UXController), true);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
Undo.RecordObject(binding, "Change UX Binding Controller");
|
|
binding.SetController(newController);
|
|
_controllerProp.objectReferenceValue = newController;
|
|
EditorUtility.SetDirty(binding);
|
|
}
|
|
|
|
if (binding.Controller == null)
|
|
{
|
|
EditorGUILayout.HelpBox("Assign a UXController on this object or one of its parents.", MessageType.Warning);
|
|
}
|
|
else
|
|
{
|
|
EditorGUILayout.LabelField($"Bound To: {binding.Controller.name}", EditorStyles.miniLabel);
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawEntries(UXBinding binding)
|
|
{
|
|
if (_entriesProp.arraySize == 0)
|
|
{
|
|
EditorGUILayout.HelpBox("No rules yet. Add one and choose a controller + property pair.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
UXBindingPropertyUtility.GetSupportedProperties(binding.gameObject, _supportedProperties);
|
|
|
|
for (int i = 0; i < _entriesProp.arraySize; i++)
|
|
{
|
|
DrawEntry(binding, i);
|
|
EditorGUILayout.Space(4f);
|
|
}
|
|
}
|
|
|
|
private void DrawEntry(UXBinding binding, int index)
|
|
{
|
|
SerializedProperty entryProp = _entriesProp.GetArrayElementAtIndex(index);
|
|
SerializedProperty controllerIdProp = entryProp.FindPropertyRelative("_controllerId");
|
|
SerializedProperty controllerIndexProp = entryProp.FindPropertyRelative("_controllerIndex");
|
|
SerializedProperty propertyProp = entryProp.FindPropertyRelative("_property");
|
|
SerializedProperty valueProp = entryProp.FindPropertyRelative("_value");
|
|
SerializedProperty fallbackModeProp = entryProp.FindPropertyRelative("_fallbackMode");
|
|
SerializedProperty fallbackValueProp = entryProp.FindPropertyRelative("_fallbackValue");
|
|
|
|
UXBindingProperty property = (UXBindingProperty)propertyProp.enumValueIndex;
|
|
bool expanded = _foldouts.ContainsKey(index) && _foldouts[index];
|
|
string label = UXBindingPropertyUtility.GetMetadata(property).DisplayName;
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.BeginHorizontal();
|
|
expanded = EditorGUILayout.Foldout(expanded, $"[{index}] {label}", true);
|
|
GUILayout.FlexibleSpace();
|
|
|
|
if (GUILayout.Button("Preview", EditorStyles.miniButtonLeft, GUILayout.Width(60f)))
|
|
{
|
|
binding.PreviewEntry(index);
|
|
SceneView.RepaintAll();
|
|
}
|
|
|
|
if (GUILayout.Button("X", EditorStyles.miniButtonRight, GUILayout.Width(24f)))
|
|
{
|
|
DeleteEntry(binding, index);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
_foldouts[index] = expanded;
|
|
|
|
if (expanded)
|
|
{
|
|
DrawControllerSelector(binding, controllerIdProp, controllerIndexProp);
|
|
DrawPropertySelector(entryProp, binding.gameObject, propertyProp);
|
|
|
|
property = (UXBindingProperty)propertyProp.enumValueIndex;
|
|
UXBindingPropertyMetadata metadata = UXBindingPropertyUtility.GetMetadata(property);
|
|
|
|
EditorGUILayout.Space(2f);
|
|
EditorGUILayout.LabelField("Value", EditorStyles.boldLabel);
|
|
DrawValueField(valueProp, metadata, "Matched Value");
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Use Current"))
|
|
{
|
|
binding.CaptureEntryValue(index);
|
|
EditorUtility.SetDirty(binding);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.Space(2f);
|
|
EditorGUILayout.PropertyField(fallbackModeProp, new GUIContent("Fallback"));
|
|
UXBindingFallbackMode fallbackMode = (UXBindingFallbackMode)fallbackModeProp.enumValueIndex;
|
|
if (fallbackMode == UXBindingFallbackMode.UseCustomValue)
|
|
{
|
|
DrawValueField(fallbackValueProp, metadata, "Fallback Value");
|
|
|
|
if (GUILayout.Button("Use Current As Fallback"))
|
|
{
|
|
binding.CaptureEntryFallbackValue(index);
|
|
EditorUtility.SetDirty(binding);
|
|
}
|
|
}
|
|
|
|
if (!_supportedProperties.Contains(property))
|
|
{
|
|
EditorGUILayout.HelpBox("This property is not supported by the components on the current GameObject.", MessageType.Error);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private void DrawControllerSelector(UXBinding binding, SerializedProperty controllerIdProp, SerializedProperty controllerIndexProp)
|
|
{
|
|
UXController controller = binding.Controller;
|
|
if (controller == null || controller.Controllers.Count == 0)
|
|
{
|
|
EditorGUILayout.HelpBox("Create a controller definition first.", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
string[] names = new string[controller.Controllers.Count];
|
|
int selectedController = 0;
|
|
|
|
for (int i = 0; i < controller.Controllers.Count; i++)
|
|
{
|
|
UXController.ControllerDefinition definition = controller.Controllers[i];
|
|
names[i] = definition.Name;
|
|
if (definition.Id == controllerIdProp.stringValue)
|
|
{
|
|
selectedController = i;
|
|
}
|
|
}
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
selectedController = EditorGUILayout.Popup("Controller", selectedController, names);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
controllerIdProp.stringValue = controller.Controllers[selectedController].Id;
|
|
controllerIndexProp.intValue = 0;
|
|
}
|
|
|
|
UXController.ControllerDefinition selectedDefinition = controller.Controllers[selectedController];
|
|
int maxIndex = Mathf.Max(1, selectedDefinition.Length);
|
|
controllerIndexProp.intValue = Mathf.Clamp(controllerIndexProp.intValue, 0, maxIndex - 1);
|
|
|
|
string[] indexNames = new string[maxIndex];
|
|
for (int i = 0; i < maxIndex; i++)
|
|
{
|
|
indexNames[i] = i.ToString();
|
|
}
|
|
|
|
controllerIndexProp.intValue = EditorGUILayout.Popup("Index", controllerIndexProp.intValue, indexNames);
|
|
}
|
|
|
|
private void DrawPropertySelector(SerializedProperty entryProp, GameObject targetObject, SerializedProperty propertyProp)
|
|
{
|
|
var options = new List<UXBindingProperty>(_supportedProperties);
|
|
UXBindingProperty current = (UXBindingProperty)propertyProp.enumValueIndex;
|
|
if (!options.Contains(current))
|
|
{
|
|
options.Add(current);
|
|
}
|
|
|
|
string[] displayNames = new string[options.Count];
|
|
int selectedIndex = 0;
|
|
for (int i = 0; i < options.Count; i++)
|
|
{
|
|
UXBindingProperty option = options[i];
|
|
UXBindingPropertyMetadata metadata = UXBindingPropertyUtility.GetMetadata(option);
|
|
bool supported = UXBindingPropertyUtility.IsSupported(targetObject, option);
|
|
displayNames[i] = supported ? metadata.DisplayName : $"{metadata.DisplayName} (Unsupported)";
|
|
if (option == current)
|
|
{
|
|
selectedIndex = i;
|
|
}
|
|
}
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
selectedIndex = EditorGUILayout.Popup("Property", selectedIndex, displayNames);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
propertyProp.enumValueIndex = (int)options[selectedIndex];
|
|
entryProp.FindPropertyRelative("_hasCapturedDefault").boolValue = false;
|
|
entryProp.FindPropertyRelative("_capturedProperty").enumValueIndex = propertyProp.enumValueIndex;
|
|
ResetValue(entryProp.FindPropertyRelative("_capturedDefault"));
|
|
ApplyDefaultFallbackForProperty(entryProp, (UXBindingProperty)propertyProp.enumValueIndex);
|
|
}
|
|
}
|
|
|
|
private void DrawValueField(SerializedProperty valueProp, UXBindingPropertyMetadata metadata, string label)
|
|
{
|
|
switch (metadata.ValueKind)
|
|
{
|
|
case UXBindingValueKind.Boolean:
|
|
{
|
|
SerializedProperty boolProp = valueProp.FindPropertyRelative("_boolValue");
|
|
boolProp.boolValue = EditorGUILayout.Toggle(label, boolProp.boolValue);
|
|
break;
|
|
}
|
|
case UXBindingValueKind.Float:
|
|
{
|
|
SerializedProperty floatProp = valueProp.FindPropertyRelative("_floatValue");
|
|
floatProp.floatValue = EditorGUILayout.FloatField(label, floatProp.floatValue);
|
|
break;
|
|
}
|
|
case UXBindingValueKind.String:
|
|
{
|
|
SerializedProperty stringProp = valueProp.FindPropertyRelative("_stringValue");
|
|
EditorGUILayout.LabelField(label);
|
|
stringProp.stringValue = EditorGUILayout.TextArea(stringProp.stringValue, GUILayout.MinHeight(54f));
|
|
break;
|
|
}
|
|
case UXBindingValueKind.Color:
|
|
{
|
|
SerializedProperty colorProp = valueProp.FindPropertyRelative("_colorValue");
|
|
colorProp.colorValue = EditorGUILayout.ColorField(label, colorProp.colorValue);
|
|
break;
|
|
}
|
|
case UXBindingValueKind.Vector2:
|
|
{
|
|
SerializedProperty vector2Prop = valueProp.FindPropertyRelative("_vector2Value");
|
|
vector2Prop.vector2Value = EditorGUILayout.Vector2Field(label, vector2Prop.vector2Value);
|
|
break;
|
|
}
|
|
case UXBindingValueKind.Vector3:
|
|
{
|
|
SerializedProperty vector3Prop = valueProp.FindPropertyRelative("_vector3Value");
|
|
vector3Prop.vector3Value = EditorGUILayout.Vector3Field(label, vector3Prop.vector3Value);
|
|
break;
|
|
}
|
|
case UXBindingValueKind.ObjectReference:
|
|
{
|
|
SerializedProperty objectProp = valueProp.FindPropertyRelative("_objectValue");
|
|
objectProp.objectReferenceValue = EditorGUILayout.ObjectField(
|
|
label,
|
|
objectProp.objectReferenceValue,
|
|
metadata.ObjectReferenceType,
|
|
false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddEntry(UXBinding binding)
|
|
{
|
|
int index = _entriesProp.arraySize;
|
|
_entriesProp.InsertArrayElementAtIndex(index);
|
|
|
|
SerializedProperty entryProp = _entriesProp.GetArrayElementAtIndex(index);
|
|
SerializedProperty controllerIdProp = entryProp.FindPropertyRelative("_controllerId");
|
|
SerializedProperty controllerIndexProp = entryProp.FindPropertyRelative("_controllerIndex");
|
|
SerializedProperty propertyProp = entryProp.FindPropertyRelative("_property");
|
|
SerializedProperty fallbackModeProp = entryProp.FindPropertyRelative("_fallbackMode");
|
|
SerializedProperty valueProp = entryProp.FindPropertyRelative("_value");
|
|
SerializedProperty fallbackValueProp = entryProp.FindPropertyRelative("_fallbackValue");
|
|
SerializedProperty capturedDefaultProp = entryProp.FindPropertyRelative("_capturedDefault");
|
|
SerializedProperty hasCapturedDefaultProp = entryProp.FindPropertyRelative("_hasCapturedDefault");
|
|
SerializedProperty capturedPropertyProp = entryProp.FindPropertyRelative("_capturedProperty");
|
|
|
|
controllerIdProp.stringValue = string.Empty;
|
|
controllerIndexProp.intValue = 0;
|
|
propertyProp.enumValueIndex = (int)UXBindingProperty.GameObjectActive;
|
|
fallbackModeProp.enumValueIndex = (int)UXBindingFallbackMode.RestoreCapturedDefault;
|
|
ResetValue(valueProp);
|
|
ResetValue(fallbackValueProp);
|
|
ResetValue(capturedDefaultProp);
|
|
hasCapturedDefaultProp.boolValue = false;
|
|
capturedPropertyProp.enumValueIndex = (int)UXBindingProperty.GameObjectActive;
|
|
ApplyDefaultFallbackForProperty(entryProp, UXBindingProperty.GameObjectActive);
|
|
|
|
if (binding.Controller != null && binding.Controller.Controllers.Count > 0)
|
|
{
|
|
controllerIdProp.stringValue = binding.Controller.Controllers[0].Id;
|
|
}
|
|
|
|
_foldouts[index] = true;
|
|
}
|
|
|
|
private static void ResetValue(SerializedProperty valueProp)
|
|
{
|
|
valueProp.FindPropertyRelative("_boolValue").boolValue = false;
|
|
valueProp.FindPropertyRelative("_floatValue").floatValue = 0f;
|
|
valueProp.FindPropertyRelative("_stringValue").stringValue = string.Empty;
|
|
valueProp.FindPropertyRelative("_colorValue").colorValue = Color.white;
|
|
valueProp.FindPropertyRelative("_vector2Value").vector2Value = Vector2.zero;
|
|
valueProp.FindPropertyRelative("_vector3Value").vector3Value = Vector3.zero;
|
|
valueProp.FindPropertyRelative("_objectValue").objectReferenceValue = null;
|
|
}
|
|
|
|
private void DeleteEntry(UXBinding binding, int index)
|
|
{
|
|
if (index < 0 || index >= _entriesProp.arraySize)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Undo.RecordObject(binding, "Delete UX Binding Rule");
|
|
_entriesProp.DeleteArrayElementAtIndex(index);
|
|
CleanupFoldouts(index);
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(binding);
|
|
GUIUtility.ExitGUI();
|
|
}
|
|
|
|
private void CleanupFoldouts(int removedIndex)
|
|
{
|
|
_foldouts.Remove(removedIndex);
|
|
|
|
var remapped = new Dictionary<int, bool>();
|
|
foreach (var pair in _foldouts)
|
|
{
|
|
int nextIndex = pair.Key > removedIndex ? pair.Key - 1 : pair.Key;
|
|
remapped[nextIndex] = pair.Value;
|
|
}
|
|
|
|
_foldouts.Clear();
|
|
foreach (var pair in remapped)
|
|
{
|
|
_foldouts[pair.Key] = pair.Value;
|
|
}
|
|
}
|
|
|
|
private static void ApplyDefaultFallbackForProperty(SerializedProperty entryProp, UXBindingProperty property)
|
|
{
|
|
SerializedProperty fallbackModeProp = entryProp.FindPropertyRelative("_fallbackMode");
|
|
SerializedProperty fallbackValueProp = entryProp.FindPropertyRelative("_fallbackValue");
|
|
|
|
if (property == UXBindingProperty.GameObjectActive)
|
|
{
|
|
fallbackModeProp.enumValueIndex = (int)UXBindingFallbackMode.UseCustomValue;
|
|
fallbackValueProp.FindPropertyRelative("_boolValue").boolValue = false;
|
|
return;
|
|
}
|
|
|
|
if ((UXBindingFallbackMode)fallbackModeProp.enumValueIndex == UXBindingFallbackMode.UseCustomValue)
|
|
{
|
|
fallbackModeProp.enumValueIndex = (int)UXBindingFallbackMode.RestoreCapturedDefault;
|
|
ResetValue(fallbackValueProp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|