From 46264f454062e96b83f138b560ce3c27dd1600f3 Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Sun, 22 Mar 2026 19:40:23 +0100 Subject: [PATCH 1/5] Update InputActionImporterEditor.cs --- .../InputActionImporterEditor.cs | 249 +++++++++++++----- 1 file changed, 183 insertions(+), 66 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs index f117089189..329e38535b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs @@ -1,9 +1,10 @@ #if UNITY_EDITOR -using System; using System.IO; using UnityEngine.InputSystem.Utilities; using UnityEditor; using UnityEditor.AssetImporters; +using UnityEngine.UIElements; +using UnityEditor.UIElements; ////TODO: support for multi-editing @@ -15,90 +16,212 @@ namespace UnityEngine.InputSystem.Editor [CustomEditor(typeof(InputActionImporter))] internal class InputActionImporterEditor : ScriptedImporterEditor { - public override void OnInspectorGUI() + public override VisualElement CreateInspectorGUI() { + var root = new VisualElement(); var inputActionAsset = GetAsset(); - // ScriptedImporterEditor in 2019.2 now requires explicitly updating the SerializedObject - // like in other types of editors. - serializedObject.Update(); + if (inputActionAsset == null) + { + root.Add(new HelpBox( + "The currently selected object is not an editable input action asset.", + HelpBoxMessageType.Info)); + } - EditorGUILayout.Space(); + var editButton = new Button(() => OpenEditor(inputActionAsset)) + { + text = GetOpenEditorButtonText(inputActionAsset) + }; + editButton.style.height = 30; + editButton.style.marginTop = 4; + editButton.style.marginBottom = 4; + editButton.SetEnabled(inputActionAsset != null); + root.Add(editButton); - if (inputActionAsset == null) - EditorGUILayout.HelpBox("The currently selected object is not an editable input action asset.", - MessageType.Info); + var projectWideContainer = new VisualElement(); + projectWideContainer.style.marginTop = 6; + projectWideContainer.style.marginBottom = 6; + root.Add(projectWideContainer); + BuildProjectWideSection(projectWideContainer, inputActionAsset); + + BuildCodeGenerationSection(root, inputActionAsset); + + root.Add(new IMGUIContainer(() => + { + serializedObject.ApplyModifiedProperties(); + ApplyRevertGUI(); + })); + + return root; + } + + private void BuildProjectWideSection(VisualElement container, InputActionAsset inputActionAsset) + { + container.Clear(); + + var currentActions = InputSystem.actions; - // Button to pop up window to edit the asset. - using (new EditorGUI.DisabledScope(inputActionAsset == null)) + if (currentActions == inputActionAsset) { - if (GUILayout.Button(GetOpenEditorButtonText(inputActionAsset), GUILayout.Height(30))) - OpenEditor(inputActionAsset); + container.Add(new HelpBox( + "These actions are assigned as the Project-wide Input Actions.", + HelpBoxMessageType.Info)); + return; + } + + var message = "These actions are not assigned as the Project-wide Input Actions for the Input System."; + if (currentActions != null) + { + var currentPath = AssetDatabase.GetAssetPath(currentActions); + if (!string.IsNullOrEmpty(currentPath)) + message += $" The actions currently assigned as the Project-wide Input Actions are: {currentPath}. "; } - EditorGUILayout.Space(); + container.Add(new HelpBox(message, HelpBoxMessageType.Warning)); + + var assignButton = new Button(() => + { + InputSystem.actions = inputActionAsset; + BuildProjectWideSection(container, inputActionAsset); + }) + { + text = "Assign as the Project-wide Input Actions" + }; + assignButton.SetEnabled(!EditorApplication.isPlayingOrWillChangePlaymode); + container.Add(assignButton); + } - // Project-wide Input Actions Asset UI. - InputAssetEditorUtils.DrawMakeActiveGui(InputSystem.actions, inputActionAsset, - inputActionAsset ? inputActionAsset.name : "Null", "Project-wide Input Actions", - (value) => InputSystem.actions = value, !EditorApplication.isPlayingOrWillChangePlaymode); + private void BuildCodeGenerationSection(VisualElement root, InputActionAsset inputActionAsset) + { + var generateField = new PropertyField( + serializedObject.FindProperty("m_GenerateWrapperCode"), "Generate C# Class"); + root.Add(generateField); - EditorGUILayout.Space(); + var codeGenContainer = new VisualElement(); + root.Add(codeGenContainer); - // Importer settings UI. - var generateWrapperCodeProperty = serializedObject.FindProperty("m_GenerateWrapperCode"); - EditorGUILayout.PropertyField(generateWrapperCodeProperty, m_GenerateWrapperCodeLabel); - if (generateWrapperCodeProperty.boolValue) + // File path with browse button + string defaultFileName = ""; + if (inputActionAsset != null) { - var wrapperCodePathProperty = serializedObject.FindProperty("m_WrapperCodePath"); - var wrapperClassNameProperty = serializedObject.FindProperty("m_WrapperClassName"); - var wrapperCodeNamespaceProperty = serializedObject.FindProperty("m_WrapperCodeNamespace"); + var assetPath = AssetDatabase.GetAssetPath(inputActionAsset); + defaultFileName = Path.ChangeExtension(assetPath, ".cs"); + } + + var pathRow = new VisualElement(); + pathRow.style.flexDirection = FlexDirection.Row; + pathRow.style.alignItems = Align.Center; + codeGenContainer.Add(pathRow); - EditorGUILayout.BeginHorizontal(); + var pathField = new TextField("C# Class File") { bindingPath = "m_WrapperCodePath" }; + pathField.style.flexGrow = 1; + SetupPlaceholder(pathField, defaultFileName); + pathRow.Add(pathField); - string defaultFileName = ""; - if (inputActionAsset != null) + var browseButton = new Button(() => + { + var fileName = EditorUtility.SaveFilePanel("Location for generated C# file", + Path.GetDirectoryName(defaultFileName), + Path.GetFileName(defaultFileName), "cs"); + if (!string.IsNullOrEmpty(fileName)) { - var assetPath = AssetDatabase.GetAssetPath(inputActionAsset); - defaultFileName = Path.ChangeExtension(assetPath, ".cs"); + if (fileName.StartsWith(Application.dataPath)) + fileName = "Assets/" + fileName.Substring(Application.dataPath.Length + 1); + + var prop = serializedObject.FindProperty("m_WrapperCodePath"); + prop.stringValue = fileName; + serializedObject.ApplyModifiedProperties(); } + }) + { + text = "…" + }; + browseButton.style.width = 25; + browseButton.style.minWidth = 25; + pathRow.Add(browseButton); - wrapperCodePathProperty.PropertyFieldWithDefaultText(m_WrapperCodePathLabel, defaultFileName); + // Class name + string typeName = inputActionAsset != null + ? CSharpCodeHelpers.MakeTypeName(inputActionAsset.name) + : null; - if (GUILayout.Button("…", EditorStyles.miniButton, GUILayout.MaxWidth(20))) - { - var fileName = EditorUtility.SaveFilePanel("Location for generated C# file", - Path.GetDirectoryName(defaultFileName), - Path.GetFileName(defaultFileName), "cs"); - if (!string.IsNullOrEmpty(fileName)) - { - if (fileName.StartsWith(Application.dataPath)) - fileName = "Assets/" + fileName.Substring(Application.dataPath.Length + 1); - - wrapperCodePathProperty.stringValue = fileName; - } - } - EditorGUILayout.EndHorizontal(); + var classNameField = new TextField("C# Class Name") { bindingPath = "m_WrapperClassName" }; + SetupPlaceholder(classNameField, typeName ?? ""); + codeGenContainer.Add(classNameField); - string typeName = null; - if (inputActionAsset != null) - typeName = CSharpCodeHelpers.MakeTypeName(inputActionAsset?.name); - wrapperClassNameProperty.PropertyFieldWithDefaultText(m_WrapperClassNameLabel, typeName ?? ""); + var classNameError = new HelpBox("Must be a valid C# identifier", HelpBoxMessageType.Error); + codeGenContainer.Add(classNameError); - if (!CSharpCodeHelpers.IsEmptyOrProperIdentifier(wrapperClassNameProperty.stringValue)) - EditorGUILayout.HelpBox("Must be a valid C# identifier", MessageType.Error); + var classNameProp = serializedObject.FindProperty("m_WrapperClassName"); + classNameError.style.display = !CSharpCodeHelpers.IsEmptyOrProperIdentifier(classNameProp.stringValue) + ? DisplayStyle.Flex : DisplayStyle.None; - wrapperCodeNamespaceProperty.PropertyFieldWithDefaultText(m_WrapperCodeNamespaceLabel, ""); + classNameField.RegisterValueChangedCallback(evt => + { + classNameError.style.display = !CSharpCodeHelpers.IsEmptyOrProperIdentifier(evt.newValue) + ? DisplayStyle.Flex : DisplayStyle.None; + }); - if (!CSharpCodeHelpers.IsEmptyOrProperNamespaceName(wrapperCodeNamespaceProperty.stringValue)) - EditorGUILayout.HelpBox("Must be a valid C# namespace name", MessageType.Error); - } + // Namespace + var namespaceField = new TextField("C# Class Namespace") { bindingPath = "m_WrapperCodeNamespace" }; + SetupPlaceholder(namespaceField, ""); + codeGenContainer.Add(namespaceField); + + var namespaceError = new HelpBox("Must be a valid C# namespace name", HelpBoxMessageType.Error); + codeGenContainer.Add(namespaceError); - // Using ApplyRevertGUI requires calling Update and ApplyModifiedProperties around the serializedObject, - // and will print warning messages otherwise (see warning message in ApplyRevertGUI implementation). - serializedObject.ApplyModifiedProperties(); + var namespaceProp = serializedObject.FindProperty("m_WrapperCodeNamespace"); + namespaceError.style.display = !CSharpCodeHelpers.IsEmptyOrProperNamespaceName(namespaceProp.stringValue) + ? DisplayStyle.Flex : DisplayStyle.None; - ApplyRevertGUI(); + namespaceField.RegisterValueChangedCallback(evt => + { + namespaceError.style.display = !CSharpCodeHelpers.IsEmptyOrProperNamespaceName(evt.newValue) + ? DisplayStyle.Flex : DisplayStyle.None; + }); + + // Show/hide code gen fields based on toggle + var generateProp = serializedObject.FindProperty("m_GenerateWrapperCode"); + codeGenContainer.style.display = generateProp.boolValue ? DisplayStyle.Flex : DisplayStyle.None; + + generateField.RegisterValueChangeCallback(evt => + { + codeGenContainer.style.display = evt.changedProperty.boolValue + ? DisplayStyle.Flex : DisplayStyle.None; + }); + } + + private static void SetupPlaceholder(TextField textField, string placeholder) + { + if (string.IsNullOrEmpty(placeholder)) + return; + + var placeholderLabel = new Label(placeholder); + placeholderLabel.pickingMode = PickingMode.Ignore; + placeholderLabel.style.position = Position.Absolute; + placeholderLabel.style.unityTextAlign = TextAnchor.MiddleLeft; + placeholderLabel.style.opacity = 0.5f; + placeholderLabel.style.paddingLeft = 2; + + textField.RegisterCallback(_ => + { + var textInput = textField.Q("unity-text-input"); + if (textInput != null && placeholderLabel.parent != textInput) + { + textInput.Add(placeholderLabel); + UpdatePlaceholder(textField, placeholderLabel); + } + }); + + textField.RegisterValueChangedCallback(_ => UpdatePlaceholder(textField, placeholderLabel)); + textField.RegisterCallback(_ => placeholderLabel.style.display = DisplayStyle.None); + textField.RegisterCallback(_ => UpdatePlaceholder(textField, placeholderLabel)); + } + + private static void UpdatePlaceholder(TextField textField, Label placeholder) + { + placeholder.style.display = string.IsNullOrEmpty(textField.value) + ? DisplayStyle.Flex : DisplayStyle.None; } private InputActionAsset GetAsset() @@ -131,7 +254,6 @@ private string GetOpenEditorButtonText(InputActionAsset asset) private static void OpenEditor(InputActionAsset asset) { - // Redirect to Project-settings Input Actions editor if this is the project-wide actions asset if (IsProjectWideActionsAsset(asset)) { SettingsService.OpenProjectSettings(InputSettingsPath.kSettingsRootPath); @@ -140,11 +262,6 @@ private static void OpenEditor(InputActionAsset asset) InputActionsEditorWindow.OpenEditor(asset); } - - private readonly GUIContent m_GenerateWrapperCodeLabel = EditorGUIUtility.TrTextContent("Generate C# Class"); - private readonly GUIContent m_WrapperCodePathLabel = EditorGUIUtility.TrTextContent("C# Class File"); - private readonly GUIContent m_WrapperClassNameLabel = EditorGUIUtility.TrTextContent("C# Class Name"); - private readonly GUIContent m_WrapperCodeNamespaceLabel = EditorGUIUtility.TrTextContent("C# Class Namespace"); } } #endif // UNITY_EDITOR From 4a7eb4871f32dc46ade12fbba9e909af9f5f2438 Mon Sep 17 00:00:00 2001 From: josepmariapujol-unity <59828124+josepmariapujol-unity@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:46:27 +0100 Subject: [PATCH 2/5] Update SerializedObject in InputActionImporterEditor Explicitly update the SerializedObject in InputActionImporterEditor. --- .../Editor/AssetImporter/InputActionImporterEditor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs index 329e38535b..000035f85f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs @@ -21,6 +21,10 @@ public override VisualElement CreateInspectorGUI() var root = new VisualElement(); var inputActionAsset = GetAsset(); + // ScriptedImporterEditor in 2019.2 now requires explicitly updating the SerializedObject + // like in other types of editors. + serializedObject.Update(); + if (inputActionAsset == null) { root.Add(new HelpBox( From 55b4cfa70f211a59ba5cf875656e839b10695a49 Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Mon, 23 Mar 2026 14:48:43 +0100 Subject: [PATCH 3/5] Update InputActionImporterEditor.cs --- .../Editor/AssetImporter/InputActionImporterEditor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs index 000035f85f..53f3f5b0a0 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs @@ -119,6 +119,7 @@ private void BuildCodeGenerationSection(VisualElement root, InputActionAsset inp var pathField = new TextField("C# Class File") { bindingPath = "m_WrapperCodePath" }; pathField.style.flexGrow = 1; + pathField.AddToClassList(BaseField.alignedFieldUssClassName); SetupPlaceholder(pathField, defaultFileName); pathRow.Add(pathField); @@ -150,6 +151,7 @@ private void BuildCodeGenerationSection(VisualElement root, InputActionAsset inp : null; var classNameField = new TextField("C# Class Name") { bindingPath = "m_WrapperClassName" }; + classNameField.AddToClassList(BaseField.alignedFieldUssClassName); SetupPlaceholder(classNameField, typeName ?? ""); codeGenContainer.Add(classNameField); @@ -168,6 +170,7 @@ private void BuildCodeGenerationSection(VisualElement root, InputActionAsset inp // Namespace var namespaceField = new TextField("C# Class Namespace") { bindingPath = "m_WrapperCodeNamespace" }; + namespaceField.AddToClassList(BaseField.alignedFieldUssClassName); SetupPlaceholder(namespaceField, ""); codeGenContainer.Add(namespaceField); From 7dce52eaa2bd9e81155d1d5885cf77646af091c4 Mon Sep 17 00:00:00 2001 From: josepmariapujol-unity <59828124+josepmariapujol-unity@users.noreply.github.com> Date: Tue, 24 Mar 2026 11:50:37 +0100 Subject: [PATCH 4/5] Update CHANGELOG.md --- Packages/com.unity.inputsystem/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 47a24edf7e..f8a37e6b38 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed +- Changed the UI for `Actions.inputactions` asset to use UI Toolkit framework. + ### Added - Support for entering the play mode with domain reload turned off (i.e. Faster Enter Playmode feature) [ISX-2411] From 01d9ba55b68acaf230f4acaad98dbd81124ea55b Mon Sep 17 00:00:00 2001 From: JosepMariaPujol Date: Tue, 24 Mar 2026 12:26:29 +0100 Subject: [PATCH 5/5] Update InputActionImporterEditor.cs --- .../Editor/AssetImporter/InputActionImporterEditor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs index 53f3f5b0a0..9b66e440b7 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporterEditor.cs @@ -142,7 +142,6 @@ private void BuildCodeGenerationSection(VisualElement root, InputActionAsset inp text = "…" }; browseButton.style.width = 25; - browseButton.style.minWidth = 25; pathRow.Add(browseButton); // Class name @@ -206,7 +205,6 @@ private static void SetupPlaceholder(TextField textField, string placeholder) var placeholderLabel = new Label(placeholder); placeholderLabel.pickingMode = PickingMode.Ignore; placeholderLabel.style.position = Position.Absolute; - placeholderLabel.style.unityTextAlign = TextAnchor.MiddleLeft; placeholderLabel.style.opacity = 0.5f; placeholderLabel.style.paddingLeft = 2;