private unsafe void AddActionItem(TreeViewItem parent, InputAction action, ref int id) { // Add item for action. var name = action.actionMap != null ? $"{action.actionMap.name}/{action.name}" : action.name; if (!action.enabled) { name += " (Disabled)"; } var item = AddChild(parent, name, ref id); // Grab state. var actionMap = action.GetOrCreateActionMap(); actionMap.ResolveBindingsIfNecessary(); var state = actionMap.m_State; // Add list of resolved controls. var actionIndex = action.m_ActionIndexInState; var totalBindingCount = state.totalBindingCount; for (var i = 0; i < totalBindingCount; ++i) { ref var bindingState = ref state.bindingStates[i]; if (bindingState.actionIndex != actionIndex) { continue; } if (bindingState.isComposite) { continue; } var binding = state.GetBinding(i); var controlCount = bindingState.controlCount; var controlStartIndex = bindingState.controlStartIndex; for (var n = 0; n < controlCount; ++n) { var control = state.controls[controlStartIndex + n]; var interactions = StringHelpers.Join(new[] { binding.effectiveInteractions, action.interactions }, ","); var text = control.path; if (!string.IsNullOrEmpty(interactions)) { var namesAndParameters = NameAndParameters.ParseMultiple(interactions); text += " ["; text += string.Join(",", namesAndParameters.Select(x => x.name)); text += "]"; } AddChild(item, text, ref id); } }
private void OnParametersChanged() { for (int i = 0; i < m_ParametersForEachListItem.Length; i++) { m_ParametersForEachListItem[i] = new NameAndParameters { name = m_ParametersForEachListItem[i].name, parameters = m_EditableParametersForEachListItem[i].GetParameters(), }; } m_Apply(); }
public new static CompositeBindingTreeItem AddTo(TreeViewItem parent, SerializedProperty bindingProperty) { var item = new CompositeBindingTreeItem(bindingProperty); item.depth = parent.depth + 1; item.displayName = !string.IsNullOrEmpty(item.name) ? item.name : ObjectNames.NicifyVariableName(NameAndParameters.ParseName(item.path)); parent.AddChild(item); return(item); }
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 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 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 (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); } }
protected NameAndParameterListView(SerializedProperty property, Action applyAction, string expectedControlLayout, TypeTable listOptions, Func <Type, Type> getValueType, string itemName) { m_ItemName = itemName; m_GetValueType = getValueType; m_DeleteButton = EditorGUIUtility.TrIconContent("Toolbar Minus", $"Delete {itemName}"); m_UpButton = EditorGUIUtility.TrIconContent(GUIHelpers.LoadIcon("ChevronUp"), $"Move {itemName} up"); m_DownButton = EditorGUIUtility.TrIconContent(GUIHelpers.LoadIcon("ChevronDown"), $"Move {itemName} down"); m_Property = property; m_Apply = applyAction; m_ListOptions = listOptions; m_ExpectedControlLayout = expectedControlLayout; if (!string.IsNullOrEmpty(m_ExpectedControlLayout)) { m_ExpectedValueType = EditorInputControlLayoutCache.GetValueType(m_ExpectedControlLayout); } m_ParametersForEachListItem = NameAndParameters.ParseMultiple(m_Property.stringValue).ToArray(); m_EditableParametersForEachListItem = new ParameterListView[m_ParametersForEachListItem.Length]; for (var i = 0; i < m_ParametersForEachListItem.Length; i++) { m_EditableParametersForEachListItem[i] = new ParameterListView { onChange = OnParametersChanged }; var typeName = m_ParametersForEachListItem[i].name; var rowType = m_ListOptions.LookupTypeRegistration(typeName); m_EditableParametersForEachListItem[i].Initialize(rowType, m_ParametersForEachListItem[i].parameters); var name = ObjectNames.NicifyVariableName(typeName); ////REVIEW: finding this kind of stuff should probably have better support globally on the asset; e.g. some //// notification that pops up and allows fixing all occurrences in one click // Find out if we still support this option and indicate it in the list, if we don't. if (rowType == null) { name += " (Obsolete)"; } else if (m_ExpectedValueType != null) { var valueType = getValueType(rowType); if (!m_ExpectedValueType.IsAssignableFrom(valueType)) { name += " (Ignored)"; } } m_EditableParametersForEachListItem[i].name = name; } }
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; }
public void Utilities_CanParseMultipleNameAndParameterLists() { Assert.That(NameAndParameters.ParseMultiple("a,b").Count(), Is.EqualTo(2)); Assert.That(NameAndParameters.ParseMultiple("a,b").ToArray()[0].name, Is.EqualTo("a")); Assert.That(NameAndParameters.ParseMultiple("a,b").ToArray()[0].parameters, Is.Empty); Assert.That(NameAndParameters.ParseMultiple("a,b").ToArray()[1].name, Is.EqualTo("b")); Assert.That(NameAndParameters.ParseMultiple("a,b").ToArray()[1].parameters, Is.Empty); Assert.That(NameAndParameters.ParseMultiple("a,b(r=1,t),c").Count(), Is.EqualTo(3)); Assert.That(NameAndParameters.ParseMultiple("a,b(r=1,t),c").ToArray()[0].name, Is.EqualTo("a")); Assert.That(NameAndParameters.ParseMultiple("a,b(r=1,t),c").ToArray()[0].parameters, Is.Empty); Assert.That(NameAndParameters.ParseMultiple("a,b(r=1,t),c").ToArray()[1].name, Is.EqualTo("b")); Assert.That(NameAndParameters.ParseMultiple("a,b(r=1,t),c").ToArray()[1].parameters, Has.Count.EqualTo(2)); Assert.That(NameAndParameters.ParseMultiple("a,b(r=1,t),c").ToArray()[2].name, Is.EqualTo("c")); Assert.That(NameAndParameters.ParseMultiple("a,b(r=1,t),c").ToArray()[2].parameters, Is.Empty); Assert.That(NameAndParameters.ParseMultiple("a(b,c=123)").Count(), Is.EqualTo(1)); }
public void Utilities_CanParseNameAndParameterList() { Assert.That(NameAndParameters.Parse("name()").name, Is.EqualTo("name")); Assert.That(NameAndParameters.Parse("name()").parameters, Is.Empty); Assert.That(NameAndParameters.Parse("name").name, Is.EqualTo("name")); Assert.That(NameAndParameters.Parse("name").parameters, Is.Empty); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").name, Is.EqualTo("Name")); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters, Has.Count.EqualTo(3)); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[0].name, Is.EqualTo("foo")); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[1].name, Is.EqualTo("Bar")); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[2].name, Is.EqualTo("blub")); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[0].type, Is.EqualTo(TypeCode.Boolean)); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[1].type, Is.EqualTo(TypeCode.Int32)); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[2].type, Is.EqualTo(TypeCode.Double)); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[0].value.ToBoolean(), Is.EqualTo(true)); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[1].value.ToInt32(), Is.EqualTo(123)); Assert.That(NameAndParameters.Parse("Name(foo,Bar=123,blub=234.56)").parameters[2].value.ToDouble(), Is.EqualTo(234.56).Within(0.0001)); }
public ModifyPopupWindow(SerializedProperty bindingProperty) { m_FlagsProperty = bindingProperty.FindPropertyRelative("m_Flags"); m_InteractionsProperty = bindingProperty.FindPropertyRelative("m_Interactions"); m_Flags = (InputBinding.Flags)m_FlagsProperty.intValue; var interactions = InputSystem.ListInteractions().ToList(); interactions.Sort(); m_InteractionChoices = interactions.Select(x => new GUIContent(x)).ToArray(); var interactionString = m_InteractionsProperty.stringValue; if (!string.IsNullOrEmpty(interactionString)) { m_Interactions = NameAndParameters.ParseMultiple(interactionString).ToArray(); } else { m_Interactions = new NameAndParameters[0]; } InitializeInteractionListView(); }
public static SerializedProperty ChangeCompositeBindingType(SerializedProperty bindingProperty, NameAndParameters nameAndParameters) { var bindingsArrayProperty = bindingProperty.GetArrayPropertyFromElement(); Debug.Assert(bindingsArrayProperty != null, "SerializedProperty is not an array of bindings"); var bindingIndex = bindingProperty.GetIndexOfArrayElement(); Debug.Assert(IsCompositeBinding(bindingProperty), $"Binding {bindingProperty.propertyPath} is not a composite"); // If the composite still has the default name, change it to the default // one for the new composite type. var pathProperty = bindingProperty.FindPropertyRelative("m_Path"); var nameProperty = bindingProperty.FindPropertyRelative("m_Name"); if (nameProperty.stringValue == ObjectNames.NicifyVariableName(NameAndParameters.Parse(pathProperty.stringValue).name)) { nameProperty.stringValue = ObjectNames.NicifyVariableName(nameAndParameters.name); } pathProperty.stringValue = nameAndParameters.ToString(); // Adjust part bindings if we have information on the registered composite. If we don't have // a type, we don't know about the parts. In that case, leave part bindings untouched. var compositeType = InputBindingComposite.s_Composites.LookupTypeRegistration(nameAndParameters.name); if (compositeType != null) { var actionName = bindingProperty.FindPropertyRelative("m_Action").stringValue; // Repurpose existing part bindings for the new composite or add any part bindings that // we're missing. var fields = compositeType.GetFields(BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance); var partIndex = 0; var partBindingsStartIndex = bindingIndex + 1; foreach (var field in fields) { // Skip fields that aren't marked with [InputControl] attribute. if (field.GetCustomAttribute <InputControlAttribute>(false) == null) { continue; } // See if we can reuse an existing part binding. SerializedProperty partProperty = null; if (partBindingsStartIndex + partIndex < bindingsArrayProperty.arraySize) { ////REVIEW: this should probably look up part bindings by name rather than going sequentially var element = bindingsArrayProperty.GetArrayElementAtIndex(partBindingsStartIndex + partIndex); if (((InputBinding.Flags)element.FindPropertyRelative("m_Flags").intValue & InputBinding.Flags.PartOfComposite) != 0) { partProperty = element; } } // If not, insert a new binding. if (partProperty == null) { partProperty = AddBindingToBindingArray(bindingsArrayProperty, partBindingsStartIndex + partIndex, flags: InputBinding.Flags.PartOfComposite); } // Initialize. partProperty.FindPropertyRelative("m_Name").stringValue = ObjectNames.NicifyVariableName(field.Name); partProperty.FindPropertyRelative("m_Action").stringValue = actionName; ++partIndex; } ////REVIEW: when we allow adding the same part multiple times, we may want to do something smarter here // Delete extraneous part bindings. while (partBindingsStartIndex + partIndex < bindingsArrayProperty.arraySize) { var element = bindingsArrayProperty.GetArrayElementAtIndex(partBindingsStartIndex + partIndex); if (((InputBinding.Flags)element.FindPropertyRelative("m_Flags").intValue & InputBinding.Flags.PartOfComposite) == 0) { break; } bindingsArrayProperty.DeleteArrayElementAtIndex(partBindingsStartIndex + partIndex); // No incrementing of partIndex. } } return(bindingProperty); }
////TODO: move this out into a general routine that can take a path and construct a display name private static GUIContent GetContentForPath(string path, string interactions, InputBinding.Flags flags) { const int kUsageNameGroup = 1; const int kDeviceNameGroup = 1; const int kDeviceUsageGroup = 3; const int kControlPathGroup = 4; ////TODO: nuke the regex stuff in here if (s_UsageRegex == null) { s_UsageRegex = new Regex("\\*/{([A-Za-z0-9]+)}"); } if (s_ControlRegex == null) { s_ControlRegex = new Regex("<([A-Za-z0-9:\\-]+)>({([A-Za-z0-9]+)})?/([A-Za-z0-9]+(/[A-Za-z0-9]+)*)"); } var text = path; var usageMatch = s_UsageRegex.Match(path); if (usageMatch.Success) { text = usageMatch.Groups[kUsageNameGroup].Value; } else { var controlMatch = s_ControlRegex.Match(path); if (controlMatch.Success) { var device = controlMatch.Groups[kDeviceNameGroup].Value; var deviceUsage = controlMatch.Groups[kDeviceUsageGroup].Value; var control = controlMatch.Groups[kControlPathGroup].Value; ////TODO: would be nice to include layout name to print something like "Gamepad A Button" instead of "Gamepad A" (or whatever) if (!string.IsNullOrEmpty(deviceUsage)) { text = $"{deviceUsage} {device} {control}"; } else { text = $"{device} {control}"; } } } ////REVIEW: would be nice to have icons for these // Show interactions. if (!string.IsNullOrEmpty(interactions)) { var interactionList = NameAndParameters.ParseMultiple(interactions); var interactionString = string.Join(" OR ", interactionList.Select(x => x.name).ToArray()); text = $"{interactionString} {text}"; } ////TODO: this looks ugly and not very obvious; find a better way // Show if linked with previous binding. if ((flags & InputBinding.Flags.ThisAndPreviousCombine) == InputBinding.Flags.ThisAndPreviousCombine) { text = "AND " + text; } return(new GUIContent(text)); }
public void Utilities_ParsingNameAndParameterList_RequiresStringToNotBeEmpty() { Assert.That(() => NameAndParameters.Parse("").name, Throws.Exception.With.Message.Contains("Expecting name")); }