UI 자동화를 위해 바인딩 기능 구현
- 유니티 에셋 인증 오류로 meta 재생성
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.InputSystem.Editor
|
||||
{
|
||||
internal class MatchingControlPath
|
||||
{
|
||||
public string deviceName
|
||||
{
|
||||
get;
|
||||
}
|
||||
public string controlName
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool isRoot
|
||||
{
|
||||
get;
|
||||
}
|
||||
public List<MatchingControlPath> children
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
|
||||
public MatchingControlPath(string deviceName, string controlName, bool isRoot)
|
||||
{
|
||||
this.deviceName = deviceName;
|
||||
this.controlName = controlName;
|
||||
this.isRoot = isRoot;
|
||||
this.children = new List<MatchingControlPath>();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public static List<TreeViewItemData<MatchingControlPath>> BuildMatchingControlPathsTreeData(List<MatchingControlPath> matchingControlPaths)
|
||||
{
|
||||
int id = 0;
|
||||
return BuildMatchingControlPathsTreeDataRecursive(ref id, matchingControlPaths);
|
||||
}
|
||||
|
||||
private static List<TreeViewItemData<MatchingControlPath>> BuildMatchingControlPathsTreeDataRecursive(ref int id, List<MatchingControlPath> matchingControlPaths)
|
||||
{
|
||||
var treeViewList = new List<TreeViewItemData<MatchingControlPath>>(matchingControlPaths.Count);
|
||||
foreach (var matchingControlPath in matchingControlPaths)
|
||||
{
|
||||
var childTreeViewList = BuildMatchingControlPathsTreeDataRecursive(ref id, matchingControlPath.children);
|
||||
|
||||
var treeViewItem = new TreeViewItemData<MatchingControlPath>(id++, matchingControlPath, childTreeViewList);
|
||||
treeViewList.Add(treeViewItem);
|
||||
}
|
||||
|
||||
return treeViewList;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public static List<MatchingControlPath> CollectMatchingControlPaths(string path, bool showPaths, ref bool controlPathUsagePresent)
|
||||
{
|
||||
var matchingControlPaths = new List<MatchingControlPath>();
|
||||
|
||||
if (path == string.Empty)
|
||||
return matchingControlPaths;
|
||||
|
||||
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 && !string.IsNullOrEmpty(deviceLayoutPath))
|
||||
{
|
||||
bool matchExists = false;
|
||||
|
||||
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 matchingControlPaths;
|
||||
|
||||
controlPathUsagePresent = parsedPath[1].usages.Count() > 0;
|
||||
bool hasChildDeviceLayouts = deviceLayoutPath == InputControlPath.Wildcard || EditorInputControlLayoutCache.HasChildLayouts(rootDeviceLayout.name);
|
||||
|
||||
// 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 matchingControlPaths;
|
||||
|
||||
// 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).
|
||||
|
||||
// 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 |= CollectMatchingControlPathsForLayout(rootDeviceLayout, in parsedPath, true, matchingControlPaths);
|
||||
}
|
||||
// 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<InputControlLayout> matchedChildLayouts = Enumerable.Empty<InputControlLayout>();
|
||||
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 childLayout in matchedChildLayouts)
|
||||
{
|
||||
matchExists |= CollectMatchingControlPathsForLayout(childLayout, in parsedPath, false, matchingControlPaths);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, indicate that no layouts match the current path.
|
||||
if (!matchExists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return matchingControlPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the deviceLayout or any of its children has controls which match the provided parsed path. exist matching registered control paths.
|
||||
/// </summary>
|
||||
/// <param name="deviceLayout">The device layout to draw control paths for</param>
|
||||
/// <param name="parsedPath">The parsed path containing details of the Input Controls that can be matched</param>
|
||||
private static bool CollectMatchingControlPathsForLayout(InputControlLayout deviceLayout, in InputControlPath.ParsedPathComponent[] parsedPath, bool isRoot, List<MatchingControlPath> matchingControlPaths)
|
||||
{
|
||||
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(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
|
||||
// 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.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)
|
||||
{
|
||||
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(ref parsedPath[2], ref controlLayoutItem))
|
||||
{
|
||||
controlName += $"/{controlLayoutItem.name}";
|
||||
matchExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
matchExists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<InputControlLayout> 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
|
||||
if (!matchExists)
|
||||
{
|
||||
foreach (var childLayout in matchedChildLayouts)
|
||||
{
|
||||
matchExists |= CollectMatchingControlPathsForLayout(childLayout, in parsedPath, false, matchingControlPaths);
|
||||
}
|
||||
}
|
||||
// Otherwise, draw the items for it, and then only process the child layouts if the foldout is expanded.
|
||||
else
|
||||
{
|
||||
var newMatchingControlPath = new MatchingControlPath(deviceName, controlName, isRoot);
|
||||
matchingControlPaths.Add(newMatchingControlPath);
|
||||
|
||||
foreach (var childLayout in matchedChildLayouts)
|
||||
{
|
||||
CollectMatchingControlPathsForLayout(childLayout, in parsedPath, false, newMatchingControlPath.children);
|
||||
}
|
||||
}
|
||||
|
||||
return matchExists;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user