File size: 14,835 Bytes
18a519f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | #if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine.InputSystem.Editor.Lists;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
////REVIEW: when we start with a blank tree view state, we should initialize the control picker to select the control currently
//// selected by the path property
namespace UnityEngine.InputSystem.Editor
{
/// <summary>
/// UI for editing properties of an <see cref="InputBinding"/>. Right-most pane in action editor when
/// binding is selected in middle pane.
/// </summary>
internal class InputBindingPropertiesView : PropertiesViewBase, IDisposable
{
public static FourCC k_GroupsChanged => new FourCC("GRPS");
public static FourCC k_PathChanged => new FourCC("PATH");
public static FourCC k_CompositeTypeChanged => new FourCC("COMP");
public static FourCC k_CompositePartAssignmentChanged => new FourCC("PART");
public InputBindingPropertiesView(
SerializedProperty bindingProperty,
Action<FourCC> onChange = null,
InputControlPickerState controlPickerState = null,
string expectedControlLayout = null,
ReadOnlyArray<InputControlScheme> controlSchemes = new ReadOnlyArray<InputControlScheme>(),
IEnumerable<string> controlPathsToMatch = null)
: base(InputActionSerializationHelpers.IsCompositeBinding(bindingProperty) ? "Composite" : "Binding",
bindingProperty, onChange, expectedControlLayout)
{
m_BindingProperty = bindingProperty;
m_GroupsProperty = bindingProperty.FindPropertyRelative("m_Groups");
m_PathProperty = bindingProperty.FindPropertyRelative("m_Path");
m_BindingGroups = m_GroupsProperty.stringValue
.Split(new[] {InputBinding.Separator}, StringSplitOptions.RemoveEmptyEntries).ToList();
m_ExpectedControlLayout = expectedControlLayout;
m_ControlSchemes = controlSchemes;
var flags = (InputBinding.Flags)bindingProperty.FindPropertyRelative("m_Flags").intValue;
m_IsPartOfComposite = (flags & InputBinding.Flags.PartOfComposite) != 0;
m_IsComposite = (flags & InputBinding.Flags.Composite) != 0;
// Set up control picker for m_Path. Not needed if the binding is a composite.
if (!m_IsComposite)
{
m_ControlPickerState = controlPickerState ?? new InputControlPickerState();
m_ControlPathEditor = new InputControlPathEditor(m_PathProperty, m_ControlPickerState, OnPathChanged);
m_ControlPathEditor.SetExpectedControlLayout(m_ExpectedControlLayout);
if (controlPathsToMatch != null)
m_ControlPathEditor.SetControlPathsToMatch(controlPathsToMatch);
}
}
public void Dispose()
{
m_ControlPathEditor?.Dispose();
}
protected override void DrawGeneralProperties()
{
var currentPath = m_PathProperty.stringValue;
InputSystem.OnDrawCustomWarningForBindingPath(currentPath);
if (m_IsComposite)
{
if (m_CompositeParameters == null)
InitializeCompositeProperties();
// Composite type dropdown.
var selectedCompositeType = EditorGUILayout.Popup(s_CompositeTypeLabel, m_SelectedCompositeType, m_CompositeTypeOptions);
if (selectedCompositeType != m_SelectedCompositeType)
{
m_SelectedCompositeType = selectedCompositeType;
OnCompositeTypeChanged();
}
// Composite parameters.
m_CompositeParameters.OnGUI();
}
else
{
// Path.
m_ControlPathEditor.OnGUI();
// Composite part.
if (m_IsPartOfComposite)
{
if (m_CompositeParts == null)
InitializeCompositePartProperties();
var selectedPart = EditorGUILayout.Popup(s_CompositePartAssignmentLabel, m_SelectedCompositePart,
m_CompositePartOptions);
if (selectedPart != m_SelectedCompositePart)
{
m_SelectedCompositePart = selectedPart;
OnCompositePartAssignmentChanged();
}
}
// Control scheme matrix.
DrawUseInControlSchemes();
}
}
/// <summary>
/// Draw control scheme matrix that allows selecting which control schemes a particular
/// binding appears in.
/// </summary>
private void DrawUseInControlSchemes()
{
if (m_ControlSchemes.Count <= 0)
return;
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField(s_UseInControlSchemesLAbel, EditorStyles.boldLabel);
EditorGUILayout.BeginVertical();
foreach (var scheme in m_ControlSchemes)
{
EditorGUI.BeginChangeCheck();
var result = EditorGUILayout.Toggle(scheme.name, m_BindingGroups.Contains(scheme.bindingGroup));
if (EditorGUI.EndChangeCheck())
{
if (result)
{
m_BindingGroups.Add(scheme.bindingGroup);
}
else
{
m_BindingGroups.Remove(scheme.bindingGroup);
}
OnBindingGroupsChanged();
}
}
EditorGUILayout.EndVertical();
}
private void InitializeCompositeProperties()
{
// Find name of current composite.
var path = m_PathProperty.stringValue;
var compositeNameAndParameters = NameAndParameters.Parse(path);
var compositeName = compositeNameAndParameters.name;
var compositeType = InputBindingComposite.s_Composites.LookupTypeRegistration(compositeName);
// Collect all possible composite types.
var selectedCompositeIndex = -1;
var compositeTypeOptionsList = new List<GUIContent>();
var compositeTypeList = new List<string>();
var currentIndex = 0;
foreach (var composite in InputBindingComposite.s_Composites.internedNames.Where(x =>
!InputBindingComposite.s_Composites.aliases.Contains(x)).OrderBy(x => x))
{
if (!string.IsNullOrEmpty(m_ExpectedControlLayout))
{
var valueType = InputBindingComposite.GetValueType(composite);
if (valueType != null &&
!InputControlLayout.s_Layouts.ValueTypeIsAssignableFrom(
new InternedString(m_ExpectedControlLayout), valueType))
continue;
}
if (InputBindingComposite.s_Composites.LookupTypeRegistration(composite) == compositeType)
selectedCompositeIndex = currentIndex;
var name = ObjectNames.NicifyVariableName(composite);
compositeTypeOptionsList.Add(new GUIContent(name));
compositeTypeList.Add(composite);
++currentIndex;
}
// If the current composite type isn't a registered type, add it to the list as
// an extra option.
if (selectedCompositeIndex == -1)
{
selectedCompositeIndex = compositeTypeList.Count;
compositeTypeOptionsList.Add(new GUIContent(ObjectNames.NicifyVariableName(compositeName)));
compositeTypeList.Add(compositeName);
}
m_CompositeTypes = compositeTypeList.ToArray();
m_CompositeTypeOptions = compositeTypeOptionsList.ToArray();
m_SelectedCompositeType = selectedCompositeIndex;
// Initialize parameters.
m_CompositeParameters = new ParameterListView
{
onChange = OnCompositeParametersModified
};
if (compositeType != null)
m_CompositeParameters.Initialize(compositeType, compositeNameAndParameters.parameters);
}
private void InitializeCompositePartProperties()
{
var currentCompositePart = m_BindingProperty.FindPropertyRelative("m_Name").stringValue;
////REVIEW: this makes a lot of assumptions about the serialized data based on the one property we've been given in the ctor
// Determine the name of the current composite type that the part belongs to.
var bindingArrayProperty = m_BindingProperty.GetArrayPropertyFromElement();
var partBindingIndex = InputActionSerializationHelpers.GetIndex(bindingArrayProperty, m_BindingProperty);
var compositeBindingIndex =
InputActionSerializationHelpers.GetCompositeStartIndex(bindingArrayProperty, partBindingIndex);
if (compositeBindingIndex == -1)
return;
var compositeBindingProperty = bindingArrayProperty.GetArrayElementAtIndex(compositeBindingIndex);
var compositePath = compositeBindingProperty.FindPropertyRelative("m_Path").stringValue;
var compositeNameAndParameters = NameAndParameters.Parse(compositePath);
// Initialize option list from all parts available for the composite.
var optionList = new List<GUIContent>();
var nameList = new List<string>();
var currentIndex = 0;
var selectedPartNameIndex = -1;
foreach (var partName in InputBindingComposite.GetPartNames(compositeNameAndParameters.name))
{
if (partName.Equals(currentCompositePart, StringComparison.InvariantCultureIgnoreCase))
selectedPartNameIndex = currentIndex;
var niceName = ObjectNames.NicifyVariableName(partName);
optionList.Add(new GUIContent(niceName));
nameList.Add(partName);
++currentIndex;
}
// If currently selected part is not in list, add it as an option.
if (selectedPartNameIndex == -1)
{
selectedPartNameIndex = nameList.Count;
optionList.Add(new GUIContent(ObjectNames.NicifyVariableName(currentCompositePart)));
nameList.Add(currentCompositePart);
}
m_CompositeParts = nameList.ToArray();
m_CompositePartOptions = optionList.ToArray();
m_SelectedCompositePart = selectedPartNameIndex;
}
private void OnCompositeParametersModified()
{
Debug.Assert(m_CompositeParameters != null);
var path = m_PathProperty.stringValue;
var nameAndParameters = NameAndParameters.Parse(path);
nameAndParameters.parameters = m_CompositeParameters.GetParameters();
m_PathProperty.stringValue = nameAndParameters.ToString();
m_PathProperty.serializedObject.ApplyModifiedProperties();
OnPathChanged();
}
private void OnBindingGroupsChanged()
{
m_GroupsProperty.stringValue = string.Join(InputBinding.kSeparatorString, m_BindingGroups.ToArray());
m_GroupsProperty.serializedObject.ApplyModifiedProperties();
onChange?.Invoke(k_GroupsChanged);
}
private void OnPathChanged()
{
m_BindingProperty.serializedObject.ApplyModifiedProperties();
onChange?.Invoke(k_PathChanged);
}
private void OnCompositeTypeChanged()
{
var nameAndParameters = new NameAndParameters
{
name = m_CompositeTypes[m_SelectedCompositeType],
parameters = m_CompositeParameters.GetParameters()
};
InputActionSerializationHelpers.ChangeCompositeBindingType(m_BindingProperty, nameAndParameters);
m_PathProperty.serializedObject.ApplyModifiedProperties();
onChange?.Invoke(k_CompositeTypeChanged);
}
private void OnCompositePartAssignmentChanged()
{
m_BindingProperty.FindPropertyRelative("m_Name").stringValue = m_CompositeParts[m_SelectedCompositePart];
m_BindingProperty.serializedObject.ApplyModifiedProperties();
onChange?.Invoke(k_CompositePartAssignmentChanged);
}
private readonly bool m_IsComposite;
private ParameterListView m_CompositeParameters;
private int m_SelectedCompositeType;
private GUIContent[] m_CompositeTypeOptions;
private string[] m_CompositeTypes;
private int m_SelectedCompositePart;
private GUIContent[] m_CompositePartOptions;
private string[] m_CompositeParts;
private readonly SerializedProperty m_GroupsProperty;
private readonly SerializedProperty m_BindingProperty;
private readonly SerializedProperty m_PathProperty;
private readonly InputControlPickerState m_ControlPickerState;
private readonly InputControlPathEditor m_ControlPathEditor;
private static readonly GUIContent s_CompositeTypeLabel = EditorGUIUtility.TrTextContent("Composite Type",
"Type of composite. Allows changing the composite type retroactively. Doing so will modify the bindings that are part of the composite.");
private static readonly GUIContent s_UseInControlSchemesLAbel = EditorGUIUtility.TrTextContent("Use in control scheme",
"In which control schemes the binding is active. A binding can be used by arbitrary many control schemes. If a binding is not "
+ "assigned to a specific control schemes, it is active in all of them.");
private static readonly GUIContent s_CompositePartAssignmentLabel = EditorGUIUtility.TrTextContent(
"Composite Part",
"The named part of the composite that the binding is assigned to. Multiple bindings may be assigned the same part. All controls from "
+ "all bindings that are assigned the same part will collectively feed values into that part of the composite.");
private ReadOnlyArray<InputControlScheme> m_ControlSchemes;
private readonly List<string> m_BindingGroups;
private readonly string m_ExpectedControlLayout;
}
}
#endif // UNITY_EDITOR
|