833 lines
36 KiB
C#
833 lines
36 KiB
C#
#if UNITY_EDITOR
|
|
using System.Collections.Generic;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace UnityEngine.UI
|
|
{
|
|
[CustomEditor(typeof(UXBinding))]
|
|
public sealed class UXBindingEditor : UnityEditor.Editor
|
|
{
|
|
private readonly struct AddRuleOption
|
|
{
|
|
public readonly string ControllerId;
|
|
public readonly string ControllerName;
|
|
public readonly UXBindingProperty Property;
|
|
public readonly string PropertyName;
|
|
|
|
public AddRuleOption(string controllerId, string controllerName, UXBindingProperty property, string propertyName)
|
|
{
|
|
ControllerId = controllerId;
|
|
ControllerName = controllerName;
|
|
Property = property;
|
|
PropertyName = propertyName;
|
|
}
|
|
}
|
|
|
|
private sealed class AddRulePopup : PopupWindowContent
|
|
{
|
|
private readonly UXBindingEditor _editor;
|
|
private readonly UXBinding _binding;
|
|
private readonly List<AddRuleOption> _options;
|
|
private readonly bool _showControllerName;
|
|
private string _search = string.Empty;
|
|
private Vector2 _scroll;
|
|
|
|
public AddRulePopup(UXBindingEditor editor, UXBinding binding, List<AddRuleOption> options, bool showControllerName)
|
|
{
|
|
_editor = editor;
|
|
_binding = binding;
|
|
_options = new List<AddRuleOption>(options);
|
|
_showControllerName = showControllerName;
|
|
}
|
|
|
|
public override Vector2 GetWindowSize()
|
|
{
|
|
return new Vector2(360f, 320f);
|
|
}
|
|
|
|
public override void OnGUI(Rect rect)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.LabelField("Add Rule", EditorStyles.boldLabel);
|
|
EditorGUILayout.EndVertical();
|
|
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
_search = GUILayout.TextField(_search, GUI.skin.FindStyle("ToolbarSearchTextField") ?? EditorStyles.toolbarSearchField, GUILayout.ExpandWidth(true));
|
|
if (GUILayout.Button(string.Empty, GUI.skin.FindStyle("ToolbarSearchCancelButton") ?? EditorStyles.toolbarButton, GUILayout.Width(18f)))
|
|
{
|
|
_search = string.Empty;
|
|
GUI.FocusControl(null);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
_scroll = EditorGUILayout.BeginScrollView(_scroll);
|
|
for (int i = 0; i < _options.Count; i++)
|
|
{
|
|
AddRuleOption option = _options[i];
|
|
if (!IsMatch(option, _search))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string label = _showControllerName ? $"{option.ControllerName} / {option.PropertyName}" : option.PropertyName;
|
|
if (GUILayout.Button(label, EditorStyles.miniButton))
|
|
{
|
|
_editor.AddEntry(_binding, option.ControllerId, option.Property);
|
|
editorWindow.Close();
|
|
}
|
|
}
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
|
|
private static bool IsMatch(AddRuleOption option, string search)
|
|
{
|
|
if (string.IsNullOrEmpty(search))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return option.ControllerName.IndexOf(search, System.StringComparison.OrdinalIgnoreCase) >= 0 ||
|
|
option.PropertyName.IndexOf(search, System.StringComparison.OrdinalIgnoreCase) >= 0;
|
|
}
|
|
}
|
|
|
|
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 string[] _controllerNames = System.Array.Empty<string>();
|
|
private string[] _indexNames = System.Array.Empty<string>();
|
|
private GUIStyle _pillOn;
|
|
private GUIStyle _pillOff;
|
|
private readonly List<AddRuleOption> _addRuleOptions = new List<AddRuleOption>();
|
|
private GUIContent _addRuleContent;
|
|
private GUIContent _autoBindContent;
|
|
private GUIContent _captureContent;
|
|
private GUIContent _resetContent;
|
|
private GUIContent _expandAllContent;
|
|
private GUIContent _collapseAllContent;
|
|
private GUIContent _upContent;
|
|
private GUIContent _downContent;
|
|
private GUIContent _deleteContent;
|
|
|
|
private void OnEnable()
|
|
{
|
|
_controllerProp = serializedObject.FindProperty("_controller");
|
|
_entriesProp = serializedObject.FindProperty("_entries");
|
|
InitializeContents();
|
|
}
|
|
|
|
private void InitializeContents()
|
|
{
|
|
_addRuleContent = EditorGUIUtility.IconContent("Toolbar Plus", "Add binding rule");
|
|
_autoBindContent = EditorGUIUtility.IconContent("d_Prefab Icon", "Auto bind parent UXController");
|
|
_captureContent = EditorGUIUtility.IconContent("d_SaveAs", "Capture defaults");
|
|
_resetContent = EditorGUIUtility.IconContent("d_Refresh", "Reset to defaults");
|
|
_expandAllContent = EditorGUIUtility.IconContent("d_scrollup", "Expand all rules");
|
|
_collapseAllContent = EditorGUIUtility.IconContent("d_scrolldown", "Collapse all rules");
|
|
_upContent = EditorGUIUtility.IconContent("d_scrollup", "Move up");
|
|
_downContent = EditorGUIUtility.IconContent("d_scrolldown", "Move down");
|
|
_deleteContent = EditorGUIUtility.IconContent("TreeEditor.Trash", "Delete rule");
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
EnsureStyles();
|
|
|
|
var binding = (UXBinding)target;
|
|
|
|
DrawHeader(binding);
|
|
EditorGUILayout.Space(6f);
|
|
DrawEntries(binding);
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
private void DrawHeader(UXBinding binding)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("UX Binding", EditorStyles.boldLabel, GUILayout.Width(82f));
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
UXController newController = (UXController)EditorGUILayout.ObjectField(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.LabelField(binding.Controller.ControllerCount.ToString(), EditorStyles.miniLabel, GUILayout.Width(18f));
|
|
}
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
if (GUILayout.Button(_addRuleContent, EditorStyles.miniButtonLeft, GUILayout.Width(28f)))
|
|
{
|
|
ShowAddRuleMenu(binding, GUILayoutUtility.GetLastRect());
|
|
}
|
|
|
|
if (GUILayout.Button(_autoBindContent, EditorStyles.miniButtonMid, GUILayout.Width(28f)))
|
|
{
|
|
AutoBindController(binding);
|
|
}
|
|
|
|
if (GUILayout.Button(_captureContent, EditorStyles.miniButtonMid, GUILayout.Width(28f)))
|
|
{
|
|
binding.CaptureDefaults();
|
|
EditorUtility.SetDirty(binding);
|
|
}
|
|
|
|
if (GUILayout.Button(_resetContent, EditorStyles.miniButtonRight, GUILayout.Width(28f)))
|
|
{
|
|
binding.ResetToDefaults();
|
|
EditorUtility.SetDirty(binding);
|
|
SceneView.RepaintAll();
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
if (binding.Controller == null)
|
|
{
|
|
EditorGUILayout.HelpBox("Assign a UXController or use Auto Bind.", MessageType.Warning);
|
|
}
|
|
|
|
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 controllerIndexMaskProp = entryProp.FindPropertyRelative("_controllerIndexMask");
|
|
SerializedProperty propertyProp = entryProp.FindPropertyRelative("_property");
|
|
SerializedProperty valueProp = entryProp.FindPropertyRelative("_value");
|
|
SerializedProperty indexedValuesProp = entryProp.FindPropertyRelative("_indexedValues");
|
|
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;
|
|
bool propertySupported = _supportedProperties.Contains(property);
|
|
bool controllerResolved = TryGetControllerName(binding.Controller, controllerIdProp.stringValue, out string controllerName);
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.BeginHorizontal();
|
|
expanded = EditorGUILayout.Foldout(expanded, $"[{index}] {label}", true);
|
|
GUILayout.FlexibleSpace();
|
|
|
|
EditorGUI.BeginDisabledGroup(index == 0);
|
|
if (GUILayout.Button(_upContent, EditorStyles.miniButtonLeft, GUILayout.Width(24f)))
|
|
{
|
|
MoveEntry(index, index - 1);
|
|
}
|
|
EditorGUI.EndDisabledGroup();
|
|
|
|
EditorGUI.BeginDisabledGroup(index >= _entriesProp.arraySize - 1);
|
|
if (GUILayout.Button(_downContent, EditorStyles.miniButtonMid, GUILayout.Width(24f)))
|
|
{
|
|
MoveEntry(index, index + 1);
|
|
}
|
|
EditorGUI.EndDisabledGroup();
|
|
|
|
if (GUILayout.Button(_deleteContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
|
|
{
|
|
DeleteEntry(binding, index);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
_foldouts[index] = expanded;
|
|
|
|
if (expanded)
|
|
{
|
|
UXBindingPropertyMetadata metadata = UXBindingPropertyUtility.GetMetadata(property);
|
|
bool indexChanged = DrawControllerSelector(binding, controllerIdProp, controllerIndexProp, controllerIndexMaskProp, property == UXBindingProperty.GameObjectActive);
|
|
|
|
if (property == UXBindingProperty.GameObjectActive)
|
|
{
|
|
DrawGameObjectActiveHint(controllerIndexMaskProp.intValue);
|
|
ForceGameObjectActiveValues(valueProp, fallbackModeProp, fallbackValueProp);
|
|
if (indexChanged)
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
binding.ApplyEntryValue(index, GetFirstSelectedIndex(controllerIndexMaskProp.intValue));
|
|
EditorUtility.SetDirty(binding);
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int selectedIndex = GetFirstSelectedIndex(controllerIndexMaskProp.intValue);
|
|
if (indexChanged)
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
binding.ApplyEntryValue(index, selectedIndex);
|
|
EditorUtility.SetDirty(binding);
|
|
SceneView.RepaintAll();
|
|
}
|
|
|
|
EditorGUILayout.Space(2f);
|
|
EditorGUILayout.LabelField("Value", EditorStyles.boldLabel);
|
|
SerializedProperty selectedValueProp = GetIndexedValueProperty(indexedValuesProp, valueProp, selectedIndex);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
DrawValueField(selectedValueProp, metadata, $"Index {selectedIndex} Value");
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
binding.ApplyEntryValue(index, selectedIndex);
|
|
EditorUtility.SetDirty(binding);
|
|
SceneView.RepaintAll();
|
|
}
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Use Current"))
|
|
{
|
|
binding.CaptureEntryValue(index, selectedIndex);
|
|
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 (!controllerResolved)
|
|
{
|
|
EditorGUILayout.HelpBox("Controller reference is missing or points to a deleted controller definition.", MessageType.Error);
|
|
}
|
|
|
|
if (!propertySupported)
|
|
{
|
|
EditorGUILayout.HelpBox("This property is not supported by the components on the current GameObject.", MessageType.Error);
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
private bool DrawControllerSelector(UXBinding binding, SerializedProperty controllerIdProp, SerializedProperty controllerIndexProp, SerializedProperty controllerIndexMaskProp, bool multiSelect)
|
|
{
|
|
UXController controller = binding.Controller;
|
|
if (controller == null || controller.Controllers.Count == 0)
|
|
{
|
|
EditorGUILayout.HelpBox("Create a controller definition first.", MessageType.Info);
|
|
return false;
|
|
}
|
|
|
|
bool changed = false;
|
|
EditorGUILayout.BeginHorizontal();
|
|
EditorGUILayout.LabelField("Controller", EditorStyles.miniLabel, GUILayout.Width(64f));
|
|
EnsureStringArray(ref _controllerNames, controller.Controllers.Count);
|
|
int selectedController = 0;
|
|
|
|
for (int i = 0; i < controller.Controllers.Count; i++)
|
|
{
|
|
UXController.ControllerDefinition definition = controller.Controllers[i];
|
|
_controllerNames[i] = definition.Name;
|
|
if (definition.Id == controllerIdProp.stringValue)
|
|
{
|
|
selectedController = i;
|
|
}
|
|
}
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
selectedController = EditorGUILayout.Popup(selectedController, _controllerNames, GUILayout.MinWidth(90f));
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
controllerIdProp.stringValue = controller.Controllers[selectedController].Id;
|
|
controllerIndexProp.intValue = 0;
|
|
controllerIndexMaskProp.intValue = 1;
|
|
changed = true;
|
|
}
|
|
|
|
UXController.ControllerDefinition selectedDefinition = controller.Controllers[selectedController];
|
|
int maxIndex = Mathf.Max(1, selectedDefinition.Length);
|
|
controllerIndexProp.intValue = Mathf.Clamp(controllerIndexProp.intValue, 0, maxIndex - 1);
|
|
controllerIndexMaskProp.intValue = ClampMask(controllerIndexMaskProp.intValue, maxIndex, controllerIndexProp.intValue);
|
|
|
|
changed |= DrawIndexMask(controllerIndexMaskProp, controllerIndexProp, maxIndex, multiSelect);
|
|
EditorGUILayout.EndHorizontal();
|
|
return changed;
|
|
}
|
|
|
|
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, string controllerId, UXBindingProperty property)
|
|
{
|
|
int index = _entriesProp.arraySize;
|
|
_entriesProp.InsertArrayElementAtIndex(index);
|
|
|
|
SerializedProperty entryProp = _entriesProp.GetArrayElementAtIndex(index);
|
|
SerializedProperty controllerIdProp = entryProp.FindPropertyRelative("_controllerId");
|
|
SerializedProperty controllerIndexProp = entryProp.FindPropertyRelative("_controllerIndex");
|
|
SerializedProperty controllerIndexMaskProp = entryProp.FindPropertyRelative("_controllerIndexMask");
|
|
SerializedProperty propertyProp = entryProp.FindPropertyRelative("_property");
|
|
SerializedProperty fallbackModeProp = entryProp.FindPropertyRelative("_fallbackMode");
|
|
SerializedProperty valueProp = entryProp.FindPropertyRelative("_value");
|
|
SerializedProperty indexedValuesProp = entryProp.FindPropertyRelative("_indexedValues");
|
|
SerializedProperty fallbackValueProp = entryProp.FindPropertyRelative("_fallbackValue");
|
|
SerializedProperty capturedDefaultProp = entryProp.FindPropertyRelative("_capturedDefault");
|
|
SerializedProperty hasCapturedDefaultProp = entryProp.FindPropertyRelative("_hasCapturedDefault");
|
|
SerializedProperty capturedPropertyProp = entryProp.FindPropertyRelative("_capturedProperty");
|
|
|
|
controllerIdProp.stringValue = controllerId;
|
|
controllerIndexProp.intValue = 0;
|
|
controllerIndexMaskProp.intValue = 1;
|
|
propertyProp.enumValueIndex = (int)property;
|
|
fallbackModeProp.enumValueIndex = (int)UXBindingFallbackMode.RestoreCapturedDefault;
|
|
ResetValue(valueProp);
|
|
indexedValuesProp.ClearArray();
|
|
ResetValue(fallbackValueProp);
|
|
ResetValue(capturedDefaultProp);
|
|
hasCapturedDefaultProp.boolValue = false;
|
|
capturedPropertyProp.enumValueIndex = (int)property;
|
|
ApplyDefaultFallbackForProperty(entryProp, property);
|
|
|
|
_foldouts[index] = true;
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(binding);
|
|
}
|
|
|
|
private void ShowAddRuleMenu(UXBinding binding, Rect activatorRect)
|
|
{
|
|
if (binding.Controller == null || binding.Controller.Controllers.Count == 0)
|
|
{
|
|
EditorUtility.DisplayDialog("Add UX Binding Rule", "Assign a UXController before adding rules.", "OK");
|
|
return;
|
|
}
|
|
|
|
UXBindingPropertyUtility.GetSupportedProperties(binding.gameObject, _supportedProperties);
|
|
_addRuleOptions.Clear();
|
|
|
|
for (int controllerIndex = 0; controllerIndex < binding.Controller.Controllers.Count; controllerIndex++)
|
|
{
|
|
UXController.ControllerDefinition controller = binding.Controller.Controllers[controllerIndex];
|
|
if (controller == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (int propertyIndex = 0; propertyIndex < _supportedProperties.Count; propertyIndex++)
|
|
{
|
|
UXBindingProperty property = _supportedProperties[propertyIndex];
|
|
if (HasRule(controller.Id, property))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UXBindingPropertyMetadata metadata = UXBindingPropertyUtility.GetMetadata(property);
|
|
_addRuleOptions.Add(new AddRuleOption(controller.Id, controller.Name, property, metadata.DisplayName));
|
|
}
|
|
}
|
|
|
|
if (_addRuleOptions.Count == 0)
|
|
{
|
|
EditorUtility.DisplayDialog("Add UX Binding Rule", "All supported states already exist.", "OK");
|
|
return;
|
|
}
|
|
|
|
PopupWindow.Show(activatorRect, new AddRulePopup(this, binding, _addRuleOptions, binding.Controller.Controllers.Count > 1));
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (!EditorUtility.DisplayDialog(
|
|
"Delete UX Binding Rule",
|
|
$"Delete binding rule {index}? This cannot be undone outside Unity Undo.",
|
|
"Delete",
|
|
"Cancel"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Undo.RecordObject(binding, "Delete UX Binding Rule");
|
|
_entriesProp.DeleteArrayElementAtIndex(index);
|
|
CleanupFoldouts(index);
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(binding);
|
|
GUIUtility.ExitGUI();
|
|
}
|
|
|
|
private void MoveEntry(int fromIndex, int toIndex)
|
|
{
|
|
if (fromIndex < 0 || toIndex < 0 || fromIndex >= _entriesProp.arraySize || toIndex >= _entriesProp.arraySize)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_entriesProp.MoveArrayElement(fromIndex, toIndex);
|
|
bool fromExpanded = _foldouts.ContainsKey(fromIndex) && _foldouts[fromIndex];
|
|
bool toExpanded = _foldouts.ContainsKey(toIndex) && _foldouts[toIndex];
|
|
_foldouts[fromIndex] = toExpanded;
|
|
_foldouts[toIndex] = fromExpanded;
|
|
serializedObject.ApplyModifiedProperties();
|
|
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);
|
|
}
|
|
}
|
|
|
|
private void AutoBindController(UXBinding binding)
|
|
{
|
|
UXController controller = binding.GetComponentInParent<UXController>();
|
|
if (controller == null)
|
|
{
|
|
EditorUtility.DisplayDialog("Auto Bind UX Controller", "No UXController found in parents.", "OK");
|
|
return;
|
|
}
|
|
|
|
Undo.RecordObject(binding, "Auto Bind UX Controller");
|
|
binding.SetController(controller);
|
|
_controllerProp.objectReferenceValue = controller;
|
|
serializedObject.ApplyModifiedProperties();
|
|
EditorUtility.SetDirty(binding);
|
|
}
|
|
|
|
private void EnsureStyles()
|
|
{
|
|
if (_pillOn != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_pillOn = new GUIStyle(EditorStyles.miniButton)
|
|
{
|
|
fontStyle = FontStyle.Bold,
|
|
fixedHeight = 18f,
|
|
margin = new RectOffset(1, 1, 1, 1),
|
|
padding = new RectOffset(2, 2, 1, 1)
|
|
};
|
|
_pillOff = new GUIStyle(EditorStyles.miniButton)
|
|
{
|
|
fixedHeight = 18f,
|
|
margin = new RectOffset(1, 1, 1, 1),
|
|
padding = new RectOffset(2, 2, 1, 1)
|
|
};
|
|
}
|
|
|
|
private bool DrawIndexMask(SerializedProperty maskProp, SerializedProperty indexProp, int length, bool multiSelect)
|
|
{
|
|
EditorGUILayout.LabelField(multiSelect ? "Active" : "Index", EditorStyles.miniLabel, GUILayout.Width(38f));
|
|
int mask = maskProp.intValue;
|
|
int originalMask = mask;
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
int bit = UXBinding.BindingEntry.IndexToMask(i);
|
|
bool selected = (mask & bit) != 0;
|
|
bool nextSelected = GUILayout.Toggle(selected, i.ToString(), selected ? _pillOn : _pillOff, GUILayout.Width(26f));
|
|
if (nextSelected != selected)
|
|
{
|
|
if (multiSelect)
|
|
{
|
|
if (nextSelected)
|
|
{
|
|
mask |= bit;
|
|
}
|
|
else
|
|
{
|
|
mask &= ~bit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mask = bit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mask == 0)
|
|
{
|
|
mask = UXBinding.BindingEntry.IndexToMask(Mathf.Clamp(indexProp.intValue, 0, length - 1));
|
|
}
|
|
|
|
maskProp.intValue = ClampMask(mask, length, indexProp.intValue);
|
|
indexProp.intValue = GetFirstSelectedIndex(maskProp.intValue);
|
|
return maskProp.intValue != originalMask;
|
|
}
|
|
|
|
private static SerializedProperty GetIndexedValueProperty(SerializedProperty indexedValuesProp, SerializedProperty fallbackValueProp, int selectedIndex)
|
|
{
|
|
for (int i = 0; i < indexedValuesProp.arraySize; i++)
|
|
{
|
|
SerializedProperty indexedValueProp = indexedValuesProp.GetArrayElementAtIndex(i);
|
|
if (indexedValueProp.FindPropertyRelative("_index").intValue == selectedIndex)
|
|
{
|
|
return indexedValueProp.FindPropertyRelative("_value");
|
|
}
|
|
}
|
|
|
|
int nextIndex = indexedValuesProp.arraySize;
|
|
indexedValuesProp.InsertArrayElementAtIndex(nextIndex);
|
|
SerializedProperty nextValueProp = indexedValuesProp.GetArrayElementAtIndex(nextIndex);
|
|
nextValueProp.FindPropertyRelative("_index").intValue = selectedIndex;
|
|
CopyValue(fallbackValueProp, nextValueProp.FindPropertyRelative("_value"));
|
|
return nextValueProp.FindPropertyRelative("_value");
|
|
}
|
|
|
|
private static void CopyValue(SerializedProperty source, SerializedProperty destination)
|
|
{
|
|
destination.FindPropertyRelative("_boolValue").boolValue = source.FindPropertyRelative("_boolValue").boolValue;
|
|
destination.FindPropertyRelative("_floatValue").floatValue = source.FindPropertyRelative("_floatValue").floatValue;
|
|
destination.FindPropertyRelative("_stringValue").stringValue = source.FindPropertyRelative("_stringValue").stringValue;
|
|
destination.FindPropertyRelative("_colorValue").colorValue = source.FindPropertyRelative("_colorValue").colorValue;
|
|
destination.FindPropertyRelative("_vector2Value").vector2Value = source.FindPropertyRelative("_vector2Value").vector2Value;
|
|
destination.FindPropertyRelative("_vector3Value").vector3Value = source.FindPropertyRelative("_vector3Value").vector3Value;
|
|
destination.FindPropertyRelative("_objectValue").objectReferenceValue = source.FindPropertyRelative("_objectValue").objectReferenceValue;
|
|
}
|
|
|
|
private static void DrawGameObjectActiveHint(int mask)
|
|
{
|
|
EditorGUILayout.LabelField($"Visible: {BuildIndexLabel(mask)} Hidden: others", EditorStyles.miniLabel);
|
|
}
|
|
|
|
private static void ForceGameObjectActiveValues(SerializedProperty valueProp, SerializedProperty fallbackModeProp, SerializedProperty fallbackValueProp)
|
|
{
|
|
valueProp.FindPropertyRelative("_boolValue").boolValue = true;
|
|
fallbackModeProp.enumValueIndex = (int)UXBindingFallbackMode.UseCustomValue;
|
|
fallbackValueProp.FindPropertyRelative("_boolValue").boolValue = false;
|
|
}
|
|
|
|
private bool HasRule(string controllerId, UXBindingProperty property)
|
|
{
|
|
for (int i = 0; i < _entriesProp.arraySize; i++)
|
|
{
|
|
SerializedProperty entry = _entriesProp.GetArrayElementAtIndex(i);
|
|
if (entry.FindPropertyRelative("_controllerId").stringValue == controllerId &&
|
|
entry.FindPropertyRelative("_property").enumValueIndex == (int)property)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void SetAllFoldouts(bool expanded)
|
|
{
|
|
for (int i = 0; i < _entriesProp.arraySize; i++)
|
|
{
|
|
_foldouts[i] = expanded;
|
|
}
|
|
}
|
|
|
|
private static bool TryGetControllerName(UXController controller, string controllerId, out string controllerName)
|
|
{
|
|
controllerName = string.Empty;
|
|
if (controller == null || string.IsNullOrEmpty(controllerId))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < controller.Controllers.Count; i++)
|
|
{
|
|
UXController.ControllerDefinition definition = controller.Controllers[i];
|
|
if (definition != null && definition.Id == controllerId)
|
|
{
|
|
controllerName = definition.Name;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static string BuildIndexLabel(int mask)
|
|
{
|
|
if (mask == 0)
|
|
{
|
|
return "0";
|
|
}
|
|
|
|
string label = string.Empty;
|
|
for (int i = 0; i < 31; i++)
|
|
{
|
|
if ((mask & UXBinding.BindingEntry.IndexToMask(i)) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
label = string.IsNullOrEmpty(label) ? i.ToString() : $"{label},{i}";
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
private static int ClampMask(int mask, int length, int fallbackIndex)
|
|
{
|
|
int validMask = 0;
|
|
int max = Mathf.Min(length, 31);
|
|
for (int i = 0; i < max; i++)
|
|
{
|
|
validMask |= UXBinding.BindingEntry.IndexToMask(i);
|
|
}
|
|
|
|
mask &= validMask;
|
|
if (mask == 0)
|
|
{
|
|
mask = UXBinding.BindingEntry.IndexToMask(Mathf.Clamp(fallbackIndex, 0, max - 1));
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
private static int GetFirstSelectedIndex(int mask)
|
|
{
|
|
for (int i = 0; i < 31; i++)
|
|
{
|
|
if ((mask & UXBinding.BindingEntry.IndexToMask(i)) != 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private static void EnsureStringArray(ref string[] array, int length)
|
|
{
|
|
if (array.Length != length)
|
|
{
|
|
array = new string[length];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|