com.alicizax.unity.ui.exten.../Editor/UX/Controller/UXBindingEditor.cs

833 lines
36 KiB
C#
Raw Normal View History

#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