Skip to content

Commit

Permalink
FIX: fixed gamepad navigation in UI Toolkit TextField (#2103)
Browse files Browse the repository at this point in the history
  • Loading branch information
benoitalain authored Feb 10, 2025
1 parent b92071e commit 14bb2d5
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 12 deletions.
39 changes: 35 additions & 4 deletions Assets/Tests/InputSystem/Plugins/UITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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())));

Expand All @@ -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())));

Expand All @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
/// The <see cref="InputDevice"/> that generated the axis input.
/// </summary>
/// <seealso cref="Keyboard"/>
/// <seealso cref="Gamepad"/>
public InputDevice device { get; set; }

public ExtendedAxisEventData(EventSystem eventSystem)
: base(eventSystem)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// The <see cref="InputDevice"/> that generated the axis input.
/// </summary>
public InputDevice device { get; set; }

public ExtendedSubmitCancelEventData(EventSystem eventSystem)
: base(eventSystem)
{
}
}
}
#endif

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#if PACKAGE_DOCS_GENERATION || UNITY_INPUT_SYSTEM_ENABLE_UI

namespace UnityEngine.InputSystem.UI
{
internal interface INavigationEventData
{
/// <summary>
/// The <see cref="InputDevice"/> that generated the axis input.
/// </summary>
public InputDevice device { get; }
}
}
#endif

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -871,6 +871,7 @@ internal void ProcessNavigation(ref NavigationModel navigationState)

eventData.moveVector = moveVector;
eventData.moveDir = moveDirection;
eventData.device = navigationState.device;

if (IsMoveAllowed(eventData))
{
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -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<Vector2>();
m_NavigationState.device = context.control.device;
}

private void OnSubmitCancelCallback(InputAction.CallbackContext context)
{
m_SubmitCancelState.device = context.control.device;
}

private void OnTrackedDeviceOrientationCallback(InputAction.CallbackContext context)
Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -2601,6 +2633,7 @@ private struct InputActionReferenceState

private Action<InputAction.CallbackContext> m_OnPointDelegate;
private Action<InputAction.CallbackContext> m_OnMoveDelegate;
private Action<InputAction.CallbackContext> m_OnSubmitCancelDelegate;
private Action<InputAction.CallbackContext> m_OnLeftClickDelegate;
private Action<InputAction.CallbackContext> m_OnRightClickDelegate;
private Action<InputAction.CallbackContext> m_OnMiddleClickDelegate;
Expand All @@ -2618,6 +2651,7 @@ private struct InputActionReferenceState

// Navigation-type input.
private NavigationModel m_NavigationState;
private SubmitCancelModel m_SubmitCancelState;

[NonSerialized] private GameObject m_LocalMultiPlayerRoot;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion Packages/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 14bb2d5

Please sign in to comment.