Skip to content

Commit 14bb2d5

Browse files
authored
FIX: fixed gamepad navigation in UI Toolkit TextField (#2103)
1 parent b92071e commit 14bb2d5

12 files changed

+139
-12
lines changed

Diff for: Assets/Tests/InputSystem/Plugins/UITests.cs

+35-4
Original file line numberDiff line numberDiff line change
@@ -2911,10 +2911,16 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29112911
Assert.That(scene.leftChildReceiver.events,
29122912
EventSequence(
29132913
OneEvent("type", EventType.Move),
2914+
OneEvent("device", gamepad),
29142915
OneEvent("moveDir", MoveDirection.Right),
29152916
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
29162917
Assert.That(scene.rightChildReceiver.events, Is.Empty);
29172918

2919+
#if UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE
2920+
Assert.That(scene.uiModule.GetNavigationEventDeviceType(scene.leftChildReceiver.events[0].data),
2921+
Is.EqualTo(NavigationDeviceType.NonKeyboard));
2922+
#endif
2923+
29182924
scene.leftChildReceiver.events.Clear();
29192925

29202926
// Move left.
@@ -2924,6 +2930,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29242930
Assert.That(scene.leftChildReceiver.events,
29252931
EventSequence(
29262932
OneEvent("type", EventType.Move),
2933+
OneEvent("device", gamepad),
29272934
OneEvent("moveDir", MoveDirection.Left),
29282935
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
29292936
Assert.That(scene.rightChildReceiver.events, Is.Empty);
@@ -2937,6 +2944,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29372944
Assert.That(scene.leftChildReceiver.events,
29382945
EventSequence(
29392946
OneEvent("type", EventType.Move),
2947+
OneEvent("device", gamepad),
29402948
OneEvent("moveDir", MoveDirection.Up),
29412949
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
29422950
Assert.That(scene.rightChildReceiver.events, Is.Empty);
@@ -2950,6 +2958,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29502958
Assert.That(scene.leftChildReceiver.events,
29512959
EventSequence(
29522960
OneEvent("type", EventType.Move),
2961+
OneEvent("device", gamepad),
29532962
OneEvent("moveDir", MoveDirection.Down),
29542963
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
29552964
Assert.That(scene.rightChildReceiver.events, Is.Empty);
@@ -2964,6 +2973,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29642973
Assert.That(scene.leftChildReceiver.events,
29652974
EventSequence(
29662975
OneEvent("type", EventType.Move),
2976+
OneEvent("device", gamepad),
29672977
OneEvent("moveDir", MoveDirection.Down),
29682978
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
29692979

@@ -2977,6 +2987,7 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29772987
Assert.That(scene.leftChildReceiver.events,
29782988
EventSequence(
29792989
OneEvent("type", EventType.Move),
2990+
OneEvent("device", gamepad),
29802991
OneEvent("moveDir", MoveDirection.Down),
29812992
OneEvent("moveVector", gamepad.leftStick.ReadValue())));
29822993

@@ -2986,7 +2997,12 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29862997
PressAndRelease(gamepad.buttonSouth);
29872998
yield return null;
29882999

2989-
Assert.That(scene.leftChildReceiver.events, EventSequence(OneEvent("type", EventType.Submit)));
3000+
Assert.That(scene.leftChildReceiver.events,
3001+
EventSequence(
3002+
OneEvent("type", EventType.Submit),
3003+
OneEvent("device", gamepad)
3004+
)
3005+
);
29903006
Assert.That(scene.rightChildReceiver.events, Is.Empty);
29913007

29923008
scene.leftChildReceiver.events.Clear();
@@ -2995,7 +3011,12 @@ public IEnumerator UI_CanDriveUIFromGamepad()
29953011
PressAndRelease(gamepad.buttonEast);
29963012
yield return null;
29973013

2998-
Assert.That(scene.leftChildReceiver.events, EventSequence(OneEvent("type", EventType.Cancel)));
3014+
Assert.That(scene.leftChildReceiver.events,
3015+
EventSequence(
3016+
OneEvent("type", EventType.Cancel),
3017+
OneEvent("device", gamepad)
3018+
)
3019+
);
29993020
Assert.That(scene.rightChildReceiver.events, Is.Empty);
30003021

30013022
scene.leftChildReceiver.events.Clear();
@@ -4463,6 +4484,7 @@ public struct Event
44634484
public BaseEventData data { get; }
44644485
public AxisEventData axisData => (AxisEventData)data;
44654486
public ExtendedPointerEventData pointerData => (ExtendedPointerEventData)data;
4487+
public INavigationEventData navigationData => (INavigationEventData)data;
44664488

44674489
public Event(EventType type, BaseEventData data)
44684490
{
@@ -4521,12 +4543,12 @@ public void OnMove(AxisEventData eventData)
45214543

45224544
public void OnSubmit(BaseEventData eventData)
45234545
{
4524-
events.Add(new Event(EventType.Submit, null));
4546+
events.Add(new Event(EventType.Submit, CloneSubmitCancelEventData(eventData)));
45254547
}
45264548

45274549
public void OnCancel(BaseEventData eventData)
45284550
{
4529-
events.Add(new Event(EventType.Cancel, null));
4551+
events.Add(new Event(EventType.Cancel, CloneSubmitCancelEventData(eventData)));
45304552
}
45314553

45324554
public void OnSelect(BaseEventData eventData)
@@ -4579,11 +4601,20 @@ private static AxisEventData CloneAxisEventData(AxisEventData eventData)
45794601
{
45804602
return new ExtendedAxisEventData(EventSystem.current)
45814603
{
4604+
device = (eventData as ExtendedAxisEventData)?.device,
45824605
moveVector = eventData.moveVector,
45834606
moveDir = eventData.moveDir
45844607
};
45854608
}
45864609

4610+
private static ExtendedSubmitCancelEventData CloneSubmitCancelEventData(BaseEventData eventData)
4611+
{
4612+
return new ExtendedSubmitCancelEventData(EventSystem.current)
4613+
{
4614+
device = (eventData as ExtendedSubmitCancelEventData)?.device
4615+
};
4616+
}
4617+
45874618
private static ExtendedPointerEventData ClonePointerEventData(PointerEventData eventData)
45884619
{
45894620
// InputSystemUIInputModule should only be sending ExtendedPointEventData.

Diff for: Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@
5151
"expression": "6000.0.11",
5252
"define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_SCROLL_DELTA"
5353
},
54+
{
55+
"name": "Unity",
56+
"expression": "6000.2.0a4",
57+
"define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE"
58+
},
5459
{
5560
"name": "Unity",
5661
"expression": "6000.0.15",

Diff for: Packages/com.unity.inputsystem/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ however, it has to be formatted properly to pass verification tests.
1313
### Fixed
1414
- 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)
1515
- Fixed arrow key navigation of Input Actions after Action rename. [ISXB-1024](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1024)
16+
- Fixed gamepad navigation in UI Toolkit TextField when using InputSystemUIInputModule. [UUM-77364](https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-77364)
1617

1718
## [1.13.0] - 2025-02-05
1819

Diff for: Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedAxisEventData.cs

+10-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@
33

44
namespace UnityEngine.InputSystem.UI
55
{
6-
// AxisEventData has no ToString. But that's the only thing we add so keeping
7-
// it internal.
8-
internal class ExtendedAxisEventData : AxisEventData
6+
// AxisEventData has no ToString. Also added device info. Keeping
7+
// it internal for now.
8+
internal class ExtendedAxisEventData : AxisEventData, INavigationEventData
99
{
10+
/// <summary>
11+
/// The <see cref="InputDevice"/> that generated the axis input.
12+
/// </summary>
13+
/// <seealso cref="Keyboard"/>
14+
/// <seealso cref="Gamepad"/>
15+
public InputDevice device { get; set; }
16+
1017
public ExtendedAxisEventData(EventSystem eventSystem)
1118
: base(eventSystem)
1219
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
2+
using UnityEngine.EventSystems;
3+
4+
namespace UnityEngine.InputSystem.UI
5+
{
6+
// A BaseEventData with added device info.
7+
internal class ExtendedSubmitCancelEventData : BaseEventData, INavigationEventData
8+
{
9+
/// <summary>
10+
/// The <see cref="InputDevice"/> that generated the axis input.
11+
/// </summary>
12+
public InputDevice device { get; set; }
13+
14+
public ExtendedSubmitCancelEventData(EventSystem eventSystem)
15+
: base(eventSystem)
16+
{
17+
}
18+
}
19+
}
20+
#endif

Diff for: Packages/com.unity.inputsystem/InputSystem/Plugins/UI/ExtendedSubmitCancelEventData.cs.meta

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI
2+
3+
namespace UnityEngine.InputSystem.UI
4+
{
5+
internal interface INavigationEventData
6+
{
7+
/// <summary>
8+
/// The <see cref="InputDevice"/> that generated the axis input.
9+
/// </summary>
10+
public InputDevice device { get; }
11+
}
12+
}
13+
#endif

Diff for: Packages/com.unity.inputsystem/InputSystem/Plugins/UI/INavigationEventData.cs.meta

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs

+38-4
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ internal void ProcessNavigation(ref NavigationModel navigationState)
861861

862862
if (allow)
863863
{
864-
var eventData = m_NavigationState.eventData;
864+
var eventData = m_NavigationState.eventData as ExtendedAxisEventData;
865865
if (eventData == null)
866866
{
867867
eventData = new ExtendedAxisEventData(eventSystem);
@@ -871,6 +871,7 @@ internal void ProcessNavigation(ref NavigationModel navigationState)
871871

872872
eventData.moveVector = moveVector;
873873
eventData.moveDir = moveDirection;
874+
eventData.device = navigationState.device;
874875

875876
if (IsMoveAllowed(eventData))
876877
{
@@ -903,7 +904,16 @@ internal void ProcessNavigation(ref NavigationModel navigationState)
903904
var submitAction = m_SubmitAction?.action;
904905
var cancelAction = m_CancelAction?.action;
905906

906-
var data = GetBaseEventData();
907+
var data = m_SubmitCancelState.eventData as ExtendedSubmitCancelEventData;
908+
if (data == null)
909+
{
910+
data = new ExtendedSubmitCancelEventData(eventSystem);
911+
m_SubmitCancelState.eventData = data;
912+
}
913+
data.Reset();
914+
915+
data.device = m_SubmitCancelState.device;
916+
907917
if (cancelAction != null && cancelAction.WasPerformedThisFrame())
908918
ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.cancelHandler);
909919
if (!data.used && submitAction != null && submitAction.WasPerformedThisFrame())
@@ -1393,7 +1403,7 @@ public InputActionReference move
13931403
public InputActionReference submit
13941404
{
13951405
get => m_SubmitAction;
1396-
set => SwapAction(ref m_SubmitAction, value, m_ActionsHooked, null);
1406+
set => SwapAction(ref m_SubmitAction, value, m_ActionsHooked, m_OnSubmitCancelDelegate);
13971407
}
13981408

13991409
/// <summary>
@@ -1433,7 +1443,7 @@ public InputActionReference submit
14331443
public InputActionReference cancel
14341444
{
14351445
get => m_CancelAction;
1436-
set => SwapAction(ref m_CancelAction, value, m_ActionsHooked, null);
1446+
set => SwapAction(ref m_CancelAction, value, m_ActionsHooked, m_OnSubmitCancelDelegate);
14371447
}
14381448

14391449
/// <summary>
@@ -2252,6 +2262,12 @@ private void OnMoveCallback(InputAction.CallbackContext context)
22522262
{
22532263
////REVIEW: should we poll this? or set the action to not be pass-through? (ps4 controller is spamming this action)
22542264
m_NavigationState.move = context.ReadValue<Vector2>();
2265+
m_NavigationState.device = context.control.device;
2266+
}
2267+
2268+
private void OnSubmitCancelCallback(InputAction.CallbackContext context)
2269+
{
2270+
m_SubmitCancelState.device = context.control.device;
22552271
}
22562272

22572273
private void OnTrackedDeviceOrientationCallback(InputAction.CallbackContext context)
@@ -2446,6 +2462,18 @@ public override Vector2 ConvertPointerEventScrollDeltaToTicks(Vector2 scrollDelt
24462462
return scrollDelta / scrollDeltaPerTick;
24472463
}
24482464

2465+
#endif
2466+
2467+
#if UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE
2468+
public override NavigationDeviceType GetNavigationEventDeviceType(BaseEventData eventData)
2469+
{
2470+
if (eventData is not INavigationEventData eed)
2471+
return NavigationDeviceType.Unknown;
2472+
if (eed.device is Keyboard)
2473+
return NavigationDeviceType.Keyboard;
2474+
return NavigationDeviceType.NonKeyboard;
2475+
}
2476+
24492477
#endif
24502478

24512479
private void HookActions()
@@ -2465,6 +2493,8 @@ private void HookActions()
24652493
m_OnScrollWheelDelegate = OnScrollCallback;
24662494
if (m_OnMoveDelegate == null)
24672495
m_OnMoveDelegate = OnMoveCallback;
2496+
if (m_OnSubmitCancelDelegate == null)
2497+
m_OnSubmitCancelDelegate = OnSubmitCancelCallback;
24682498
if (m_OnTrackedDeviceOrientationDelegate == null)
24692499
m_OnTrackedDeviceOrientationDelegate = OnTrackedDeviceOrientationCallback;
24702500
if (m_OnTrackedDevicePositionDelegate == null)
@@ -2486,6 +2516,8 @@ private void SetActionCallbacks(bool install)
24862516
m_ActionsHooked = install;
24872517
SetActionCallback(m_PointAction, m_OnPointDelegate, install);
24882518
SetActionCallback(m_MoveAction, m_OnMoveDelegate, install);
2519+
SetActionCallback(m_SubmitAction, m_OnSubmitCancelDelegate, install);
2520+
SetActionCallback(m_CancelAction, m_OnSubmitCancelDelegate, install);
24892521
SetActionCallback(m_LeftClickAction, m_OnLeftClickDelegate, install);
24902522
SetActionCallback(m_RightClickAction, m_OnRightClickDelegate, install);
24912523
SetActionCallback(m_MiddleClickAction, m_OnMiddleClickDelegate, install);
@@ -2601,6 +2633,7 @@ private struct InputActionReferenceState
26012633

26022634
private Action<InputAction.CallbackContext> m_OnPointDelegate;
26032635
private Action<InputAction.CallbackContext> m_OnMoveDelegate;
2636+
private Action<InputAction.CallbackContext> m_OnSubmitCancelDelegate;
26042637
private Action<InputAction.CallbackContext> m_OnLeftClickDelegate;
26052638
private Action<InputAction.CallbackContext> m_OnRightClickDelegate;
26062639
private Action<InputAction.CallbackContext> m_OnMiddleClickDelegate;
@@ -2618,6 +2651,7 @@ private struct InputActionReferenceState
26182651

26192652
// Navigation-type input.
26202653
private NavigationModel m_NavigationState;
2654+
private SubmitCancelModel m_SubmitCancelState;
26212655

26222656
[NonSerialized] private GameObject m_LocalMultiPlayerRoot;
26232657

Diff for: Packages/com.unity.inputsystem/InputSystem/Plugins/UI/NavigationModel.cs

+7
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@ internal struct NavigationModel
1010
public MoveDirection lastMoveDirection;
1111
public float lastMoveTime;
1212
public AxisEventData eventData;
13+
public InputDevice device;
1314

1415
public void Reset()
1516
{
1617
move = Vector2.zero;
1718
}
1819
}
20+
21+
internal struct SubmitCancelModel
22+
{
23+
public BaseEventData eventData;
24+
public InputDevice device;
25+
}
1926
}
2027
#endif

Diff for: Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@
9292
"expression": "6000.0.11",
9393
"define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_SCROLL_DELTA"
9494
},
95+
{
96+
"name": "Unity",
97+
"expression": "6000.2.0a4",
98+
"define": "UNITY_INPUT_SYSTEM_INPUT_MODULE_NAVIGATION_DEVICE_TYPE"
99+
},
95100
{
96101
"name": "Unity",
97102
"expression": "6000.0.15",

Diff for: Packages/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"com.unity.test-framework.performance": "3.0.3",
1212
"com.unity.test-framework.utp-reporter": "1.1.0-preview",
1313
"com.unity.textmeshpro": "3.0.6",
14-
"com.unity.ugui": "1.0.0",
14+
"com.unity.ugui": "2.0.0",
1515
"nuget.mono-cecil": "1.0.0",
1616
"com.unity.modules.ai": "1.0.0",
1717
"com.unity.modules.androidjni": "1.0.0",

0 commit comments

Comments
 (0)