Skip to content

Commit 2a986f9

Browse files
CHANGE: Update asset Actions.inputactions to use UI Toolkit framework (#2394)
1 parent 534d719 commit 2a986f9

File tree

4 files changed

+233
-63
lines changed

4 files changed

+233
-63
lines changed

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
2222

2323
### Changed
2424

25+
- Changed the UI for `Actions.inputactions` asset to use UI Toolkit framework.
26+
2527
### Added
2628

2729
- Support for entering the play mode with domain reload turned off (i.e. Faster Enter Playmode feature) [ISX-2411]
Lines changed: 183 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#if UNITY_EDITOR
2-
using System;
32
using System.IO;
43
using UnityEngine.InputSystem.Utilities;
54
using UnityEditor;
65
using UnityEditor.AssetImporters;
6+
using UnityEngine.UIElements;
7+
using UnityEditor.UIElements;
78

89
////TODO: support for multi-editing
910

@@ -15,90 +16,215 @@ namespace UnityEngine.InputSystem.Editor
1516
[CustomEditor(typeof(InputActionImporter))]
1617
internal class InputActionImporterEditor : ScriptedImporterEditor
1718
{
18-
public override void OnInspectorGUI()
19+
public override VisualElement CreateInspectorGUI()
1920
{
21+
var root = new VisualElement();
22+
root.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(
23+
InputActionsEditorConstants.PackagePath +
24+
"/InputSystem/Editor/AssetImporter/InputActionImporterEditor.uss"));
2025
var inputActionAsset = GetAsset();
2126

2227
// ScriptedImporterEditor in 2019.2 now requires explicitly updating the SerializedObject
2328
// like in other types of editors.
2429
serializedObject.Update();
2530

26-
EditorGUILayout.Space();
27-
2831
if (inputActionAsset == null)
29-
EditorGUILayout.HelpBox("The currently selected object is not an editable input action asset.",
30-
MessageType.Info);
32+
{
33+
root.Add(new HelpBox(
34+
"The currently selected object is not an editable input action asset.",
35+
HelpBoxMessageType.Info));
36+
}
37+
38+
var editButton = new Button(() => OpenEditor(inputActionAsset))
39+
{
40+
text = GetOpenEditorButtonText(inputActionAsset)
41+
};
42+
editButton.AddToClassList("input-action-importer-editor__edit-button");
43+
editButton.SetEnabled(inputActionAsset != null);
44+
root.Add(editButton);
45+
46+
var projectWideContainer = new VisualElement();
47+
projectWideContainer.AddToClassList("input-action-importer-editor__project-wide-container");
48+
root.Add(projectWideContainer);
49+
BuildProjectWideSection(projectWideContainer, inputActionAsset);
50+
51+
BuildCodeGenerationSection(root, inputActionAsset);
52+
53+
root.Add(new IMGUIContainer(() =>
54+
{
55+
serializedObject.ApplyModifiedProperties();
56+
ApplyRevertGUI();
57+
}));
58+
59+
return root;
60+
}
61+
62+
private void BuildProjectWideSection(VisualElement container, InputActionAsset inputActionAsset)
63+
{
64+
container.Clear();
3165

32-
// Button to pop up window to edit the asset.
33-
using (new EditorGUI.DisabledScope(inputActionAsset == null))
66+
var currentActions = InputSystem.actions;
67+
68+
if (currentActions == inputActionAsset)
3469
{
35-
if (GUILayout.Button(GetOpenEditorButtonText(inputActionAsset), GUILayout.Height(30)))
36-
OpenEditor(inputActionAsset);
70+
container.Add(new HelpBox(
71+
"These actions are assigned as the Project-wide Input Actions.",
72+
HelpBoxMessageType.Info));
73+
return;
3774
}
3875

39-
EditorGUILayout.Space();
76+
var message = "These actions are not assigned as the Project-wide Input Actions for the Input System.";
77+
if (currentActions != null)
78+
{
79+
var currentPath = AssetDatabase.GetAssetPath(currentActions);
80+
if (!string.IsNullOrEmpty(currentPath))
81+
message += $" The actions currently assigned as the Project-wide Input Actions are: {currentPath}. ";
82+
}
4083

41-
// Project-wide Input Actions Asset UI.
42-
InputAssetEditorUtils.DrawMakeActiveGui(InputSystem.actions, inputActionAsset,
43-
inputActionAsset ? inputActionAsset.name : "Null", "Project-wide Input Actions",
44-
(value) => InputSystem.actions = value, !EditorApplication.isPlayingOrWillChangePlaymode);
84+
container.Add(new HelpBox(message, HelpBoxMessageType.Warning));
4585

46-
EditorGUILayout.Space();
86+
var assignButton = new Button(() =>
87+
{
88+
InputSystem.actions = inputActionAsset;
89+
BuildProjectWideSection(container, inputActionAsset);
90+
})
91+
{
92+
text = "Assign as the Project-wide Input Actions"
93+
};
94+
assignButton.AddToClassList("input-action-importer-editor__assign-button");
95+
assignButton.SetEnabled(!EditorApplication.isPlayingOrWillChangePlaymode);
96+
container.Add(assignButton);
97+
}
4798

48-
// Importer settings UI.
49-
var generateWrapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode");
50-
EditorGUILayout.PropertyField(generateWrapperCodeProperty, m_GenerateWrapperCodeLabel);
51-
if (generateWrapperCodeProperty.boolValue)
99+
private void BuildCodeGenerationSection(VisualElement root, InputActionAsset inputActionAsset)
100+
{
101+
var generateField = new PropertyField(
102+
serializedObject.FindProperty("m_GenerateWrapperCode"), "Generate C# Class");
103+
root.Add(generateField);
104+
105+
var codeGenContainer = new VisualElement();
106+
root.Add(codeGenContainer);
107+
108+
// File path with browse button
109+
string defaultFileName = "";
110+
if (inputActionAsset != null)
52111
{
53-
var wrapperCodePathProperty = serializedObject.FindProperty("m_WrapperCodePath");
54-
var wrapperClassNameProperty = serializedObject.FindProperty("m_WrapperClassName");
55-
var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace");
112+
var assetPath = AssetDatabase.GetAssetPath(inputActionAsset);
113+
defaultFileName = Path.ChangeExtension(assetPath, ".cs");
114+
}
115+
116+
var pathRow = new VisualElement();
117+
pathRow.AddToClassList("input-action-importer-editor__path-row");
118+
codeGenContainer.Add(pathRow);
56119

57-
EditorGUILayout.BeginHorizontal();
120+
var pathField = new TextField("C# Class File") { bindingPath = "m_WrapperCodePath" };
121+
pathField.AddToClassList("input-action-importer-editor__path-field");
122+
pathField.AddToClassList(BaseField<string>.alignedFieldUssClassName);
123+
SetupPlaceholder(pathField, defaultFileName);
124+
pathRow.Add(pathField);
58125

59-
string defaultFileName = "";
60-
if (inputActionAsset != null)
126+
var browseButton = new Button(() =>
127+
{
128+
var fileName = EditorUtility.SaveFilePanel("Location for generated C# file",
129+
Path.GetDirectoryName(defaultFileName),
130+
Path.GetFileName(defaultFileName), "cs");
131+
if (!string.IsNullOrEmpty(fileName))
61132
{
62-
var assetPath = AssetDatabase.GetAssetPath(inputActionAsset);
63-
defaultFileName = Path.ChangeExtension(assetPath, ".cs");
133+
if (fileName.StartsWith(Application.dataPath))
134+
fileName = "Assets/" + fileName.Substring(Application.dataPath.Length + 1);
135+
136+
var prop = serializedObject.FindProperty("m_WrapperCodePath");
137+
prop.stringValue = fileName;
138+
serializedObject.ApplyModifiedProperties();
64139
}
140+
})
141+
{
142+
text = "…"
143+
};
144+
browseButton.AddToClassList("input-action-importer-editor__browse-button");
145+
pathRow.Add(browseButton);
65146

66-
wrapperCodePathProperty.PropertyFieldWithDefaultText(m_WrapperCodePathLabel, defaultFileName);
147+
// Class name
148+
string typeName = inputActionAsset != null
149+
? CSharpCodeHelpers.MakeTypeName(inputActionAsset.name)
150+
: null;
67151

68-
if (GUILayout.Button("…", EditorStyles.miniButton, GUILayout.MaxWidth(20)))
69-
{
70-
var fileName = EditorUtility.SaveFilePanel("Location for generated C# file",
71-
Path.GetDirectoryName(defaultFileName),
72-
Path.GetFileName(defaultFileName), "cs");
73-
if (!string.IsNullOrEmpty(fileName))
74-
{
75-
if (fileName.StartsWith(Application.dataPath))
76-
fileName = "Assets/" + fileName.Substring(Application.dataPath.Length + 1);
77-
78-
wrapperCodePathProperty.stringValue = fileName;
79-
}
80-
}
81-
EditorGUILayout.EndHorizontal();
152+
var classNameField = new TextField("C# Class Name") { bindingPath = "m_WrapperClassName" };
153+
classNameField.AddToClassList(BaseField<string>.alignedFieldUssClassName);
154+
SetupPlaceholder(classNameField, typeName ?? "<Class name>");
155+
codeGenContainer.Add(classNameField);
82156

83-
string typeName = null;
84-
if (inputActionAsset != null)
85-
typeName = CSharpCodeHelpers.MakeTypeName(inputActionAsset?.name);
86-
wrapperClassNameProperty.PropertyFieldWithDefaultText(m_WrapperClassNameLabel, typeName ?? "<Class name>");
157+
var classNameError = new HelpBox("Must be a valid C# identifier", HelpBoxMessageType.Error);
158+
codeGenContainer.Add(classNameError);
87159

88-
if (!CSharpCodeHelpers.IsEmptyOrProperIdentifier(wrapperClassNameProperty.stringValue))
89-
EditorGUILayout.HelpBox("Must be a valid C# identifier", MessageType.Error);
160+
var classNameProp = serializedObject.FindProperty("m_WrapperClassName");
161+
classNameError.style.display = !CSharpCodeHelpers.IsEmptyOrProperIdentifier(classNameProp.stringValue)
162+
? DisplayStyle.Flex : DisplayStyle.None;
90163

91-
wrapperCodeNamespaceProperty.PropertyFieldWithDefaultText(m_WrapperCodeNamespaceLabel, "<Global namespace>");
164+
classNameField.RegisterValueChangedCallback(evt =>
165+
{
166+
classNameError.style.display = !CSharpCodeHelpers.IsEmptyOrProperIdentifier(evt.newValue)
167+
? DisplayStyle.Flex : DisplayStyle.None;
168+
});
92169

93-
if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue))
94-
EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error);
95-
}
170+
// Namespace
171+
var namespaceField = new TextField("C# Class Namespace") { bindingPath = "m_WrapperCodeNamespace" };
172+
namespaceField.AddToClassList(BaseField<string>.alignedFieldUssClassName);
173+
SetupPlaceholder(namespaceField, "<Global namespace>");
174+
codeGenContainer.Add(namespaceField);
175+
176+
var namespaceError = new HelpBox("Must be a valid C# namespace name", HelpBoxMessageType.Error);
177+
codeGenContainer.Add(namespaceError);
96178

