Skip to content

Commit 46264f4

Browse files
Update InputActionImporterEditor.cs
1 parent 0bb75c4 commit 46264f4

1 file changed

Lines changed: 183 additions & 66 deletions

File tree

Lines changed: 183 additions & 66 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,212 @@ 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();
2022
var inputActionAsset = GetAsset();
2123

22-
// ScriptedImporterEditor in 2019.2 now requires explicitly updating the SerializedObject
23-
// like in other types of editors.
24-
serializedObject.Update();
24+
if (inputActionAsset == null)
25+
{
26+
root.Add(new HelpBox(
27+
"The currently selected object is not an editable input action asset.",
28+
HelpBoxMessageType.Info));
29+
}
2530

26-
EditorGUILayout.Space();
31+
var editButton = new Button(() => OpenEditor(inputActionAsset))
32+
{
33+
text = GetOpenEditorButtonText(inputActionAsset)
34+
};
35+
editButton.style.height = 30;
36+
editButton.style.marginTop = 4;
37+
editButton.style.marginBottom = 4;
38+
editButton.SetEnabled(inputActionAsset != null);
39+
root.Add(editButton);
2740

28-
if (inputActionAsset == null)
29-
EditorGUILayout.HelpBox("The currently selected object is not an editable input action asset.",
30-
MessageType.Info);
41+
var projectWideContainer = new VisualElement();
42+
projectWideContainer.style.marginTop = 6;
43+
projectWideContainer.style.marginBottom = 6;
44+
root.Add(projectWideContainer);
45+
BuildProjectWideSection(projectWideContainer, inputActionAsset);
46+
47+
BuildCodeGenerationSection(root, inputActionAsset);
48+
49+
root.Add(new IMGUIContainer(() =>
50+
{
51+
serializedObject.ApplyModifiedProperties();
52+
ApplyRevertGUI();
53+
}));
54+
55+
return root;
56+
}
57+
58+
private void BuildProjectWideSection(VisualElement container, InputActionAsset inputActionAsset)
59+
{
60+
container.Clear();
61+
62+
var currentActions = InputSystem.actions;
3163

32-
// Button to pop up window to edit the asset.
33-
using (new EditorGUI.DisabledScope(inputActionAsset == null))
64+
if (currentActions == inputActionAsset)
3465
{
35-
if (GUILayout.Button(GetOpenEditorButtonText(inputActionAsset), GUILayout.Height(30)))
36-
OpenEditor(inputActionAsset);
66+
container.Add(new HelpBox(
67+
"These actions are assigned as the Project-wide Input Actions.",
68+
HelpBoxMessageType.Info));
69+
return;
70+
}
71+
72+
var message = "These actions are not assigned as the Project-wide Input Actions for the Input System.";
73+
if (currentActions != null)
74+
{
75+
var currentPath = AssetDatabase.GetAssetPath(currentActions);
76+
if (!string.IsNullOrEmpty(currentPath))
77+
message += $" The actions currently assigned as the Project-wide Input Actions are: {currentPath}. ";
3778
}
3879

39-
EditorGUILayout.Space();
80+
container.Add(new HelpBox(message, HelpBoxMessageType.Warning));
81+
82+
var assignButton = new Button(() =>
83+
{
84+
InputSystem.actions = inputActionAsset;
85+
BuildProjectWideSection(container, inputActionAsset);
86+
})
87+
{
88+
text = "Assign as the Project-wide Input Actions"
89+
};
90+
assignButton.SetEnabled(!EditorApplication.isPlayingOrWillChangePlaymode);
91+
container.Add(assignButton);
92+
}
4093

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);
94+
private void BuildCodeGenerationSection(VisualElement root, InputActionAsset inputActionAsset)
95+
{
96+
var generateField = new PropertyField(
97+
serializedObject.FindProperty("m_GenerateWrapperCode"), "Generate C# Class");
98+
root.Add(generateField);
4599

46-
EditorGUILayout.Space();
100+
var codeGenContainer = new VisualElement();
101+
root.Add(codeGenContainer);
47102

48-
// Importer settings UI.
49-
var generateWrapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode");
50-
EditorGUILayout.PropertyField(generateWrapperCodeProperty, m_GenerateWrapperCodeLabel);
51-
if (generateWrapperCodeProperty.boolValue)
103+
// File path with browse button
104+
string defaultFileName = "";
105+
if (inputActionAsset != null)
52106
{
53-
var wrapperCodePathProperty = serializedObject.FindProperty("m_WrapperCodePath");
54-
var wrapperClassNameProperty = serializedObject.FindProperty("m_WrapperClassName");
55-
var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace");
107+
var assetPath = AssetDatabase.GetAssetPath(inputActionAsset);
108+
defaultFileName = Path.ChangeExtension(assetPath, ".cs");
109+
}
110+
111+
var pathRow = new VisualElement();
112+
pathRow.style.flexDirection = FlexDirection.Row;
113+
pathRow.style.alignItems = Align.Center;
114+
codeGenContainer.Add(pathRow);
56115

57-
EditorGUILayout.BeginHorizontal();
116+
var pathField = new TextField("C# Class File") { bindingPath = "m_WrapperCodePath" };
117+
pathField.style.flexGrow = 1;
118+
SetupPlaceholder(pathField, defaultFileName);
119+
pathRow.Add(pathField);
58120

59-
string defaultFileName = "";
60-
if (inputActionAsset != null)
121+
var browseButton = new Button(() =>
122+
{
123+
var fileName = EditorUtility.SaveFilePanel("Location for generated C# file",
124+
Path.GetDirectoryName(defaultFileName),
125+
Path.GetFileName(defaultFileName), "cs");
126+
if (!string.IsNullOrEmpty(fileName))
61127
{
62-
var assetPath = AssetDatabase.GetAssetPath(inputActionAsset);
63-
defaultFileName = Path.ChangeExtension(assetPath, ".cs");
128+
if (fileName.StartsWith(Application.dataPath))
129+
fileName = "Assets/" + fileName.Substring(Application.dataPath.Length + 1);
130+
131+
var prop = serializedObject.FindProperty("m_WrapperCodePath");
132+
prop.stringValue = fileName;
133+
serializedObject.ApplyModifiedProperties();
64134
}
135+
})
136+
{
137+
text = "…"
138+
};
139+
browseButton.style.width = 25;
140+
browseButton.style.minWidth = 25;
141+
pathRow.Add(browseButton);
65142

66-
wrapperCodePathProperty.PropertyFieldWithDefaultText(m_WrapperCodePathLabel, defaultFileName);
143+
// Class name
144+
string typeName = inputActionAsset != null
145+
? CSharpCodeHelpers.MakeTypeName(inputActionAsset.name)
146+
: null;
67147

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();
148+
var classNameField = new TextField("C# Class Name") { bindingPath = "m_WrapperClassName" };
149+
SetupPlaceholder(classNameField, typeName ?? "<Class name>");
150+
codeGenContainer.Add(classNameField);
82151

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

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

91-
wrapperCodeNamespaceProperty.PropertyFieldWithDefaultText(m_WrapperCodeNamespaceLabel, "<Global namespace>");
159+
classNameField.RegisterValueChangedCallback(evt =>
160+
{
161+
classNameError.style.display = !CSharpCodeHelpers.IsEmptyOrProperIdentifier(evt.newValue)
162+
? DisplayStyle.Flex : DisplayStyle.None;
163+
});
92164

93-
if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue))
94-
EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error);
95-
}
165+
// Namespace
166+
var namespaceField = new TextField("C# Class Namespace") { bindingPath = "m_WrapperCodeNamespace" };
167+
SetupPlaceholder(namespaceField, "<Global namespace>");
168+
codeGenContainer.Add(namespaceField);
169+
170+
var namespaceError = new HelpBox("Must be a valid C# namespace name", HelpBoxMessageType.Error);
171+
codeGenContainer.Add(namespaceError);
96172

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();
173+
var namespaceProp = serializedObject.FindProperty("m_WrapperCodeNamespace");
174+
namespaceError.style.display = !CSharpCodeHelpers.IsEmptyOrProperNamespaceName(namespaceProp.stringValue)
175+
? DisplayStyle.Flex : DisplayStyle.None;
100176

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

104227
private InputActionAsset GetAsset()
@@ -131,7 +254,6 @@ private string GetOpenEditorButtonText(InputActionAsset asset)
131254

132255
private static void OpenEditor(InputActionAsset asset)
133256
{
134-
// Redirect to Project-settings Input Actions editor if this is the project-wide actions asset
135257
if (IsProjectWideActionsAsset(asset))
136258
{
137259
SettingsService.OpenProjectSettings(InputSettingsPath.kSettingsRootPath);
@@ -140,11 +262,6 @@ private static void OpenEditor(InputActionAsset asset)
140262

141263
InputActionsEditorWindow.OpenEditor(asset);
142264
}
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");
148265
}
149266
}
150267
#endif // UNITY_EDITOR

0 commit comments

Comments
 (0)