diff --git a/Assets/Tests/InputSystem/Plugins/UITests.cs b/Assets/Tests/InputSystem/Plugins/UITests.cs
index 1b94f75ab8..2928db8c8a 100644
--- a/Assets/Tests/InputSystem/Plugins/UITests.cs
+++ b/Assets/Tests/InputSystem/Plugins/UITests.cs
@@ -2911,10 +2911,16 @@ public IEnumerator UI_CanDriveUIFromGamepad()
Assert.That(scene.leftChildReceiver.events,
EventSequence(
OneEvent("type", EventType.Move),
+ OneEvent("device", gamepad),
OneEvent("moveDir", MoveDirection.Right),
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
Assert.That(scene.rightChildReceiver.events, Is.Empty);
+#if UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE
+ Assert.That(scene.uiModule.GetNavigationEventDeviceType(scene.leftChildReceiver.events[0].data),
+ Is.EqualTo(NavigationDeviceType.NonKeyboard));
+#endif
+
scene.leftChildReceiver.events.Clear();
// Move left.
@@ -2924,6 +2930,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
Assert.That(scene.leftChildReceiver.events,
EventSequence(
OneEvent("type", EventType.Move),
+ OneEvent("device", gamepad),
OneEvent("moveDir", MoveDirection.Left),
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
Assert.That(scene.rightChildReceiver.events, Is.Empty);
@@ -2937,6 +2944,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
Assert.That(scene.leftChildReceiver.events,
EventSequence(
OneEvent("type", EventType.Move),
+ OneEvent("device", gamepad),
OneEvent("moveDir", MoveDirection.Up),
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
Assert.That(scene.rightChildReceiver.events, Is.Empty);
@@ -2950,6 +2958,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
Assert.That(scene.leftChildReceiver.events,
EventSequence(
OneEvent("type", EventType.Move),
+ OneEvent("device", gamepad),
OneEvent("moveDir", MoveDirection.Down),
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
Assert.That(scene.rightChildReceiver.events, Is.Empty);
@@ -2964,6 +2973,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
Assert.That(scene.leftChildReceiver.events,
EventSequence(
OneEvent("type", EventType.Move),
+ OneEvent("device", gamepad),
OneEvent("moveDir", MoveDirection.Down),
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
@@ -2977,6 +2987,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
Assert.That(scene.leftChildReceiver.events,
EventSequence(
OneEvent("type", EventType.Move),
+ OneEvent("device", gamepad),
OneEvent("moveDir", MoveDirection.Down),
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
@@ -2986,7 +2997,12 @@ public IEnumerator UI_CanDriveUIFromGamepad()
PressAndRelease(gamepad.buttonSouth);
yield return null;
- Assert.That(scene.leftChildReceiver.events, EventSequence(OneEvent("type", EventType.Submit)));
+ Assert.That(scene.leftChildReceiver.events,
+ EventSequence(
+ OneEvent("type", EventType.Submit),
+ OneEvent("device", gamepad)
+ )
+ );
Assert.That(scene.rightChildReceiver.events, Is.Empty);
scene.leftChildReceiver.events.Clear();
@@ -2995,7 +3011,12 @@ public IEnumerator UI_CanDriveUIFromGamepad()
PressAndRelease(gamepad.buttonEast);
yield return null;
- Assert.That(scene.leftChildReceiver.events, EventSequence(OneEvent("type", EventType.Cancel)));
+ Assert.That(scene.leftChildReceiver.events,
+ EventSequence(
+ OneEvent("type", EventType.Cancel),
+ OneEvent("device", gamepad)
+ )
+ );
Assert.That(scene.rightChildReceiver.events, Is.Empty);
scene.leftChildReceiver.events.Clear();
@@ -4463,6 +4484,7 @@ public struct Event
public BaseEventData data { get; }
public AxisEventData axisData => (AxisEventData)data;
public ExtendedPointerEventData pointerData => (ExtendedPointerEventData)data;
+ public INavigationEventData navigationData => (INavigationEventData)data;
public Event(EventType type, BaseEventData data)
{
@@ -4521,12 +4543,12 @@ public void OnMove(AxisEventData eventData)
public void OnSubmit(BaseEventData eventData)
{
- events.Add(new Event(EventType.Submit, null));
+ events.Add(new Event(EventType.Submit, CloneSubmitCancelEventData(eventData)));
}
public void OnCancel(BaseEventData eventData)
{
- events.Add(new Event(EventType.Cancel, null));
+ events.Add(new Event(EventType.Cancel, CloneSubmitCancelEventData(eventData)));
}
public void OnSelect(BaseEventData eventData)
@@ -4579,11 +4601,20 @@ private static AxisEventData CloneAxisEventData(AxisEventData eventData)
{
return new ExtendedAxisEventData(EventSystem.current)
{
+ device = (eventData as ExtendedAxisEventData)?.device,
moveVector = eventData.moveVector,
moveDir = eventData.moveDir
};
}
+ private static ExtendedSubmitCancelEventData CloneSubmitCancelEventData(BaseEventData eventData)
+ {
+ return new ExtendedSubmitCancelEventData(EventSystem.current)
+ {
+ device = (eventData as ExtendedSubmitCancelEventData)?.device
+ };
+ }
+
private static ExtendedPointerEventData ClonePointerEventData(PointerEventData eventData)
{
// InputSystemUIInputModule should only be sending ExtendedPointEventData.
diff --git a/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef b/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef
index 260e6e8b89..e10c74f1cc 100644
--- a/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef
+++ b/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef
@@ -51,6 +51,11 @@
"expression": "6000.0.11",
"define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_SCROLL_DELTA"
},
+ {
+ "name": "Unity",
+ "expression": "6000.2.0a4",
+ "define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE"
+ },
{
"name": "Unity",
"expression": "6000.0.15",
diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md
index 0450a277f8..600d71c5f3 100644
--- a/Packages/com.unity.inputsystem/CHANGELOG.md
+++ b/Packages/com.unity.inputsystem/CHANGELOG.md
@@ -13,6 +13,7 @@ however, it has to be formatted properly to pass verification tests.
### Fixed
- Fixed an issue where removing a newly created action in the Asset Editor would cause an exception. [UUM-95693](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-95693)
- Fixed arrow key navigation of Input Actions after Action rename. [ISXB-1024](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1024)
+- Fixed gamepad navigation in UI Toolkit TextField when using InputSystemUIInputModule. [UUM-77364](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-77364)
## [1.13.0] - 2025-02-05
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedAxisEventData.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedAxisEventData.cs
index cce60e74c0..d916fcb174 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedAxisEventData.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedAxisEventData.cs
@@ -3,10 +3,17 @@
namespace UnityEngine.InputSystem.UI
{
- // AxisEventData has no ToString. But that's the only thing we add so keeping
- // it internal.
- internal class ExtendedAxisEventData : AxisEventData
+ // AxisEventData has no ToString. Also added device info. Keeping
+ // it internal for now.
+ internal class ExtendedAxisEventData : AxisEventData, INavigationEventData
{
+ ///
+ /// The that generated the axis input.
+ ///
+ ///
+ ///
+ public InputDevice device { get; set; }
+
public ExtendedAxisEventData(EventSystem eventSystem)
: base(eventSystem)
{
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedSubmitCancelEventData.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedSubmitCancelEventData.cs
new file mode 100644
index 0000000000..8541d613d5
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedSubmitCancelEventData.cs
@@ -0,0 +1,20 @@
+#if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
+using UnityEngine.EventSystems;
+
+namespace UnityEngine.InputSystem.UI
+{
+ // A BaseEventData with added device info.
+ internal class ExtendedSubmitCancelEventData : BaseEventData, INavigationEventData
+ {
+ ///
+ /// The that generated the axis input.
+ ///
+ public InputDevice device { get; set; }
+
+ public ExtendedSubmitCancelEventData(EventSystem eventSystem)
+ : base(eventSystem)
+ {
+ }
+ }
+}
+#endif
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedSubmitCancelEventData.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedSubmitCancelEventData.cs.meta
new file mode 100644
index 0000000000..872b268403
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedSubmitCancelEventData.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: b7430542eee4694469709e804d36cbbb
\ No newline at end of file
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/INavigationEventData.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/INavigationEventData.cs
new file mode 100644
index 0000000000..e839fc6fd0
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/INavigationEventData.cs
@@ -0,0 +1,13 @@
+#if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
+
+namespace UnityEngine.InputSystem.UI
+{
+ internal interface INavigationEventData
+ {
+ ///
+ /// The that generated the axis input.
+ ///
+ public InputDevice device { get; }
+ }
+}
+#endif
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/INavigationEventData.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/INavigationEventData.cs.meta
new file mode 100644
index 0000000000..6e973b1c0d
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/INavigationEventData.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 4ee65784bb8ce434a947aea199821419
\ No newline at end of file
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs
index d0c03ad6f0..b4b9202b18 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs
@@ -861,7 +861,7 @@ internal void ProcessNavigation(ref NavigationModel navigationState)
if (allow)
{
- var eventData = m_NavigationState.eventData;
+ var eventData = m_NavigationState.eventData as ExtendedAxisEventData;
if (eventData == null)
{
eventData = new ExtendedAxisEventData(eventSystem);
@@ -871,6 +871,7 @@ internal void ProcessNavigation(ref NavigationModel navigationState)
eventData.moveVector = moveVector;
eventData.moveDir = moveDirection;
+ eventData.device = navigationState.device;
if (IsMoveAllowed(eventData))
{
@@ -903,7 +904,16 @@ internal void ProcessNavigation(ref NavigationModel navigationState)
var submitAction = m_SubmitAction?.action;
var cancelAction = m_CancelAction?.action;
- var data = GetBaseEventData();
+ var data = m_SubmitCancelState.eventData as ExtendedSubmitCancelEventData;
+ if (data == null)
+ {
+ data = new ExtendedSubmitCancelEventData(eventSystem);
+ m_SubmitCancelState.eventData = data;
+ }
+ data.Reset();
+
+ data.device = m_SubmitCancelState.device;
+
if (cancelAction != null && cancelAction.WasPerformedThisFrame())
ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.cancelHandler);
if (!data.used && submitAction != null && submitAction.WasPerformedThisFrame())
@@ -1393,7 +1403,7 @@ public InputActionReference move
public InputActionReference submit
{
get => m_SubmitAction;
- set => SwapAction(ref m_SubmitAction, value, m_ActionsHooked, null);
+ set => SwapAction(ref m_SubmitAction, value, m_ActionsHooked, m_OnSubmitCancelDelegate);
}
///
@@ -1433,7 +1443,7 @@ public InputActionReference submit
public InputActionReference cancel
{
get => m_CancelAction;
- set => SwapAction(ref m_CancelAction, value, m_ActionsHooked, null);
+ set => SwapAction(ref m_CancelAction, value, m_ActionsHooked, m_OnSubmitCancelDelegate);
}
///
@@ -2252,6 +2262,12 @@ private void OnMoveCallback(InputAction.CallbackContext context)
{
////REVIEW: should we poll this? or set the action to not be pass-through? (ps4 controller is spamming this action)
m_NavigationState.move = context.ReadValue();
+ m_NavigationState.device = context.control.device;
+ }
+
+ private void OnSubmitCancelCallback(InputAction.CallbackContext context)
+ {
+ m_SubmitCancelState.device = context.control.device;
}
private void OnTrackedDeviceOrientationCallback(InputAction.CallbackContext context)
@@ -2446,6 +2462,18 @@ public override Vector2 ConvertPointerEventScrollDeltaToTicks(Vector2 scrollDelt
return scrollDelta / scrollDeltaPerTick;
}
+#endif
+
+#if UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE
+ public override NavigationDeviceType GetNavigationEventDeviceType(BaseEventData eventData)
+ {
+ if (eventData is not INavigationEventData eed)
+ return NavigationDeviceType.Unknown;
+ if (eed.device is Keyboard)
+ return NavigationDeviceType.Keyboard;
+ return NavigationDeviceType.NonKeyboard;
+ }
+
#endif
private void HookActions()
@@ -2465,6 +2493,8 @@ private void HookActions()
m_OnScrollWheelDelegate = OnScrollCallback;
if (m_OnMoveDelegate == null)
m_OnMoveDelegate = OnMoveCallback;
+ if (m_OnSubmitCancelDelegate == null)
+ m_OnSubmitCancelDelegate = OnSubmitCancelCallback;
if (m_OnTrackedDeviceOrientationDelegate == null)
m_OnTrackedDeviceOrientationDelegate = OnTrackedDeviceOrientationCallback;
if (m_OnTrackedDevicePositionDelegate == null)
@@ -2486,6 +2516,8 @@ private void SetActionCallbacks(bool install)
m_ActionsHooked = install;
SetActionCallback(m_PointAction, m_OnPointDelegate, install);
SetActionCallback(m_MoveAction, m_OnMoveDelegate, install);
+ SetActionCallback(m_SubmitAction, m_OnSubmitCancelDelegate, install);
+ SetActionCallback(m_CancelAction, m_OnSubmitCancelDelegate, install);
SetActionCallback(m_LeftClickAction, m_OnLeftClickDelegate, install);
SetActionCallback(m_RightClickAction, m_OnRightClickDelegate, install);
SetActionCallback(m_MiddleClickAction, m_OnMiddleClickDelegate, install);
@@ -2601,6 +2633,7 @@ private struct InputActionReferenceState
private Action m_OnPointDelegate;
private Action m_OnMoveDelegate;
+ private Action m_OnSubmitCancelDelegate;
private Action m_OnLeftClickDelegate;
private Action m_OnRightClickDelegate;
private Action m_OnMiddleClickDelegate;
@@ -2618,6 +2651,7 @@ private struct InputActionReferenceState
// Navigation-type input.
private NavigationModel m_NavigationState;
+ private SubmitCancelModel m_SubmitCancelState;
[NonSerialized] private GameObject m_LocalMultiPlayerRoot;
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/NavigationModel.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/NavigationModel.cs
index a6e493047e..5fb6d80c79 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/NavigationModel.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/NavigationModel.cs
@@ -10,11 +10,18 @@ internal struct NavigationModel
public MoveDirection lastMoveDirection;
public float lastMoveTime;
public AxisEventData eventData;
+ public InputDevice device;
public void Reset()
{
move = Vector2.zero;
}
}
+
+ internal struct SubmitCancelModel
+ {
+ public BaseEventData eventData;
+ public InputDevice device;
+ }
}
#endif
diff --git a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef
index 6bc8cc3e6a..f60bf6a10b 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef
+++ b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef
@@ -92,6 +92,11 @@
"expression": "6000.0.11",
"define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_SCROLL_DELTA"
},
+ {
+ "name": "Unity",
+ "expression": "6000.2.0a4",
+ "define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE"
+ },
{
"name": "Unity",
"expression": "6000.0.15",
diff --git a/Packages/manifest.json b/Packages/manifest.json
index 7890b741b0..47deea3119 100644
--- a/Packages/manifest.json
+++ b/Packages/manifest.json
@@ -11,7 +11,7 @@
"com.unity.test-framework.performance": "3.0.3",
"com.unity.test-framework.utp-reporter": "1.1.0-preview",
"com.unity.textmeshpro": "3.0.6",
- "com.unity.ugui": "1.0.0",
+ "com.unity.ugui": "2.0.0",
"nuget.mono-cecil": "1.0.0",
"com.unity.modules.ai": "1.0.0",
"com.unity.modules.androidjni": "1.0.0",