97-
// Using ApplyRevertGUI requires calling Update and ApplyModifiedProperties around the serializedObject,
98-
// and will print warning messages otherwise (see warning message in ApplyRevertGUI implementation).
99-
serializedObject.ApplyModifiedProperties();
179+
var namespaceProp = serializedObject.FindProperty("m_WrapperCodeNamespace");
180+
namespaceError.style.display = !CSharpCodeHelpers.IsEmptyOrProperNamespaceName(namespaceProp.stringValue)
181+
? DisplayStyle.Flex : DisplayStyle.None;
100182

101-
ApplyRevertGUI();
183+
namespaceField.RegisterValueChangedCallback(evt =>
184+
{
185+
namespaceError.style.display = !CSharpCodeHelpers.IsEmptyOrProperNamespaceName(evt.newValue)
186+
? DisplayStyle.Flex : DisplayStyle.None;
187+
});
188+
189+
// Show/hide code gen fields based on toggle
190+
var generateProp = serializedObject.FindProperty("m_GenerateWrapperCode");
191+
codeGenContainer.style.display = generateProp.boolValue ? DisplayStyle.Flex : DisplayStyle.None;
192+
193+
generateField.RegisterValueChangeCallback(evt =>
194+
{
195+
codeGenContainer.style.display = evt.changedProperty.boolValue
196+
? DisplayStyle.Flex : DisplayStyle.None;
197+
});
198+
}
199+
200+
private static void SetupPlaceholder(TextField textField, string placeholder)
201+
{
202+
if (string.IsNullOrEmpty(placeholder))
203+
return;
204+
205+
var placeholderLabel = new Label(placeholder);
206+
placeholderLabel.pickingMode = PickingMode.Ignore;
207+
placeholderLabel.AddToClassList("input-action-importer-editor__placeholder");
208+
209+
textField.RegisterCallback<GeometryChangedEvent>(_ =>
210+
{
211+
var textInput = textField.Q("unity-text-input");
212+
if (textInput != null && placeholderLabel.parent != textInput)
213+
{
214+
textInput.Add(placeholderLabel);
215+
UpdatePlaceholder(textField, placeholderLabel);
216+
}
217+
});
218+
219+
textField.RegisterValueChangedCallback(_ => UpdatePlaceholder(textField, placeholderLabel));
220+
textField.RegisterCallback<FocusInEvent>(_ => placeholderLabel.style.display = DisplayStyle.None);
221+
textField.RegisterCallback<FocusOutEvent>(_ => UpdatePlaceholder(textField, placeholderLabel));
222+
}
223+
224+
private static void UpdatePlaceholder(TextField textField, Label placeholder)
225+
{
226+
placeholder.style.display = string.IsNullOrEmpty(textField.value)
227+
? DisplayStyle.Flex : DisplayStyle.None;
102228
}
103229

