From 1a6cb31f88e986475437708a076311645b7f095a Mon Sep 17 00:00:00 2001 From: Paulius Dervinis Date: Tue, 16 Jun 2026 10:37:24 +0300 Subject: [PATCH] FIX: Two clicks needed to move focus between parameter fields in the Input Actions editor [UUM-144339] In the Input Actions editor, moving focus directly from one interaction/processor parameter field to another (for example a Normalize processor's Min to its Max) required two clicks instead of one. Committing a parameter value calls SerializedProperty.ApplyModifiedProperties(), which StateContainer's TrackSerializedObjectValue callback turns into a full editor UI rebuild. That rebuild tore down and recreated the parameter field the click was moving focus into, so the first click was lost. NameAndParametersListView.OnParametersChanged now asks the StateContainer to skip the rebuild for that single value-only commit (StateContainer.IgnoreNextSerializedObjectChange), leaving the fields intact so focus moves on the first click. Structural edits (add/move/delete) do not opt out and still rebuild. Jira: https://jira.unity3d.com/browse/UUM-144339 --- Packages/com.unity.inputsystem/CHANGELOG.md | 1 + .../Editor/UITKAssetEditor/StateContainer.cs | 23 +++++++++++++++++++ .../Views/NameAndParametersListView.cs | 17 +++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 3453304eb6..e80a381a8b 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed +- Fixed the Input Actions editor requiring two clicks to move focus between processor or interaction parameter fields, because committing a parameter value rebuilt the parameter field UI mid-click [UUM-144339](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-144339) - Fixed `InputSystemProvider` disabling project-wide actions on shutdown when UI Toolkit destroys its objects mid-play. The provider now scopes its lifecycle to the UI action map only and does not disable project-wide actions [UUM-134130](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-134130) - Fixed `InputActionRebindingExtensions.GetBindingDisplayString(InputAction, InputBinding, ...)` returning an empty string for composite bindings when the binding mask filters by group [UUM-141423](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-141423) - Fixed `InputEventPtr.handled` not preventing actions from triggering when switching devices. The default event handled policy has been changed from `SuppressStateUpdates` (now deprecated) to `SuppressActionEventNotifications`, which keeps device state synchronized while suppressing action callbacks for handled events. [ISXB-1097](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1097) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs index 4a2af93675..d7dd16559f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs @@ -22,6 +22,12 @@ internal class StateContainer private InputActionsEditorState m_State; public readonly string assetGUID; + // When set, the next change detected by the TrackSerializedObjectValue callback below does not rebuild + // the editor UI. Used to commit value-only edits (e.g. an interaction/processor parameter) without + // tearing down and recreating the field the user is interacting with (UUM-144339). It is a one-shot: + // the very next change notification consumes it, so unrelated changes still rebuild as normal. + private bool m_IgnoreNextSerializedObjectChange; + public StateContainer(InputActionsEditorState initialState, string assetGUID) { m_State = initialState; @@ -52,6 +58,15 @@ public void Dispatch(Command command, UIRebuildMode editorRebuildMode = UIRebuil }); } + // Request that the editor UI is not rebuilt in response to the next change of the tracked serialized + // object. Call this immediately before committing a value-only edit (ApplyModifiedProperties) that must + // not tear down the VisualElement the user is interacting with. The request is consumed by the next + // change notification, so any subsequent change still rebuilds normally. + public void IgnoreNextSerializedObjectChange() + { + m_IgnoreNextSerializedObjectChange = true; + } + public void Initialize(VisualElement rootVisualElement) { // We need to use a root element for the TrackSerializedObjectValue that is destroyed with the view. @@ -62,6 +77,14 @@ public void Initialize(VisualElement rootVisualElement) m_RootVisualElement.Unbind(); m_RootVisualElement.TrackSerializedObjectValue(m_State.serializedObject, so => { + // A value-only edit asked us not to rebuild the UI for this change (see + // IgnoreNextSerializedObjectChange). Consume the request and skip the rebuild. + if (m_IgnoreNextSerializedObjectChange) + { + m_IgnoreNextSerializedObjectChange = false; + return; + } + StateChanged?.Invoke(m_State, UIRebuildMode.Rebuild); }); StateChanged?.Invoke(m_State, UIRebuildMode.Rebuild); diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs index c0f0bade83..cba44ac0e3 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs @@ -77,7 +77,22 @@ private void OnParametersChanged(ParameterListView listView, int index) { var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList(); interactionsOrProcessorsList[index] = new NameAndParameters { name = interactionsOrProcessorsList[index].name, parameters = listView.GetParameters() }; - m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); + var newValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); + + // onChange also fires on a plain blur with no edit, leaving the serialized string unchanged. Bail before + // arming the ignore flag below: ApplyModifiedProperties would be a no-op that never fires the + // TrackSerializedObjectValue callback, so the flag would stay armed and wrongly swallow the next real rebuild. + if (m_ListProperty.stringValue == newValue) + return; + + m_ListProperty.stringValue = newValue; + + // This is a value-only edit of an existing interaction/processor: the parameter fields are already + // showing the new value. A full UI rebuild here would tear down and recreate the field the user is + // moving focus into, forcing a second click (UUM-144339). Suppress the rebuild that committing the + // value would otherwise trigger via StateContainer's TrackSerializedObjectValue callback. Structural + // edits (add/move/delete) intentionally do not call this and still rebuild. + stateContainer.IgnoreNextSerializedObjectChange(); m_ListProperty.serializedObject.ApplyModifiedProperties(); }