From d0d8ad88cdc55c6a89b2d50044c579c46ea8cf05 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Thu, 13 Apr 2023 18:11:32 -0700 Subject: [PATCH 01/32] Checking-Pointing progress for specifying input usages in editor, as well as listing out the specific device paths that match to a binding --- .../InputSystem/Controls/InputControlPath.cs | 36 ++++++++++ .../AssetEditor/InputBindingPropertiesView.cs | 67 +++++++++++++++++++ .../ControlPicker/InputControlDropdownItem.cs | 7 +- .../InputControlPickerDropdown.cs | 25 +++++-- 4 files changed, 128 insertions(+), 7 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 5cbfa4a41c..45af3110b9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -720,6 +720,42 @@ public static bool Matches(string expected, InputControl control) return MatchesRecursive(ref parser, control); } + internal static bool Matches(IEnumerable usages, string name, ref InputControlLayout.ControlItem controlItem) + { + if (usages.Count() > 0) + { + // All of usages should match to the one of usage in the control + foreach (var usage in usages) + { + if (usage.Length > 0) + { + var usageCount = controlItem.usages.Count; + var anyUsageMatches = false; + for (var i = 0; i < usageCount; ++i) + { + if (StringMatches(usage, controlItem.usages[i])) + { + anyUsageMatches = true; + break; + } + } + + if (!anyUsageMatches) + return false; + } + } + } + + // Match name. + if (name.Length > 0) + { + if (!StringMatches(name, controlItem.name)) + return false; + } + + return true; + } + /// /// Check whether the given path matches or any of its parents. /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index d8de344c92..4f52e9594a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using UnityEditor; using UnityEngine.InputSystem.Editor.Lists; using UnityEngine.InputSystem.Layouts; @@ -61,6 +62,7 @@ public void Dispose() m_ControlPathEditor?.Dispose(); } + bool showPaths = false; protected override void DrawGeneralProperties() { var currentPath = m_PathProperty.stringValue; @@ -102,11 +104,76 @@ protected override void DrawGeneralProperties() } } + showPaths = EditorGUILayout.Toggle("Show full paths", showPaths); + // Show the specific layouts that implement the control on this path + if (showPaths) + { + // Control scheme matrix. + DrawFullPathInfo(); + } // Control scheme matrix. DrawUseInControlSchemes(); } } + /// + /// Draw control scheme matrix that allows selecting which control schemes a particular + /// binding appears in. + /// + private void DrawFullPathInfo() + { + + var path = m_ControlPathEditor.pathProperty.stringValue; + var layout = InputControlPath.TryGetDeviceLayout(path); + var parsedPath = InputControlPath.Parse(path).ToArray(); + + // Try to find all relevant full paths + if(parsedPath.Length == 2) + { + var usages = parsedPath[1].usages; + var name = parsedPath[1].name; + + EditorGUILayout.BeginVertical(); + DrawPathInfoForLayout(new InternedString(layout), usages, name); + EditorGUILayout.EndVertical(); + } + // Otherwise skip + } + + private void DrawPathInfoForLayout(InternedString rootLayout, IEnumerable usages, string name) + { + var path = m_ControlPathEditor.pathProperty.stringValue; + var childLayouts = EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.baseLayouts.Contains(rootLayout)).OrderBy(x => x.displayName); + + if (rootLayout == InputControlPath.Wildcard) + { + childLayouts = EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); + } + + if (childLayouts.Count() > 0) + { + foreach (var childLayout in childLayouts) + { + for(int i = 0; i < childLayout.m_Controls.Length;i++) + { + if (InputControlPath.Matches(usages, name, ref childLayout.m_Controls[i])) + { + EditorGUILayout.LabelField(childLayout.displayName + "/" + childLayout.m_Controls[i].displayName); + continue; + } + } + + EditorGUI.indentLevel++; + DrawPathInfoForLayout(childLayout.name, usages, name); + EditorGUI.indentLevel--; + } + } + } + + + /// /// Draw control scheme matrix that allows selecting which control schemes a particular /// binding appears in. diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs index f473cdb830..db68570cdb 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs @@ -72,12 +72,13 @@ public OptionalControlDropdownItem(EditorInputControlLayoutCache.OptionalControl internal sealed class UsageDropdownItem : InputControlDropdownItem { - public override string controlPathWithDevice => $"{m_Device}/{{{m_ControlPath}}}"; + public override string controlPathWithDevice => string.IsNullOrEmpty(m_Device) ? $"*/{{{m_ControlPath}}}" + : $"<{m_Device}>/{{{m_ControlPath}}}"; - public UsageDropdownItem(string usage) + public UsageDropdownItem(string usage, string device) : base(usage) { - m_Device = "*"; + m_Device = device; m_ControlPath = usage; id = controlPathWithDevice.GetHashCode(); m_Searchable = true; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs index 27aeb10d5a..20a640c168 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs @@ -81,7 +81,7 @@ protected override AdvancedDropdownItem BuildRoot() // Usages. if (m_Mode != InputControlPicker.Mode.PickDevice) { - var usages = BuildTreeForUsages(); + var usages = BuildTreeForControlUsages(); if (usages.children.Any()) { root.AddChild(usages); @@ -124,14 +124,14 @@ protected override void ItemSelected(AdvancedDropdownItem item) m_OnPickCallback(path); } - private AdvancedDropdownItem BuildTreeForUsages() + private AdvancedDropdownItem BuildTreeForControlUsages(string device = "") { var usageRoot = new AdvancedDropdownItem("Usages"); foreach (var usageAndLayouts in EditorInputControlLayoutCache.allUsages) { if (usageAndLayouts.Item2.Any(LayoutMatchesExpectedControlLayoutFilter)) { - var child = new UsageDropdownItem(usageAndLayouts.Item1); + var child = new UsageDropdownItem(usageAndLayouts.Item1, device); usageRoot.AddChild(child); } } @@ -183,12 +183,29 @@ private void AddDeviceTreeItemRecursive(InputControlLayout layout, AdvancedDropd var defaultControlPickerLayout = new DefaultInputControlPickerLayout(); - // Add common usage variants. + // Add control usages for the device + var deviceControlUsages = BuildTreeForControlUsages(layout.name); + if (deviceControlUsages.children.Any()) + { + deviceItem.AddChild(deviceControlUsages); + deviceItem.AddSeparator(); + } + + // Add common device usage variants. if (layout.commonUsages.Count > 0) { foreach (var usage in layout.commonUsages) { var usageItem = new DeviceDropdownItem(layout, usage); + + // Add control usages for the device sub-variant + var deviceVariantControlUsages = BuildTreeForControlUsages(usageItem.name); + if (deviceVariantControlUsages.children.Any()) + { + usageItem.AddChild(deviceVariantControlUsages); + usageItem.AddSeparator(); + } + if (m_Mode == InputControlPicker.Mode.PickControl) AddControlTreeItemsRecursive(defaultControlPickerLayout, layout, usageItem, layout.name, usage, searchable); deviceItem.AddChild(usageItem); From afca0e1ca7b92c7e9fd19c711b2e19380b577079 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 14 Apr 2023 12:18:56 -0700 Subject: [PATCH 02/32] Style update, optimizing performance a bit --- .../InputSystem/Controls/InputControlPath.cs | 33 +++++++-------- .../AssetEditor/InputBindingPropertiesView.cs | 42 +++++++++---------- .../ControlPicker/InputControlDropdownItem.cs | 5 +-- .../InputControlPickerDropdown.cs | 22 +++++----- 4 files changed, 48 insertions(+), 54 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 45af3110b9..0edc1e4b6a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -720,36 +720,33 @@ public static bool Matches(string expected, InputControl control) return MatchesRecursive(ref parser, control); } - internal static bool Matches(IEnumerable usages, string name, ref InputControlLayout.ControlItem controlItem) + internal static bool MatchControlComponent(ref ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem) { - if (usages.Count() > 0) + // All of usages should match to the one of usage in the control + foreach (var usage in expectedControlComponent.m_Usages) { - // All of usages should match to the one of usage in the control - foreach (var usage in usages) + if (!usage.isEmpty) { - if (usage.Length > 0) + var usageCount = controlItem.usages.Count; + var anyUsageMatches = false; + for (var i = 0; i < usageCount; ++i) { - var usageCount = controlItem.usages.Count; - var anyUsageMatches = false; - for (var i = 0; i < usageCount; ++i) + if (StringMatches(usage, controlItem.usages[i])) { - if (StringMatches(usage, controlItem.usages[i])) - { - anyUsageMatches = true; - break; - } + anyUsageMatches = true; + break; } - - if (!anyUsageMatches) - return false; } + + if (!anyUsageMatches) + return false; } } // Match name. - if (name.Length > 0) + if (!expectedControlComponent.m_Name.isEmpty) { - if (!StringMatches(name, controlItem.name)) + if (!StringMatches(expectedControlComponent.m_Name, controlItem.name)) return false; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 4f52e9594a..ff994da0f9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -62,7 +62,7 @@ public void Dispose() m_ControlPathEditor?.Dispose(); } - bool showPaths = false; + private static bool showPaths = false; protected override void DrawGeneralProperties() { var currentPath = m_PathProperty.stringValue; @@ -104,12 +104,12 @@ protected override void DrawGeneralProperties() } } - showPaths = EditorGUILayout.Toggle("Show full paths", showPaths); + showPaths = EditorGUILayout.Toggle("Show all matched control paths", showPaths); // Show the specific layouts that implement the control on this path if (showPaths) { // Control scheme matrix. - DrawFullPathInfo(); + DrawMatchingControlPaths(); } // Control scheme matrix. DrawUseInControlSchemes(); @@ -117,48 +117,46 @@ protected override void DrawGeneralProperties() } /// - /// Draw control scheme matrix that allows selecting which control schemes a particular - /// binding appears in. + /// Finds all registered control paths implemented by concrete classes which match the current binding path and renders it. /// - private void DrawFullPathInfo() + private void DrawMatchingControlPaths() { - var path = m_ControlPathEditor.pathProperty.stringValue; var layout = InputControlPath.TryGetDeviceLayout(path); var parsedPath = InputControlPath.Parse(path).ToArray(); - // Try to find all relevant full paths if(parsedPath.Length == 2) { - var usages = parsedPath[1].usages; - var name = parsedPath[1].name; - EditorGUILayout.BeginVertical(); - DrawPathInfoForLayout(new InternedString(layout), usages, name); + DrawMatchingControlPathsForLayout(new InternedString(layout), ref parsedPath[1]); EditorGUILayout.EndVertical(); } - // Otherwise skip } - private void DrawPathInfoForLayout(InternedString rootLayout, IEnumerable usages, string name) + /// + /// Finds all registered control paths implemented by concrete classes under a given device layout which match the current binding path and renders it. + /// + /// The device layout to draw control paths for + /// The parsed path component containing details of the Input Controls that can be matched + private void DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref InputControlPath.ParsedPathComponent pathControlComponent) { var path = m_ControlPathEditor.pathProperty.stringValue; - var childLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.baseLayouts.Contains(rootLayout)).OrderBy(x => x.displayName); + var matchedChildLayouts = EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.baseLayouts.Contains(deviceLayout)).OrderBy(x => x.displayName); - if (rootLayout == InputControlPath.Wildcard) + if (deviceLayout == InputControlPath.Wildcard) { - childLayouts = EditorInputControlLayoutCache.allLayouts + matchedChildLayouts = EditorInputControlLayoutCache.allLayouts .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); } - if (childLayouts.Count() > 0) + if (matchedChildLayouts.Count() > 0) { - foreach (var childLayout in childLayouts) + foreach (var childLayout in matchedChildLayouts) { for(int i = 0; i < childLayout.m_Controls.Length;i++) { - if (InputControlPath.Matches(usages, name, ref childLayout.m_Controls[i])) + if (InputControlPath.MatchControlComponent(ref pathControlComponent, ref childLayout.m_Controls[i])) { EditorGUILayout.LabelField(childLayout.displayName + "/" + childLayout.m_Controls[i].displayName); continue; @@ -166,7 +164,7 @@ private void DrawPathInfoForLayout(InternedString rootLayout, IEnumerable string.IsNullOrEmpty(m_Device) ? $"*/{{{m_ControlPath}}}" - : $"<{m_Device}>/{{{m_ControlPath}}}"; + public override string controlPathWithDevice => string.IsNullOrEmpty(m_Device) ? $"*/{{{m_ControlPath}}}" : $"<{m_Device}>/{{{m_ControlPath}}}"; - public UsageDropdownItem(string usage, string device) + public UsageDropdownItem(string device, string usage) : base(usage) { m_Device = device; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs index 20a640c168..5533c3ba00 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs @@ -131,7 +131,7 @@ private AdvancedDropdownItem BuildTreeForControlUsages(string device = "") { if (usageAndLayouts.Item2.Any(LayoutMatchesExpectedControlLayoutFilter)) { - var child = new UsageDropdownItem(usageAndLayouts.Item1, device); + var child = new UsageDropdownItem(device, usageAndLayouts.Item1); usageRoot.AddChild(child); } } @@ -183,22 +183,14 @@ private void AddDeviceTreeItemRecursive(InputControlLayout layout, AdvancedDropd var defaultControlPickerLayout = new DefaultInputControlPickerLayout(); - // Add control usages for the device - var deviceControlUsages = BuildTreeForControlUsages(layout.name); - if (deviceControlUsages.children.Any()) - { - deviceItem.AddChild(deviceControlUsages); - deviceItem.AddSeparator(); - } - - // Add common device usage variants. + // Add common usage variants of the device if (layout.commonUsages.Count > 0) { foreach (var usage in layout.commonUsages) { var usageItem = new DeviceDropdownItem(layout, usage); - // Add control usages for the device sub-variant + // Add control usages to the device variants var deviceVariantControlUsages = BuildTreeForControlUsages(usageItem.name); if (deviceVariantControlUsages.children.Any()) { @@ -213,6 +205,14 @@ private void AddDeviceTreeItemRecursive(InputControlLayout layout, AdvancedDropd deviceItem.AddSeparator(); } + // Add control usages + var deviceControlUsages = BuildTreeForControlUsages(layout.name); + if (deviceControlUsages.children.Any()) + { + deviceItem.AddChild(deviceControlUsages); + deviceItem.AddSeparator(); + } + // Add controls. if (m_Mode != InputControlPicker.Mode.PickDevice) { From 83244321f176b766dbb7e30c505b7f5c9ad35297 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 14 Apr 2023 12:19:09 -0700 Subject: [PATCH 03/32] added unit test --- Assets/Tests/InputSystem/CoreTests_Layouts.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Assets/Tests/InputSystem/CoreTests_Layouts.cs b/Assets/Tests/InputSystem/CoreTests_Layouts.cs index a3b76d5f88..fc9eaa7b75 100644 --- a/Assets/Tests/InputSystem/CoreTests_Layouts.cs +++ b/Assets/Tests/InputSystem/CoreTests_Layouts.cs @@ -2546,6 +2546,43 @@ public void Layouts_CanForceMixedVariantsThroughLayout() Assert.That(device.allControls, Has.Exactly(1).With.Property("name").EqualTo("ButtonC")); } + [Test] + [Category("Layouts")] + public void Layouts_CanMatchControlPath() + { + const string jsonBase = @" + { + ""name"" : ""BaseLayout"", + ""extend"" : ""DeviceWithLayoutVariantA"", + ""controls"" : [ + { ""name"" : ""ControlFromBase"", ""layout"" : ""Button"" }, + { ""name"" : ""OtherControlFromBase"", ""layout"" : ""Axis"" }, + { ""name"" : ""ControlWithExplicitDefaultVariant"", ""layout"" : ""Axis"", ""variants"" : ""default"" }, + { ""name"" : ""StickControl"", ""layout"" : ""Stick"" }, + { ""name"" : ""StickControl/x"", ""offset"" : 14, ""variants"" : ""A"" } + ] + } + "; + const string jsonDerived = @" + { + ""name"" : ""DerivedLayout"", + ""extend"" : ""BaseLayout"", + ""controls"" : [ + { ""name"" : ""ControlFromBase"", ""variants"" : ""A"", ""offset"" : 20 } + ] + } + "; + + InputSystem.RegisterLayout(); + InputSystem.RegisterLayout(jsonBase); + InputSystem.RegisterLayout(jsonDerived); + + var layout = InputSystem.LoadLayout("DerivedLayout"); + var parsedPath = InputControlPath.Parse("/ControlWithExplicitDefaultVariant").ToArray()[1]; + + Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(ref parsedPath, ref x)), Is.True); + } + [Test] [Category("Layouts")] [Ignore("TODO")] From 361cf5f25ca984c71012ab48f1898217061a71cd Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 14 Apr 2023 14:08:39 -0700 Subject: [PATCH 04/32] Control usages now create the right path when combined with device usages, now shows a message when no matching registered paths exist --- .../AssetEditor/InputBindingPropertiesView.cs | 17 +++++++++---- .../ControlPicker/InputControlDropdownItem.cs | 24 +++++++++++++++---- .../InputControlPickerDropdown.cs | 6 ++--- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index ff994da0f9..1c9729bdc3 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -104,7 +104,7 @@ protected override void DrawGeneralProperties() } } - showPaths = EditorGUILayout.Toggle("Show all matched control paths", showPaths); + showPaths = EditorGUILayout.Toggle("Show Matching Paths", showPaths); // Show the specific layouts that implement the control on this path if (showPaths) { @@ -128,17 +128,21 @@ private void DrawMatchingControlPaths() if(parsedPath.Length == 2) { EditorGUILayout.BeginVertical(); - DrawMatchingControlPathsForLayout(new InternedString(layout), ref parsedPath[1]); + if(!DrawMatchingControlPathsForLayout(new InternedString(layout), ref parsedPath[1])) + { + EditorGUILayout.LabelField("No registered control paths match this current binding"); + } EditorGUILayout.EndVertical(); } } /// /// Finds all registered control paths implemented by concrete classes under a given device layout which match the current binding path and renders it. + /// Return true if there exist matching registered control paths, false otherwise. /// /// The device layout to draw control paths for /// The parsed path component containing details of the Input Controls that can be matched - private void DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref InputControlPath.ParsedPathComponent pathControlComponent) + private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref InputControlPath.ParsedPathComponent pathControlComponent) { var path = m_ControlPathEditor.pathProperty.stringValue; var matchedChildLayouts = EditorInputControlLayoutCache.allLayouts @@ -150,6 +154,8 @@ private void DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); } + bool matchExists = false; + if (matchedChildLayouts.Count() > 0) { foreach (var childLayout in matchedChildLayouts) @@ -159,15 +165,18 @@ private void DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref if (InputControlPath.MatchControlComponent(ref pathControlComponent, ref childLayout.m_Controls[i])) { EditorGUILayout.LabelField(childLayout.displayName + "/" + childLayout.m_Controls[i].displayName); + matchExists = true; continue; } } EditorGUI.indentLevel++; - DrawMatchingControlPathsForLayout(childLayout.name, ref pathControlComponent); + matchExists |= DrawMatchingControlPathsForLayout(childLayout.name, ref pathControlComponent); EditorGUI.indentLevel--; } } + + return matchExists; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs index da4bd332ad..b955c9eea9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs @@ -70,15 +70,31 @@ public OptionalControlDropdownItem(EditorInputControlLayoutCache.OptionalControl } } - internal sealed class UsageDropdownItem : InputControlDropdownItem + internal sealed class ControlUsageDropdownItem : InputControlDropdownItem { - public override string controlPathWithDevice => string.IsNullOrEmpty(m_Device) ? $"*/{{{m_ControlPath}}}" : $"<{m_Device}>/{{{m_ControlPath}}}"; + public override string controlPathWithDevice => BuildControlPath(); + private string BuildControlPath() + { + if (m_Device == "*") + { + var path = new StringBuilder(m_Device); + if (!string.IsNullOrEmpty(m_Usage)) + path.Append($"{{{m_Usage}}}"); + if (!string.IsNullOrEmpty(m_ControlPath)) + path.Append($"/{m_ControlPath}"); + return path.ToString(); + } + else + return base.controlPathWithDevice; + } - public UsageDropdownItem(string device, string usage) + public ControlUsageDropdownItem(string device, string usage, string controlUsage) : base(usage) { m_Device = device; - m_ControlPath = usage; + m_Usage = usage; + m_ControlPath = $"{{{ controlUsage }}}"; + name = controlUsage; id = controlPathWithDevice.GetHashCode(); m_Searchable = true; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs index 5533c3ba00..5f595a6a1d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs @@ -124,14 +124,14 @@ protected override void ItemSelected(AdvancedDropdownItem item) m_OnPickCallback(path); } - private AdvancedDropdownItem BuildTreeForControlUsages(string device = "") + private AdvancedDropdownItem BuildTreeForControlUsages(string device = "", string usage = "") { var usageRoot = new AdvancedDropdownItem("Usages"); foreach (var usageAndLayouts in EditorInputControlLayoutCache.allUsages) { if (usageAndLayouts.Item2.Any(LayoutMatchesExpectedControlLayoutFilter)) { - var child = new UsageDropdownItem(device, usageAndLayouts.Item1); + var child = new ControlUsageDropdownItem(device, usage, usageAndLayouts.Item1); usageRoot.AddChild(child); } } @@ -191,7 +191,7 @@ private void AddDeviceTreeItemRecursive(InputControlLayout layout, AdvancedDropd var usageItem = new DeviceDropdownItem(layout, usage); // Add control usages to the device variants - var deviceVariantControlUsages = BuildTreeForControlUsages(usageItem.name); + var deviceVariantControlUsages = BuildTreeForControlUsages(layout.name, usage); if (deviceVariantControlUsages.children.Any()) { usageItem.AddChild(deviceVariantControlUsages); From a9f0caca089b85a7822196fbb26e385888703bae Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 14 Apr 2023 14:56:53 -0700 Subject: [PATCH 05/32] updated to show relevant information when the path doesn't match any additional contexts --- .../AssetEditor/InputBindingPropertiesView.cs | 41 +++++++++++++++---- .../ControlPicker/InputControlDropdownItem.cs | 2 +- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 1c9729bdc3..2abb958468 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -122,18 +122,45 @@ protected override void DrawGeneralProperties() private void DrawMatchingControlPaths() { var path = m_ControlPathEditor.pathProperty.stringValue; - var layout = InputControlPath.TryGetDeviceLayout(path); + var deviceLayout = new InternedString(InputControlPath.TryGetDeviceLayout(path)); var parsedPath = InputControlPath.Parse(path).ToArray(); - if(parsedPath.Length == 2) + bool matchExists = false; + EditorGUILayout.BeginVertical(); + if (parsedPath.Length == 2) { - EditorGUILayout.BeginVertical(); - if(!DrawMatchingControlPathsForLayout(new InternedString(layout), ref parsedPath[1])) + if (deviceLayout != InputControlPath.Wildcard) + { + var rootLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.name == deviceLayout); + if (rootLayout != null) + { + for (int i = 0; i < rootLayout.m_Controls.Length; i++) + { + if (InputControlPath.MatchControlComponent(ref parsedPath[1], ref rootLayout.m_Controls[i])) + { + EditorGUILayout.LabelField($"{rootLayout.displayName}/{rootLayout.m_Controls[i].displayName}"); + matchExists = true; + continue; + } + } + + EditorGUI.indentLevel++; + matchExists |= DrawMatchingControlPathsForLayout(deviceLayout, ref parsedPath[1]); + EditorGUI.indentLevel--; + } + } + else { - EditorGUILayout.LabelField("No registered control paths match this current binding"); + matchExists |= DrawMatchingControlPathsForLayout(deviceLayout, ref parsedPath[1]); } - EditorGUILayout.EndVertical(); } + + if (!matchExists) + { + EditorGUILayout.LabelField("No registered control paths match this current binding"); + } + + EditorGUILayout.EndVertical(); } /// @@ -164,7 +191,7 @@ private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref { if (InputControlPath.MatchControlComponent(ref pathControlComponent, ref childLayout.m_Controls[i])) { - EditorGUILayout.LabelField(childLayout.displayName + "/" + childLayout.m_Controls[i].displayName); + EditorGUILayout.LabelField($"{childLayout.displayName}/{childLayout.m_Controls[i].displayName}"); matchExists = true; continue; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs index b955c9eea9..a1bc18709d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlDropdownItem.cs @@ -91,7 +91,7 @@ private string BuildControlPath() public ControlUsageDropdownItem(string device, string usage, string controlUsage) : base(usage) { - m_Device = device; + m_Device = string.IsNullOrEmpty(device) ? "*" : device; m_Usage = usage; m_ControlPath = $"{{{ controlUsage }}}"; name = controlUsage; From 57111f0274010d1baa37b88d8e7f6be7bdc5ff40 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Thu, 27 Apr 2023 13:57:38 -0700 Subject: [PATCH 06/32] removing whitespace, retriggering formatting CI --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 2abb958468..c6aab4f41a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -206,8 +206,6 @@ private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref return matchExists; } - - /// /// Draw control scheme matrix that allows selecting which control schemes a particular /// binding appears in. From 370d0da2b6823ce126a3b427b1291e574082c8a3 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Thu, 27 Apr 2023 14:16:30 -0700 Subject: [PATCH 07/32] fixing remaining formatting complaints by running format.ps1 --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index c6aab4f41a..f866f521e8 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -173,12 +173,12 @@ private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref { var path = m_ControlPathEditor.pathProperty.stringValue; var matchedChildLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.baseLayouts.Contains(deviceLayout)).OrderBy(x => x.displayName); + .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.baseLayouts.Contains(deviceLayout)).OrderBy(x => x.displayName); if (deviceLayout == InputControlPath.Wildcard) { matchedChildLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); + .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); } bool matchExists = false; @@ -187,7 +187,7 @@ private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref { foreach (var childLayout in matchedChildLayouts) { - for(int i = 0; i < childLayout.m_Controls.Length;i++) + for (int i = 0; i < childLayout.m_Controls.Length; i++) { if (InputControlPath.MatchControlComponent(ref pathControlComponent, ref childLayout.m_Controls[i])) { From d35d3d4e2bf308630cb500d8d702334b33d72846 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Mon, 1 May 2023 16:02:30 -0700 Subject: [PATCH 08/32] tweaked UI to PR suggestions --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index f866f521e8..bc3dfb53a5 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -62,7 +62,7 @@ public void Dispose() m_ControlPathEditor?.Dispose(); } - private static bool showPaths = false; + private static bool showMatchingControls = false; protected override void DrawGeneralProperties() { var currentPath = m_PathProperty.stringValue; @@ -104,12 +104,13 @@ protected override void DrawGeneralProperties() } } - showPaths = EditorGUILayout.Toggle("Show Matching Paths", showPaths); - // Show the specific layouts that implement the control on this path - if (showPaths) + showMatchingControls = EditorGUILayout.Foldout(showMatchingControls, "Matching Controls"); + // Show the specific controls which match the current path + if (showMatchingControls) { - // Control scheme matrix. + EditorGUI.indentLevel++; DrawMatchingControlPaths(); + EditorGUI.indentLevel--; } // Control scheme matrix. DrawUseInControlSchemes(); From 0a241f1e585f55482a5989ba86dc8e4cf8e6c899 Mon Sep 17 00:00:00 2001 From: RogPodge <39840334+RogPodge@users.noreply.github.com> Date: Wed, 10 May 2023 14:25:44 -0700 Subject: [PATCH 09/32] Update Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs Co-authored-by: Dave Ruddell <94039157+vrdave-unity@users.noreply.github.com> --- .../InputSystem/Controls/InputControlPath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 47ba882a5c..935739bf64 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -721,7 +721,7 @@ public static bool Matches(string expected, InputControl control) return MatchesRecursive(ref parser, control); } - internal static bool MatchControlComponent(ref ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem) + internal static bool MatchControlComponent(in ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem) { // All of usages should match to the one of usage in the control foreach (var usage in expectedControlComponent.m_Usages) From 3866b5dcc4cd871b82bc126aaddb4f6e9dbbcec2 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Wed, 10 May 2023 14:40:39 -0700 Subject: [PATCH 10/32] Adjusted instances of ref to in, fixed instances where break should be used instead of continue --- Assets/Tests/InputSystem/CoreTests_Layouts.cs | 2 +- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Layouts.cs b/Assets/Tests/InputSystem/CoreTests_Layouts.cs index fc9eaa7b75..aff39454a4 100644 --- a/Assets/Tests/InputSystem/CoreTests_Layouts.cs +++ b/Assets/Tests/InputSystem/CoreTests_Layouts.cs @@ -2580,7 +2580,7 @@ public void Layouts_CanMatchControlPath() var layout = InputSystem.LoadLayout("DerivedLayout"); var parsedPath = InputControlPath.Parse("/ControlWithExplicitDefaultVariant").ToArray()[1]; - Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(ref parsedPath, ref x)), Is.True); + Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedPath, ref x)), Is.True); } [Test] diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index bc3dfb53a5..3cda0a3ae9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -137,11 +137,11 @@ private void DrawMatchingControlPaths() { for (int i = 0; i < rootLayout.m_Controls.Length; i++) { - if (InputControlPath.MatchControlComponent(ref parsedPath[1], ref rootLayout.m_Controls[i])) + if (InputControlPath.MatchControlComponent(in parsedPath[1], ref rootLayout.m_Controls[i])) { EditorGUILayout.LabelField($"{rootLayout.displayName}/{rootLayout.m_Controls[i].displayName}"); matchExists = true; - continue; + break; } } @@ -190,11 +190,11 @@ private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref { for (int i = 0; i < childLayout.m_Controls.Length; i++) { - if (InputControlPath.MatchControlComponent(ref pathControlComponent, ref childLayout.m_Controls[i])) + if (InputControlPath.MatchControlComponent(in pathControlComponent, ref childLayout.m_Controls[i])) { EditorGUILayout.LabelField($"{childLayout.displayName}/{childLayout.m_Controls[i].displayName}"); matchExists = true; - continue; + break; } } From b01289d1618355e2da179bfa6b2acc97101574f5 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Tue, 23 May 2023 15:30:13 -0700 Subject: [PATCH 11/32] Added a lookup dictionary in the EditorInputControlLayoutCache for derived layout types, updated dictionary to use a hashset for performance --- .../AssetEditor/InputBindingPropertiesView.cs | 25 ++++---- .../Editor/EditorInputControlLayoutCache.cs | 58 ++++++++++++++++--- 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 3cda0a3ae9..96aca493d7 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -128,17 +128,25 @@ private void DrawMatchingControlPaths() bool matchExists = false; EditorGUILayout.BeginVertical(); - if (parsedPath.Length == 2) + if (parsedPath.Length >= 2) { if (deviceLayout != InputControlPath.Wildcard) { - var rootLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.name == deviceLayout); + var rootLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => !x.isOverride && !x.hideInUI && x.name == deviceLayout); if (rootLayout != null) { for (int i = 0; i < rootLayout.m_Controls.Length; i++) { if (InputControlPath.MatchControlComponent(in parsedPath[1], ref rootLayout.m_Controls[i])) { + // Need to include edge case where we go over the control's controllayout and ensure that the subvariant is written accordingly + // i.e. Dpad/south + // Alternatively, just use the parsePath display name since it'll match + + // **** TODO **** + // Create (and cache) a dependency tree for the input control layouts and then use it to quickly fetch the child classes for a layout + // This should remove linq's in many places and generally smooth out lookup + EditorGUILayout.LabelField($"{rootLayout.displayName}/{rootLayout.m_Controls[i].displayName}"); matchExists = true; break; @@ -173,17 +181,14 @@ private void DrawMatchingControlPaths() private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref InputControlPath.ParsedPathComponent pathControlComponent) { var path = m_ControlPathEditor.pathProperty.stringValue; - var matchedChildLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.baseLayouts.Contains(deviceLayout)).OrderBy(x => x.displayName); - - if (deviceLayout == InputControlPath.Wildcard) + var matchedChildLayouts = deviceLayout.ToLiteral() switch { - matchedChildLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.isOverride && !x.hideInUI && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); - } + InputControlPath.Wildcard => EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName), + _ => EditorInputControlLayoutCache.TryGetDerivedLayouts(deviceLayout) + }; bool matchExists = false; - if (matchedChildLayouts.Count() > 0) { foreach (var childLayout in matchedChildLayouts) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs index a559c95691..885a78b8ac 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs @@ -74,6 +74,24 @@ public static IEnumerable allProductLayouts } } + public static IEnumerable TryGetDerivedLayouts(string layoutName) + { + if (string.IsNullOrEmpty(layoutName)) + throw new ArgumentException("Layout name cannot be null or empty", nameof(layoutName)); + + Refresh(); + + var internedLayout = new InternedString(layoutName); + // return nothing is the layout does not have any derivations + if (!s_DeviceDerivations.TryGetValue(internedLayout, out var derivations)) + yield break; + else + { + foreach (var name in derivations) + yield return InputControlLayout.cache.FindOrLoadLayout(name.ToString()); + } + } + public static InputControlLayout TryGetLayout(string layoutName) { if (string.IsNullOrEmpty(layoutName)) @@ -310,10 +328,15 @@ private static void Refresh() private static Dictionary s_Icons = new Dictionary(); + // We keep a map of the devices which a derived from a base device. + private static readonly Dictionary> s_DeviceDerivations = + new Dictionary>(); + + // We keep a map of all unique usages we find in layouts and also // retain a list of the layouts they are used with. - private static readonly SortedDictionary> s_Usages = - new SortedDictionary>(); + private static readonly SortedDictionary> s_Usages = + new SortedDictionary>(); private static void ScanLayout(InputControlLayout layout) { @@ -343,17 +366,34 @@ private static void ScanLayout(InputControlLayout layout) var internedUsage = new InternedString(usage); var internedLayout = new InternedString(control.layout); - if (!s_Usages.TryGetValue(internedUsage, out var layoutList)) + if (!s_Usages.TryGetValue(internedUsage, out var layoutSet)) { - layoutList = new List {internedLayout}; - s_Usages[internedUsage] = layoutList; + layoutSet = new HashSet { internedLayout }; + s_Usages[internedUsage] = layoutSet; } else { - var layoutAlreadyInList = - layoutList.Any(x => x == internedLayout); - if (!layoutAlreadyInList) - layoutList.Add(internedLayout); + layoutSet.Add(internedLayout); + } + } + + + + // Create a dependency tree of each concrete device + // layout and all of the layouts that are directly derived from it. + if (layout.isDeviceLayout && !layout.hideInUI) + { + foreach (var baseLayoutName in layout.baseLayouts) + { + if (!s_DeviceDerivations.TryGetValue(baseLayoutName, out var derivedSet)) + { + derivedSet = new HashSet { layout.name }; + s_DeviceDerivations[baseLayoutName] = derivedSet; + } + else + { + derivedSet.Add(layout.name); + } } } } From 99e0f613b7a7915fd8e54b0ab40df60de61039f5 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Wed, 24 May 2023 18:56:33 -0700 Subject: [PATCH 12/32] Updated matching controls layout information to an expandable foldout --- .../AssetEditor/InputBindingPropertiesView.cs | 149 +++++++++++------- 1 file changed, 96 insertions(+), 53 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 96aca493d7..4092172dc2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -104,66 +104,57 @@ protected override void DrawGeneralProperties() } } - showMatchingControls = EditorGUILayout.Foldout(showMatchingControls, "Matching Controls"); // Show the specific controls which match the current path - if (showMatchingControls) - { - EditorGUI.indentLevel++; - DrawMatchingControlPaths(); - EditorGUI.indentLevel--; - } + DrawMatchingControlPaths(); + // Control scheme matrix. DrawUseInControlSchemes(); } } + /// + /// Used to keep track of which foldouts are expanded. + /// + private Dictionary showMatchingChildLayouts = new Dictionary(); + /// /// Finds all registered control paths implemented by concrete classes which match the current binding path and renders it. /// private void DrawMatchingControlPaths() { var path = m_ControlPathEditor.pathProperty.stringValue; - var deviceLayout = new InternedString(InputControlPath.TryGetDeviceLayout(path)); + var deviceLayoutPath = new InternedString(InputControlPath.TryGetDeviceLayout(path)); var parsedPath = InputControlPath.Parse(path).ToArray(); - bool matchExists = false; EditorGUILayout.BeginVertical(); + + // If the provided path is parseable into device and control components, draw UI which shows all control layouts that match the path. + bool matchExists = false; + EditorGUILayout.LabelField("Matching Controls: "); + if (parsedPath.Length >= 2) { - if (deviceLayout != InputControlPath.Wildcard) + if (deviceLayoutPath != InputControlPath.Wildcard) { - var rootLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => !x.isOverride && !x.hideInUI && x.name == deviceLayout); - if (rootLayout != null) + var deviceLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => !x.isOverride && !x.hideInUI && x.name == deviceLayoutPath); + if (deviceLayout != null) { - for (int i = 0; i < rootLayout.m_Controls.Length; i++) - { - if (InputControlPath.MatchControlComponent(in parsedPath[1], ref rootLayout.m_Controls[i])) - { - // Need to include edge case where we go over the control's controllayout and ensure that the subvariant is written accordingly - // i.e. Dpad/south - // Alternatively, just use the parsePath display name since it'll match - - // **** TODO **** - // Create (and cache) a dependency tree for the input control layouts and then use it to quickly fetch the child classes for a layout - // This should remove linq's in many places and generally smooth out lookup - - EditorGUILayout.LabelField($"{rootLayout.displayName}/{rootLayout.m_Controls[i].displayName}"); - matchExists = true; - break; - } - } - - EditorGUI.indentLevel++; - matchExists |= DrawMatchingControlPathsForLayout(deviceLayout, ref parsedPath[1]); - EditorGUI.indentLevel--; + matchExists |= DrawMatchingControlPathsForLayout(deviceLayout, in parsedPath); } } else { - matchExists |= DrawMatchingControlPathsForLayout(deviceLayout, ref parsedPath[1]); + var rootLayouts = EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); + + foreach(var rootDeviceLayout in rootLayouts) + { + matchExists |= DrawMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath); + } } } + // Otherwise, indicate that no layouts match the current path. if (!matchExists) { EditorGUILayout.LabelField("No registered control paths match this current binding"); @@ -173,40 +164,92 @@ private void DrawMatchingControlPaths() } /// - /// Finds all registered control paths implemented by concrete classes under a given device layout which match the current binding path and renders it. - /// Return true if there exist matching registered control paths, false otherwise. + /// Returns true if the deviceLayout or any of its children has controls which match the provided parsed path. exist matching registered control paths. /// /// The device layout to draw control paths for - /// The parsed path component containing details of the Input Controls that can be matched - private bool DrawMatchingControlPathsForLayout(InternedString deviceLayout, ref InputControlPath.ParsedPathComponent pathControlComponent) + /// The parsed path containing details of the Input Controls that can be matched + private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, in InputControlPath.ParsedPathComponent[] parsedPath) { - var path = m_ControlPathEditor.pathProperty.stringValue; + string deviceName = deviceLayout.displayName; + string controlName = string.Empty; + bool matchExists = false; + + for (int i = 0; i < deviceLayout.m_Controls.Length; i++) + { + ref InputControlLayout.ControlItem controlItem = ref deviceLayout.m_Controls[i]; + if (InputControlPath.MatchControlComponent(in parsedPath[1], ref controlItem)) + { + // If we've already located a match, append a ", " to the control name + // This is to accomodate cases where multiple control items match the same path within a single device layout + // Note, some controlItems have names but invalid displayNames (i.e. the Dualsense HID > leftTriggerButton) + // There are instance where there are 2 control items with the same name inside a layout definition, however they are not + // labeled significantly differently. + // The notable example is that the Android Xbox and Android Dualshock layouts have 2 d-pad definitions, one is a "button" + // while the other is an axis. + controlName += matchExists ? $", {controlItem.displayName}" : controlItem.displayName; + + // if the parsePath has a 3rd component, try to match it with items in the controlItem's layout definition. + if (parsedPath.Length == 3) + { + var controlLayout = EditorInputControlLayoutCache.TryGetLayout(controlItem.layout); + if (controlLayout.isControlLayout && !controlLayout.hideInUI) + { + for (int j = 0; j < controlLayout.m_Controls.Count(); j++) + { + ref InputControlLayout.ControlItem controlLayoutItem = ref controlLayout.m_Controls[j]; + if (InputControlPath.MatchControlComponent(in parsedPath[2], ref controlLayoutItem)) + { + controlName += $"/{controlLayoutItem.displayName}"; + matchExists = true; + } + } + } + } + else + { + matchExists = true; + } + } + } + var matchedChildLayouts = deviceLayout.ToLiteral() switch { InputControlPath.Wildcard => EditorInputControlLayoutCache.allLayouts .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName), - _ => EditorInputControlLayoutCache.TryGetDerivedLayouts(deviceLayout) + _ => EditorInputControlLayoutCache.TryGetDerivedLayouts(deviceLayout.name) }; - bool matchExists = false; - if (matchedChildLayouts.Count() > 0) + // If this layout does not have a match, skip over trying to draw any items for it, and immdiately try processing the child layouts + if (!matchExists) { foreach (var childLayout in matchedChildLayouts) { - for (int i = 0; i < childLayout.m_Controls.Length; i++) + matchExists |= DrawMatchingControlPathsForLayout(childLayout, in parsedPath); + } + } + // Otherwise, draw the items for it, and then only process the child layouts if the foldout is expanded. + else + { + bool showLayout = false; + EditorGUI.indentLevel++; + if (matchedChildLayouts.Count() > 0) + { + showMatchingChildLayouts.TryGetValue(deviceName, out showLayout); + showMatchingChildLayouts[deviceName] = EditorGUILayout.Foldout(showLayout, $"{deviceName} > {controlName}"); + } + else + { + EditorGUILayout.LabelField($"{deviceName} > {controlName}"); + } + + if (showLayout) + { + foreach (var childLayout in matchedChildLayouts) { - if (InputControlPath.MatchControlComponent(in pathControlComponent, ref childLayout.m_Controls[i])) - { - EditorGUILayout.LabelField($"{childLayout.displayName}/{childLayout.m_Controls[i].displayName}"); - matchExists = true; - break; - } + DrawMatchingControlPathsForLayout(childLayout, in parsedPath); } - - EditorGUI.indentLevel++; - matchExists |= DrawMatchingControlPathsForLayout(childLayout.name, ref pathControlComponent); - EditorGUI.indentLevel--; } + EditorGUI.indentLevel--; } return matchExists; From 7483e8d9cd5a8ff9df0d0402230bf0aded6c6baa Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 2 Jun 2023 15:26:30 -0700 Subject: [PATCH 13/32] fixed issue where invalid controls could be selected in the dropdown UI, updated UI with PR feedback --- .../AssetEditor/InputBindingPropertiesView.cs | 60 ++++++++++--------- .../Editor/EditorInputControlLayoutCache.cs | 20 +++---- 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 4092172dc2..8ce89968b2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -62,7 +62,6 @@ public void Dispose() m_ControlPathEditor?.Dispose(); } - private static bool showMatchingControls = false; protected override void DrawGeneralProperties() { var currentPath = m_PathProperty.stringValue; @@ -115,7 +114,8 @@ protected override void DrawGeneralProperties() /// /// Used to keep track of which foldouts are expanded. /// - private Dictionary showMatchingChildLayouts = new Dictionary(); + private static bool showMatchingLayouts = false; + private static Dictionary showMatchingChildLayouts = new Dictionary(); /// /// Finds all registered control paths implemented by concrete classes which match the current binding path and renders it. @@ -130,34 +130,37 @@ private void DrawMatchingControlPaths() // If the provided path is parseable into device and control components, draw UI which shows all control layouts that match the path. bool matchExists = false; - EditorGUILayout.LabelField("Matching Controls: "); + showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Matching Control Paths:"); - if (parsedPath.Length >= 2) + if (showMatchingLayouts) { - if (deviceLayoutPath != InputControlPath.Wildcard) + if (parsedPath.Length >= 2) { - var deviceLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => !x.isOverride && !x.hideInUI && x.name == deviceLayoutPath); - if (deviceLayout != null) + if (deviceLayoutPath != InputControlPath.Wildcard) { - matchExists |= DrawMatchingControlPathsForLayout(deviceLayout, in parsedPath); + var rootDeviceLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => !x.isOverride && !x.hideInUI && x.name == deviceLayoutPath); + if (rootDeviceLayout != null) + { + matchExists |= DrawMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath, true); + } } - } - else - { - var rootLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); - - foreach(var rootDeviceLayout in rootLayouts) + else { - matchExists |= DrawMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath); + var genericDeviceLayouts = EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); + + foreach (var genericDeviceLayout in genericDeviceLayouts) + { + matchExists |= DrawMatchingControlPathsForLayout(genericDeviceLayout, in parsedPath); + } } } - } - // Otherwise, indicate that no layouts match the current path. - if (!matchExists) - { - EditorGUILayout.LabelField("No registered control paths match this current binding"); + // Otherwise, indicate that no layouts match the current path. + if (!matchExists) + { + EditorGUILayout.LabelField("No registered control paths match this current binding"); + } } EditorGUILayout.EndVertical(); @@ -168,7 +171,7 @@ private void DrawMatchingControlPaths() /// /// The device layout to draw control paths for /// The parsed path containing details of the Input Controls that can be matched - private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, in InputControlPath.ParsedPathComponent[] parsedPath) + private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, in InputControlPath.ParsedPathComponent[] parsedPath, bool isRoot = false) { string deviceName = deviceLayout.displayName; string controlName = string.Empty; @@ -186,7 +189,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, // labeled significantly differently. // The notable example is that the Android Xbox and Android Dualshock layouts have 2 d-pad definitions, one is a "button" // while the other is an axis. - controlName += matchExists ? $", {controlItem.displayName}" : controlItem.displayName; + controlName += matchExists ? $", {controlItem.name}" : controlItem.name; // if the parsePath has a 3rd component, try to match it with items in the controlItem's layout definition. if (parsedPath.Length == 3) @@ -199,7 +202,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, ref InputControlLayout.ControlItem controlLayoutItem = ref controlLayout.m_Controls[j]; if (InputControlPath.MatchControlComponent(in parsedPath[2], ref controlLayoutItem)) { - controlName += $"/{controlLayoutItem.displayName}"; + controlName += $"/{controlLayoutItem.name}"; matchExists = true; } } @@ -216,10 +219,11 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, { InputControlPath.Wildcard => EditorInputControlLayoutCache.allLayouts .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName), - _ => EditorInputControlLayoutCache.TryGetDerivedLayouts(deviceLayout.name) + _ => EditorInputControlLayoutCache.TryGetChildLayouts(deviceLayout.name) }; - // If this layout does not have a match, skip over trying to draw any items for it, and immdiately try processing the child layouts + // If this layout does not have a match, or is the top level root layout, + // skip over trying to draw any items for it, and immdiately try processing the child layouts if (!matchExists) { foreach (var childLayout in matchedChildLayouts) @@ -232,7 +236,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, { bool showLayout = false; EditorGUI.indentLevel++; - if (matchedChildLayouts.Count() > 0) + if (matchedChildLayouts.Count() > 0 && !isRoot) { showMatchingChildLayouts.TryGetValue(deviceName, out showLayout); showMatchingChildLayouts[deviceName] = EditorGUILayout.Foldout(showLayout, $"{deviceName} > {controlName}"); @@ -242,6 +246,8 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, EditorGUILayout.LabelField($"{deviceName} > {controlName}"); } + showLayout |= isRoot; + if (showLayout) { foreach (var childLayout in matchedChildLayouts) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs index 885a78b8ac..0fe5c52def 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs @@ -74,7 +74,7 @@ public static IEnumerable allProductLayouts } } - public static IEnumerable TryGetDerivedLayouts(string layoutName) + public static IEnumerable TryGetChildLayouts(string layoutName) { if (string.IsNullOrEmpty(layoutName)) throw new ArgumentException("Layout name cannot be null or empty", nameof(layoutName)); @@ -83,7 +83,7 @@ public static IEnumerable TryGetDerivedLayouts(string layout var internedLayout = new InternedString(layoutName); // return nothing is the layout does not have any derivations - if (!s_DeviceDerivations.TryGetValue(internedLayout, out var derivations)) + if (!s_DeviceChildLayouts.TryGetValue(internedLayout, out var derivations)) yield break; else { @@ -329,7 +329,7 @@ private static void Refresh() new Dictionary(); // We keep a map of the devices which a derived from a base device. - private static readonly Dictionary> s_DeviceDerivations = + private static readonly Dictionary> s_DeviceChildLayouts = new Dictionary>(); @@ -350,7 +350,9 @@ private static void ScanLayout(InputControlLayout layout) // // NOTE: We're looking at layouts post-merging here. Means we have already picked up all the // controls present on the base. - if (control.isFirstDefinedInThisLayout && !control.isModifyingExistingControl && !control.layout.IsEmpty()) + // Only controls which belong to UI-facing layouts are included, as optional controls are used solely by + // the InputControlPickerDropdown UI + if (control.isFirstDefinedInThisLayout && !control.isModifyingExistingControl && !control.layout.IsEmpty() && !layout.hideInUI) { foreach (var baseLayout in layout.baseLayouts) AddOptionalControlRecursive(baseLayout, ref control); @@ -377,18 +379,16 @@ private static void ScanLayout(InputControlLayout layout) } } - - - // Create a dependency tree of each concrete device - // layout and all of the layouts that are directly derived from it. + // Create a dependency tree matching each concrete device layout exposed in the UI + // to all of the layouts that are directly derived from it. if (layout.isDeviceLayout && !layout.hideInUI) { foreach (var baseLayoutName in layout.baseLayouts) { - if (!s_DeviceDerivations.TryGetValue(baseLayoutName, out var derivedSet)) + if (!s_DeviceChildLayouts.TryGetValue(baseLayoutName, out var derivedSet)) { derivedSet = new HashSet { layout.name }; - s_DeviceDerivations[baseLayoutName] = derivedSet; + s_DeviceChildLayouts[baseLayoutName] = derivedSet; } else { From e2130b8a0382a40a6f36e09ac30c4d1dc7e267d9 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 2 Jun 2023 15:43:14 -0700 Subject: [PATCH 14/32] fixed wording, fixed case where the binding path could break --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 8ce89968b2..cf2a0cc804 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -123,6 +123,9 @@ protected override void DrawGeneralProperties() private void DrawMatchingControlPaths() { var path = m_ControlPathEditor.pathProperty.stringValue; + if (path == string.Empty) + return; + var deviceLayoutPath = new InternedString(InputControlPath.TryGetDeviceLayout(path)); var parsedPath = InputControlPath.Parse(path).ToArray(); @@ -130,7 +133,7 @@ private void DrawMatchingControlPaths() // If the provided path is parseable into device and control components, draw UI which shows all control layouts that match the path. bool matchExists = false; - showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Matching Control Paths:"); + showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Matched Controls"); if (showMatchingLayouts) { From ddd446543c7e86b67b6f62cf56c6976d8b1918d8 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 2 Jun 2023 16:26:47 -0700 Subject: [PATCH 15/32] codestyle formatting pass --- .../AssetEditor/InputBindingPropertiesView.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index cf2a0cc804..bd93200bb4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -150,7 +150,7 @@ private void DrawMatchingControlPaths() else { var genericDeviceLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); + .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); foreach (var genericDeviceLayout in genericDeviceLayouts) { @@ -218,12 +218,17 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, } } - var matchedChildLayouts = deviceLayout.ToLiteral() switch + IEnumerable matchedChildLayouts = default; + switch (deviceLayout.ToLiteral()) { - InputControlPath.Wildcard => EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName), - _ => EditorInputControlLayoutCache.TryGetChildLayouts(deviceLayout.name) - }; + case InputControlPath.Wildcard: + matchedChildLayouts = EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); + break; + default: + matchedChildLayouts = EditorInputControlLayoutCache.TryGetChildLayouts(deviceLayout.name); + break; + } // If this layout does not have a match, or is the top level root layout, // skip over trying to draw any items for it, and immdiately try processing the child layouts From 35a27d7b0bdcd8474a8bfff670f457c6a0c32cd8 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Tue, 6 Jun 2023 11:00:03 -0700 Subject: [PATCH 16/32] fixed issue where device layouts were queried twice due to being child layouts of the 'root' base layouts --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index bd93200bb4..6087022a30 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -150,7 +150,7 @@ private void DrawMatchingControlPaths() else { var genericDeviceLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); + .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice && x.baseLayouts.Count() == 0).OrderBy(x => x.displayName); foreach (var genericDeviceLayout in genericDeviceLayouts) { From 9ae08b1e8d826dce92130dd51d5c721e2a054d41 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Wed, 7 Jun 2023 16:30:27 -0700 Subject: [PATCH 17/32] applying PR feedback --- .../InputSystem/Controls/InputControlPath.cs | 14 +++++++------- .../AssetEditor/InputBindingPropertiesView.cs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 935739bf64..cd8891f96e 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -723,6 +723,13 @@ public static bool Matches(string expected, InputControl control) internal static bool MatchControlComponent(in ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem) { + // Match name. + if (!expectedControlComponent.m_Name.isEmpty) + { + if (!StringMatches(expectedControlComponent.m_Name, controlItem.name)) + return false; + } + // All of usages should match to the one of usage in the control foreach (var usage in expectedControlComponent.m_Usages) { @@ -744,13 +751,6 @@ internal static bool MatchControlComponent(in ParsedPathComponent expectedContro } } - // Match name. - if (!expectedControlComponent.m_Name.isEmpty) - { - if (!StringMatches(expectedControlComponent.m_Name, controlItem.name)) - return false; - } - return true; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 6087022a30..136d092ae4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -231,7 +231,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, } // If this layout does not have a match, or is the top level root layout, - // skip over trying to draw any items for it, and immdiately try processing the child layouts + // skip over trying to draw any items for it, and immediately try processing the child layouts if (!matchExists) { foreach (var childLayout in matchedChildLayouts) From e7404014f104a62565b4a0d149f23b58e88a620a Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Thu, 8 Jun 2023 11:40:54 -0700 Subject: [PATCH 18/32] Updated No Matching Control paths message with PR suggestions --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 136d092ae4..789ba8d044 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -162,7 +162,7 @@ private void DrawMatchingControlPaths() // Otherwise, indicate that no layouts match the current path. if (!matchExists) { - EditorGUILayout.LabelField("No registered control paths match this current binding"); + EditorGUILayout.LabelField("No registered control paths match this current binding. Some control paths are only registered at runtime.", EditorStyles.wordWrappedLabel); } } From 3e953ee0f8388128dfd72fcce851223e4976e910 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Fri, 9 Jun 2023 13:32:35 -0700 Subject: [PATCH 19/32] Matching controls display now shows control path's whose alias matches the user specified path --- .../InputSystem/Controls/InputControlPath.cs | 27 ++++++++++++++++--- .../AssetEditor/InputBindingPropertiesView.cs | 2 +- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index cd8891f96e..6d491ccdb0 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -721,14 +721,33 @@ public static bool Matches(string expected, InputControl control) return MatchesRecursive(ref parser, control); } - internal static bool MatchControlComponent(in ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem) + internal static bool MatchControlComponent(in ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem, bool matchAlias = false) { - // Match name. - if (!expectedControlComponent.m_Name.isEmpty) + // Check to see that there is a match with the name or alias if specified + // Exit early if we can't create a match. + if (!StringMatches(expectedControlComponent.m_Name, controlItem.name)) { - if (!StringMatches(expectedControlComponent.m_Name, controlItem.name)) + if (matchAlias) + { + bool hasMatchingAlias = false; + + var aliases = controlItem.aliases; + for (var i = 0; i < aliases.Count; i++) + { + if (StringMatches(expectedControlComponent.m_Name, aliases[i])) + { + hasMatchingAlias = true; + break; + } + } + + if(!hasMatchingAlias) + return false; + } + else return false; } + // All of usages should match to the one of usage in the control foreach (var usage in expectedControlComponent.m_Usages) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 789ba8d044..58dab0afef 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -183,7 +183,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, for (int i = 0; i < deviceLayout.m_Controls.Length; i++) { ref InputControlLayout.ControlItem controlItem = ref deviceLayout.m_Controls[i]; - if (InputControlPath.MatchControlComponent(in parsedPath[1], ref controlItem)) + if (InputControlPath.MatchControlComponent(in parsedPath[1], ref controlItem, true)) { // If we've already located a match, append a ", " to the control name // This is to accomodate cases where multiple control items match the same path within a single device layout From 9454776babd1373a8d3ca07092b286cb5df4d319 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Mon, 12 Jun 2023 12:51:57 -0700 Subject: [PATCH 20/32] fixed bug where controls wouldn't match if only usages were checked --- .../InputSystem/Controls/InputControlPath.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 6d491ccdb0..8456719fef 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -725,7 +725,7 @@ internal static bool MatchControlComponent(in ParsedPathComponent expectedContro { // Check to see that there is a match with the name or alias if specified // Exit early if we can't create a match. - if (!StringMatches(expectedControlComponent.m_Name, controlItem.name)) + if (!expectedControlComponent.m_Name.isEmpty && !StringMatches(expectedControlComponent.m_Name, controlItem.name)) { if (matchAlias) { @@ -741,7 +741,7 @@ internal static bool MatchControlComponent(in ParsedPathComponent expectedContro } } - if(!hasMatchingAlias) + if (!hasMatchingAlias) return false; } else From 4409e5383d1049f753cb78cf29485112257fd4ac Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Tue, 13 Jun 2023 11:41:07 -0700 Subject: [PATCH 21/32] added unit tests for the new control path layout capabilities --- Assets/Tests/InputSystem/CoreTests_Layouts.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Layouts.cs b/Assets/Tests/InputSystem/CoreTests_Layouts.cs index aff39454a4..182d811c2a 100644 --- a/Assets/Tests/InputSystem/CoreTests_Layouts.cs +++ b/Assets/Tests/InputSystem/CoreTests_Layouts.cs @@ -2568,7 +2568,7 @@ public void Layouts_CanMatchControlPath() ""name"" : ""DerivedLayout"", ""extend"" : ""BaseLayout"", ""controls"" : [ - { ""name"" : ""ControlFromBase"", ""variants"" : ""A"", ""offset"" : 20 } + { ""name"" : ""ControlFromBase"", ""variants"" : ""A"", ""offset"" : 20, ""usages"" : [""Submit""], ""aliases"" : [""A""] } ] } "; @@ -2579,8 +2579,16 @@ public void Layouts_CanMatchControlPath() var layout = InputSystem.LoadLayout("DerivedLayout"); var parsedPath = InputControlPath.Parse("/ControlWithExplicitDefaultVariant").ToArray()[1]; - Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedPath, ref x)), Is.True); + + // Verify that we can match alias's when provided + var parsedAliasPath = InputControlPath.Parse("/A").ToArray()[1]; + Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedAliasPath, ref x, true)), Is.True); + + // Verify that we match usages when it is the only control path component provided + var parsedUsagesPath = InputControlPath.Parse("/{Submit}").ToArray()[1]; + Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedUsagesPath, ref x)), Is.True); + } [Test] From f63051db7f4668a284ce77a32d0c5d25e100d296 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Tue, 13 Jun 2023 12:28:23 -0700 Subject: [PATCH 22/32] fixing formatting issues --- Assets/Tests/InputSystem/CoreTests_Layouts.cs | 1 - .../InputSystem/Controls/InputControlPath.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Layouts.cs b/Assets/Tests/InputSystem/CoreTests_Layouts.cs index 182d811c2a..97b03eaa97 100644 --- a/Assets/Tests/InputSystem/CoreTests_Layouts.cs +++ b/Assets/Tests/InputSystem/CoreTests_Layouts.cs @@ -2588,7 +2588,6 @@ public void Layouts_CanMatchControlPath() // Verify that we match usages when it is the only control path component provided var parsedUsagesPath = InputControlPath.Parse("/{Submit}").ToArray()[1]; Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedUsagesPath, ref x)), Is.True); - } [Test] diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 8456719fef..8f8f86fea3 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -747,7 +747,7 @@ internal static bool MatchControlComponent(in ParsedPathComponent expectedContro else return false; } - + // All of usages should match to the one of usage in the control foreach (var usage in expectedControlComponent.m_Usages) From cb2995cbff1f959dfc1011034529cbba41bffc68 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Wed, 14 Jun 2023 16:28:45 -0700 Subject: [PATCH 23/32] Removed reduntant control path listings when the control path specifically doesn't contain usages --- .../AssetEditor/InputBindingPropertiesView.cs | 89 +++++++++++-------- .../Editor/EditorInputControlLayoutCache.cs | 12 +++ 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 58dab0afef..2cfe3fb02b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -129,44 +129,73 @@ private void DrawMatchingControlPaths() var deviceLayoutPath = new InternedString(InputControlPath.TryGetDeviceLayout(path)); var parsedPath = InputControlPath.Parse(path).ToArray(); - EditorGUILayout.BeginVertical(); + // If the provided path is parseable into device and control components, draw UI which shows control layouts that match the path. + if (parsedPath.Length >= 2) + { + bool matchExists = false; - // If the provided path is parseable into device and control components, draw UI which shows all control layouts that match the path. - bool matchExists = false; - showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Matched Controls"); + var rootDeviceLayout = EditorInputControlLayoutCache.TryGetLayout(deviceLayoutPath); + bool isValidDeviceLayout = deviceLayoutPath != InputControlPath.Wildcard && rootDeviceLayout != null && !rootDeviceLayout.isOverride && !rootDeviceLayout.hideInUI; + // Exit early if a malformed device layout was provided, + if (!isValidDeviceLayout) + return; - if (showMatchingLayouts) - { - if (parsedPath.Length >= 2) + bool controlPathUsagePresent = parsedPath[1].usages.Count() > 0; + bool hasChildDeviceLayouts = deviceLayoutPath == InputControlPath.Wildcard || EditorInputControlLayoutCache.HasChildLayouts(rootDeviceLayout.name); + + // If the path provided is a specific device (i.e. has no ui-facing child device layouts or uses control usages), then exit early + if (!controlPathUsagePresent && !hasChildDeviceLayouts) + return; + + // Otherwise, we will show either all controls that match the current binding (if control usages are used) + // or all controls in derived device layouts (if a no control usages are used). + EditorGUILayout.BeginVertical(); + if (controlPathUsagePresent) + { + showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Matched Controls"); + } + else { - if (deviceLayoutPath != InputControlPath.Wildcard) + showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Other Matched Controls"); + } + + if (showMatchingLayouts) + { + if (controlPathUsagePresent && deviceLayoutPath != InputControlPath.Wildcard) { - var rootDeviceLayout = EditorInputControlLayoutCache.allLayouts.FirstOrDefault(x => !x.isOverride && !x.hideInUI && x.name == deviceLayoutPath); - if (rootDeviceLayout != null) - { - matchExists |= DrawMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath, true); - } + matchExists |= DrawMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath, true); } else { - var genericDeviceLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice && x.baseLayouts.Count() == 0).OrderBy(x => x.displayName); + IEnumerable matchedChildLayouts = Enumerable.Empty(); + if (deviceLayoutPath == InputControlPath.Wildcard) + { + matchedChildLayouts = EditorInputControlLayoutCache.allLayouts + .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice && x.baseLayouts.Count() == 0).OrderBy(x => x.displayName); + } + else + { + matchedChildLayouts = EditorInputControlLayoutCache.TryGetChildLayouts(rootDeviceLayout.name); + } - foreach (var genericDeviceLayout in genericDeviceLayouts) + foreach (var childLayout in matchedChildLayouts) { - matchExists |= DrawMatchingControlPathsForLayout(genericDeviceLayout, in parsedPath); + matchExists |= DrawMatchingControlPathsForLayout(childLayout, in parsedPath); } } - } - // Otherwise, indicate that no layouts match the current path. - if (!matchExists) - { - EditorGUILayout.LabelField("No registered control paths match this current binding. Some control paths are only registered at runtime.", EditorStyles.wordWrappedLabel); + // Otherwise, indicate that no layouts match the current path. + if (!matchExists) + { + if (controlPathUsagePresent) + EditorGUILayout.HelpBox("No registered controls match this current binding. Some controls are only registered at runtime.", MessageType.Warning); + else + EditorGUILayout.HelpBox("No other registered controls match this current binding. Some controls are only registered at runtime.", MessageType.Warning); + } } - } - EditorGUILayout.EndVertical(); + EditorGUILayout.EndVertical(); + } } /// @@ -218,17 +247,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, } } - IEnumerable matchedChildLayouts = default; - switch (deviceLayout.ToLiteral()) - { - case InputControlPath.Wildcard: - matchedChildLayouts = EditorInputControlLayoutCache.allLayouts - .Where(x => x.isDeviceLayout && !x.hideInUI && !x.isOverride && x.isGenericTypeOfDevice).OrderBy(x => x.displayName); - break; - default: - matchedChildLayouts = EditorInputControlLayoutCache.TryGetChildLayouts(deviceLayout.name); - break; - } + IEnumerable matchedChildLayouts = EditorInputControlLayoutCache.TryGetChildLayouts(deviceLayout.name); // If this layout does not have a match, or is the top level root layout, // skip over trying to draw any items for it, and immediately try processing the child layouts diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs index 0fe5c52def..81dac4b311 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs @@ -74,6 +74,18 @@ public static IEnumerable allProductLayouts } } + public static bool HasChildLayouts(string layoutName) + { + if (string.IsNullOrEmpty(layoutName)) + throw new ArgumentException("Layout name cannot be null or empty", nameof(layoutName)); + + Refresh(); + + var internedLayout = new InternedString(layoutName); + // return nothing is the layout does not have any derivations + return s_DeviceChildLayouts.TryGetValue(internedLayout, out var derivations) && derivations.Count > 0; + } + public static IEnumerable TryGetChildLayouts(string layoutName) { if (string.IsNullOrEmpty(layoutName)) From 5e522e3a28175c429f8f583ae77641fdf76c3408 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Thu, 15 Jun 2023 09:16:21 -0700 Subject: [PATCH 24/32] formatting fix --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 2cfe3fb02b..fe4b8f6334 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -136,7 +136,7 @@ private void DrawMatchingControlPaths() var rootDeviceLayout = EditorInputControlLayoutCache.TryGetLayout(deviceLayoutPath); bool isValidDeviceLayout = deviceLayoutPath != InputControlPath.Wildcard && rootDeviceLayout != null && !rootDeviceLayout.isOverride && !rootDeviceLayout.hideInUI; - // Exit early if a malformed device layout was provided, + // Exit early if a malformed device layout was provided, if (!isValidDeviceLayout) return; @@ -147,7 +147,7 @@ private void DrawMatchingControlPaths() if (!controlPathUsagePresent && !hasChildDeviceLayouts) return; - // Otherwise, we will show either all controls that match the current binding (if control usages are used) + // Otherwise, we will show either all controls that match the current binding (if control usages are used) // or all controls in derived device layouts (if a no control usages are used). EditorGUILayout.BeginVertical(); if (controlPathUsagePresent) From fa8216390971283991d2fba4875175424d1608a3 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Tue, 27 Jun 2023 15:32:11 -0700 Subject: [PATCH 25/32] relabed the 'matched controls' field to be 'Derived controls' --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index fe4b8f6334..20cfbeb1af 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -150,14 +150,7 @@ private void DrawMatchingControlPaths() // Otherwise, we will show either all controls that match the current binding (if control usages are used) // or all controls in derived device layouts (if a no control usages are used). EditorGUILayout.BeginVertical(); - if (controlPathUsagePresent) - { - showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Matched Controls"); - } - else - { - showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Other Matched Controls"); - } + showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Derived Controls"); if (showMatchingLayouts) { From ce5edd21fa271002cceb8edb1c3cc60ddbe76398 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Tue, 27 Jun 2023 15:36:36 -0700 Subject: [PATCH 26/32] Fixed to 'Derived Bindings' --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 20cfbeb1af..092a46f0dc 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -150,7 +150,7 @@ private void DrawMatchingControlPaths() // Otherwise, we will show either all controls that match the current binding (if control usages are used) // or all controls in derived device layouts (if a no control usages are used). EditorGUILayout.BeginVertical(); - showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Derived Controls"); + showMatchingLayouts = EditorGUILayout.Foldout(showMatchingLayouts, "Derived Bindings"); if (showMatchingLayouts) { From 5443d0d7e13f467094ac8b9a99e86cb17c6a6eae Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Thu, 29 Jun 2023 13:18:17 -0700 Subject: [PATCH 27/32] fixed case where the wildcard device moniker wasn't detected properly --- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 092a46f0dc..9c93be4ea4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -135,7 +135,7 @@ private void DrawMatchingControlPaths() bool matchExists = false; var rootDeviceLayout = EditorInputControlLayoutCache.TryGetLayout(deviceLayoutPath); - bool isValidDeviceLayout = deviceLayoutPath != InputControlPath.Wildcard && rootDeviceLayout != null && !rootDeviceLayout.isOverride && !rootDeviceLayout.hideInUI; + bool isValidDeviceLayout = deviceLayoutPath == InputControlPath.Wildcard || (rootDeviceLayout != null && !rootDeviceLayout.isOverride && !rootDeviceLayout.hideInUI); // Exit early if a malformed device layout was provided, if (!isValidDeviceLayout) return; From 553ead3b035701af9f7ee657bb5ca5d0d1f9c531 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Tue, 25 Jul 2023 11:46:06 -0700 Subject: [PATCH 28/32] fixed errors that would occur when malformed control paths are provided --- .../InputSystem/Controls/InputControlPath.cs | 23 ++++++++----------- .../AssetEditor/InputBindingPropertiesView.cs | 11 +++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 8f8f86fea3..d5afd23932 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -723,39 +723,38 @@ public static bool Matches(string expected, InputControl control) internal static bool MatchControlComponent(in ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem, bool matchAlias = false) { + bool controlItemNameMatched = false; + var anyUsageMatches = false; + // Check to see that there is a match with the name or alias if specified // Exit early if we can't create a match. - if (!expectedControlComponent.m_Name.isEmpty && !StringMatches(expectedControlComponent.m_Name, controlItem.name)) + if (!expectedControlComponent.m_Name.isEmpty) { - if (matchAlias) + if (StringMatches(expectedControlComponent.m_Name, controlItem.name)) + controlItemNameMatched = true; + else if (matchAlias) { - bool hasMatchingAlias = false; var aliases = controlItem.aliases; for (var i = 0; i < aliases.Count; i++) { if (StringMatches(expectedControlComponent.m_Name, aliases[i])) { - hasMatchingAlias = true; + controlItemNameMatched = true; break; } } - - if (!hasMatchingAlias) - return false; } else return false; } - // All of usages should match to the one of usage in the control foreach (var usage in expectedControlComponent.m_Usages) { if (!usage.isEmpty) { var usageCount = controlItem.usages.Count; - var anyUsageMatches = false; for (var i = 0; i < usageCount; ++i) { if (StringMatches(usage, controlItem.usages[i])) @@ -764,13 +763,11 @@ internal static bool MatchControlComponent(in ParsedPathComponent expectedContro break; } } - - if (!anyUsageMatches) - return false; } } - return true; + // Return whether or not we were able to match an alias or a usage + return controlItemNameMatched || anyUsageMatches; } /// diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 9c93be4ea4..ffe72fa82c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -126,11 +126,11 @@ private void DrawMatchingControlPaths() if (path == string.Empty) return; - var deviceLayoutPath = new InternedString(InputControlPath.TryGetDeviceLayout(path)); + var deviceLayoutPath = InputControlPath.TryGetDeviceLayout(path); var parsedPath = InputControlPath.Parse(path).ToArray(); // If the provided path is parseable into device and control components, draw UI which shows control layouts that match the path. - if (parsedPath.Length >= 2) + if (parsedPath.Length >= 2 && !string.IsNullOrEmpty(deviceLayoutPath)) { bool matchExists = false; @@ -143,7 +143,7 @@ private void DrawMatchingControlPaths() bool controlPathUsagePresent = parsedPath[1].usages.Count() > 0; bool hasChildDeviceLayouts = deviceLayoutPath == InputControlPath.Wildcard || EditorInputControlLayoutCache.HasChildLayouts(rootDeviceLayout.name); - // If the path provided is a specific device (i.e. has no ui-facing child device layouts or uses control usages), then exit early + // If the path provided matches exactly one control path (i.e. has no ui-facing child device layouts or uses control usages), then exit early if (!controlPathUsagePresent && !hasChildDeviceLayouts) return; @@ -154,10 +154,13 @@ private void DrawMatchingControlPaths() if (showMatchingLayouts) { - if (controlPathUsagePresent && deviceLayoutPath != InputControlPath.Wildcard) + // If our control path contains a usage, make sure we render the binding that belongs to the root device layout first + if (deviceLayoutPath != InputControlPath.Wildcard && controlPathUsagePresent) { matchExists |= DrawMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath, true); } + // Otherwise, just render the bindings that belong to child device layouts. The binding that matches the root layout is + // already represented by the user generated control path itself. else { IEnumerable matchedChildLayouts = Enumerable.Empty(); From edd86f94f62e19a6fc3d9495db2467e3e359ffd2 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Wed, 26 Jul 2023 14:16:03 -0700 Subject: [PATCH 29/32] style change to due to code analyzer results --- .../InputSystem/Controls/InputControlPath.cs | 2 +- .../Editor/AssetEditor/InputBindingPropertiesView.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index d5afd23932..2c2e80fbee 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -721,7 +721,7 @@ public static bool Matches(string expected, InputControl control) return MatchesRecursive(ref parser, control); } - internal static bool MatchControlComponent(in ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem, bool matchAlias = false) + internal static bool MatchControlComponent(ref ParsedPathComponent expectedControlComponent, ref InputControlLayout.ControlItem controlItem, bool matchAlias = false) { bool controlItemNameMatched = false; var anyUsageMatches = false; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index ffe72fa82c..94e596204a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -208,7 +208,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, for (int i = 0; i < deviceLayout.m_Controls.Length; i++) { ref InputControlLayout.ControlItem controlItem = ref deviceLayout.m_Controls[i]; - if (InputControlPath.MatchControlComponent(in parsedPath[1], ref controlItem, true)) + if (InputControlPath.MatchControlComponent(ref parsedPath[1], ref controlItem, true)) { // If we've already located a match, append a ", " to the control name // This is to accomodate cases where multiple control items match the same path within a single device layout @@ -228,7 +228,7 @@ private bool DrawMatchingControlPathsForLayout(InputControlLayout deviceLayout, for (int j = 0; j < controlLayout.m_Controls.Count(); j++) { ref InputControlLayout.ControlItem controlLayoutItem = ref controlLayout.m_Controls[j]; - if (InputControlPath.MatchControlComponent(in parsedPath[2], ref controlLayoutItem)) + if (InputControlPath.MatchControlComponent(ref parsedPath[2], ref controlLayoutItem)) { controlName += $"/{controlLayoutItem.name}"; matchExists = true; From 2beeb73a350574ffcbedbe88b26d69c2de95b4a5 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Wed, 26 Jul 2023 14:28:48 -0700 Subject: [PATCH 30/32] added missing in/ref changes --- Assets/Tests/InputSystem/CoreTests_Layouts.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Layouts.cs b/Assets/Tests/InputSystem/CoreTests_Layouts.cs index 97b03eaa97..38af2abaaa 100644 --- a/Assets/Tests/InputSystem/CoreTests_Layouts.cs +++ b/Assets/Tests/InputSystem/CoreTests_Layouts.cs @@ -2579,15 +2579,15 @@ public void Layouts_CanMatchControlPath() var layout = InputSystem.LoadLayout("DerivedLayout"); var parsedPath = InputControlPath.Parse("/ControlWithExplicitDefaultVariant").ToArray()[1]; - Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedPath, ref x)), Is.True); + Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(ref parsedPath, ref x)), Is.True); // Verify that we can match alias's when provided var parsedAliasPath = InputControlPath.Parse("/A").ToArray()[1]; - Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedAliasPath, ref x, true)), Is.True); + Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(ref parsedAliasPath, ref x, true)), Is.True); // Verify that we match usages when it is the only control path component provided var parsedUsagesPath = InputControlPath.Parse("/{Submit}").ToArray()[1]; - Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(in parsedUsagesPath, ref x)), Is.True); + Assert.That(layout.m_Controls.Any(x => InputControlPath.MatchControlComponent(ref parsedUsagesPath, ref x)), Is.True); } [Test] From 74bd2f9104ee47f98f89e5498e0adc0c2bbda1e6 Mon Sep 17 00:00:00 2001 From: "roger.liu" Date: Wed, 26 Jul 2023 14:38:09 -0700 Subject: [PATCH 31/32] fixing formatter nits --- .../InputSystem/Controls/InputControlPath.cs | 1 - .../Editor/AssetEditor/InputBindingPropertiesView.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs index 2c2e80fbee..d2b42d39b3 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlPath.cs @@ -734,7 +734,6 @@ internal static bool MatchControlComponent(ref ParsedPathComponent expectedContr controlItemNameMatched = true; else if (matchAlias) { - var aliases = controlItem.aliases; for (var i = 0; i < aliases.Count; i++) { diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs index 94e596204a..8050d99a7c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputBindingPropertiesView.cs @@ -159,7 +159,7 @@ private void DrawMatchingControlPaths() { matchExists |= DrawMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath, true); } - // Otherwise, just render the bindings that belong to child device layouts. The binding that matches the root layout is + // Otherwise, just render the bindings that belong to child device layouts. The binding that matches the root layout is // already represented by the user generated control path itself. else { From 5244c1d67af8302a5daa5ddcadac4e42009fc9fc Mon Sep 17 00:00:00 2001 From: James McGill Date: Mon, 7 Aug 2023 17:27:53 +0200 Subject: [PATCH 32/32] add changelog entry --- Packages/com.unity.inputsystem/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 5d42de8189..168f60c8ce 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -12,6 +12,7 @@ however, it has to be formatted properly to pass verification tests. ### Added - Preliminary support for visionOS. +- Show a list of `Derived Bindings` underneath the Binding Path editor to show all controls that matched. ### Changed - Changed the `InputAction` constructors so it generates an ID for the action and the optional binding parameter. This is intended to improve the serialization of input actions on behaviors when created through API when the property drawer in the Inspector window does not have a chance to generate an ID.