104230
private InputActionAsset GetAsset()
@@ -131,7 +257,6 @@ private string GetOpenEditorButtonText(InputActionAsset asset)
131257

132258
private static void OpenEditor(InputActionAsset asset)
133259
{
134-
// Redirect to Project-settings Input Actions editor if this is the project-wide actions asset
135260
if (IsProjectWideActionsAsset(asset))
136261
{
137262
SettingsService.OpenProjectSettings(InputSettingsPath.kSettingsRootPath);
@@ -140,11 +265,6 @@ private static void OpenEditor(InputActionAsset asset)
140265

141266
InputActionsEditorWindow.OpenEditor(asset);
142267
}
143-
144-
private readonly GUIContent m_GenerateWrapperCodeLabel = EditorGUIUtility.TrTextContent("Generate C# Class");
145-
private readonly GUIContent m_WrapperCodePathLabel = EditorGUIUtility.TrTextContent("C# Class File");
146-
private readonly GUIContent m_WrapperClassNameLabel = EditorGUIUtility.TrTextContent("C# Class Name");
147-
private readonly GUIContent m_WrapperCodeNamespaceLabel = EditorGUIUtility.TrTextContent("C# Class Namespace");
148268
}
149269
}
150270
#endif // UNITY_EDITOR
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.input-action-importer-editor__edit-button {
2+
min-height: 30px;
3+
margin-top: 4px;
4+
margin-bottom: 4px;
5+
white-space: normal;
6+
overflow: hidden;
7+
}
8+
9+
.input-action-importer-editor__assign-button {
10+
white-space: normal;
11+
overflow: hidden;
12+
}
13+
14+
.input-action-importer-editor__project-wide-container {
15+
margin-top: 6px;
16+
margin-bottom: 6px;
17+
}
18+
19+
.input-action-importer-editor__path-row {
20+
flex-direction: row;
21+
align-items: center;
22+
}
23+
24+
.input-action-importer-editor__path-field {
25+
flex-grow: 1;
26+
}
27+
28+
.input-action-importer-editor__browse-button {
29+
width: 25px;
30+
}
31+
32+
.input-action-importer-editor__placeholder {
33+
position: absolute;
34+
opacity: 0.5;
35+
padding-left: 2px;
36+
}

Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.uss.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)