Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3806968
Add RenameAction test
tspiller Nov 1, 2024
d927379
Update CHANGELOG.md
tspiller Nov 11, 2024
40b6dce
RefreshItems using same logic as ActionMapsView
tspiller Dec 9, 2024
2f638eb
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Dec 10, 2024
249949b
Wait for frame to be processed after another state change
tspiller Dec 10, 2024
7b0ee7c
Make Action renaming more closely follow the same behaviour as ActionMap
tspiller Dec 10, 2024
258e5eb
Update InputActionsEditorTests.cs
tspiller Dec 10, 2024
ef19674
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Dec 11, 2024
9d3e504
Update InputActionsEditorTests.cs
tspiller Dec 11, 2024
058c932
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Jan 7, 2025
f010303
Update ActionItem ContextMenu to behave like ActionMaps as well
tspiller Jan 15, 2025
998aa88
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Jan 15, 2025
1daf69d
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Jan 17, 2025
d745383
Update CHANGELOG.md
tspiller Jan 17, 2025
c310708
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
Pauliusd01 Jan 20, 2025
617dc44
Populate ContextMenu for BindingItem and CompositeItem using same eve…
tspiller Jan 20, 2025
b02473e
Re-register Rename InputField
tspiller Jan 21, 2025
12d933b
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Jan 21, 2025
6c93741
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Jan 28, 2025
1a3c16e
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Feb 3, 2025
661896d
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Feb 6, 2025
7c46276
Update CHANGELOG.md
tspiller Feb 6, 2025
ca52508
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Feb 6, 2025
c2e230e
Merge branch 'develop' into isxb-1024-action-navigation-after-rename
tspiller Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion Assets/Tests/InputSystem.Editor/InputActionsEditorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ public override void OneTimeSetUp()
{
base.OneTimeSetUp();
m_Asset = AssetDatabaseUtils.CreateAsset<InputActionAsset>();
m_Asset.AddActionMap("First Name");
var actionMap = m_Asset.AddActionMap("First Name");
m_Asset.AddActionMap("Second Name");
m_Asset.AddActionMap("Third Name");

actionMap.AddAction("Action One");
actionMap.AddAction("Action Two");
}

public override void OneTimeTearDown()
Expand Down Expand Up @@ -55,6 +58,19 @@ IEnumerator WaitForActionMapRename(int index, bool isActive, double timeoutSecs
}, $"WaitForActionMapRename {index} {isActive}", timeoutSecs);
}

IEnumerator WaitForActionRename(int index, bool isActive, double timeoutSecs = 5.0)
{
return WaitUntil(() =>
{
var actionItems = m_Window.rootVisualElement.Q("actions-container").Query<InputActionsTreeViewItem>().ToList();
if (actionItems.Count > index && actionItems[index].IsFocused == isActive)
{
return true;
}
return false;
}, $"WaitForActionRename {index} {isActive}", timeoutSecs);
}

#endregion

[Test]
Expand Down Expand Up @@ -177,5 +193,53 @@ public IEnumerator CanDeleteActionMap()
Assert.That(m_Window.currentAssetInEditor.actionMaps[0].name, Is.EqualTo("First Name"));
Assert.That(m_Window.currentAssetInEditor.actionMaps[1].name, Is.EqualTo("Third Name"));
}

[UnityTest]
[Ignore("Instability, see ISXB-1284")]
public IEnumerator CanRenameAction()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome you've added a proper editor test for this. Thank you!

{
var actionContainer = m_Window.rootVisualElement.Q("actions-container");
var actionItem = actionContainer.Query<InputActionsTreeViewItem>().ToList();
Assume.That(actionItem[1].Q<Label>("name").text, Is.EqualTo("Action Two"));

m_Window.rootVisualElement.Q<TreeView>("actions-tree-view").Focus();
m_Window.rootVisualElement.Q<TreeView>("actions-tree-view").selectedIndex = 1;

// Selection change triggers a state change, wait for the scheduler to process the frame
yield return WaitForSchedulerLoop();
yield return WaitForNotDirty();
yield return WaitForFocus(m_Window.rootVisualElement.Q<TreeView>("actions-tree-view"));

// Re-fetch the actions since the UI may have refreshed.
actionItem = actionContainer.Query<InputActionsTreeViewItem>().ToList();

// Click twice to start the rename
SimulateClickOn(actionItem[1]);
// If the item is already focused, don't click again
if (!actionItem[1].IsFocused)
{
SimulateClickOn(actionItem[1]);
}

yield return WaitForActionRename(1, isActive: true);

// Rename the action
SimulateTypingText("New Name");

// Wait for rename to end and focus to return from text field
yield return WaitForSchedulerLoop();
yield return WaitForFocus(m_Window.rootVisualElement.Q<TreeView>("actions-tree-view"));

// Check on the UI side
actionContainer = m_Window.rootVisualElement.Q("actions-container");
Assume.That(actionContainer, Is.Not.Null);
actionItem = actionContainer.Query<InputActionsTreeViewItem>().ToList();
Assert.That(actionItem, Is.Not.Null);
Assert.That(actionItem.Count, Is.EqualTo(2));
Assert.That(actionItem[1].Q<Label>("name").text, Is.EqualTo("New Name"));

// Check on the asset side
Assert.That(m_Window.currentAssetInEditor.actionMaps[0].actions[1].name, Is.EqualTo("New Name"));
}
}
#endif
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ however, it has to be formatted properly to pass verification tests.
- Fixed an issue where compiling Addressables with Input System package present would result in failed compilation due to `IInputAnalytic.TryGatherData` not being defined [ISXB-1203](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1203).
- Pinned Touch Samples sample package dependencies to avoid errors with Cinemachine 3.x and Probuilder 6.x. [ISXB-1245](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1245)
- Fixed an issue where dropdown menu for Path in Input Actions Editor could not be selected from any button position. [ISXB-1309](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1309)
- Fixed arrow key navigation of Input Actions after Action rename. [ISXB-1024](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1024)

## [1.12.0] - 2025-01-15

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ public ActionsTreeView(VisualElement root, StateContainer stateContainer)

if (item.isAction)
{
// Items in the TreeView which were previously Bindings had input explicitly unregistered.
// Since the input field is normally registered on creation, when using RefreshItem rather than Rebuild
// it must be re-registered here.
treeViewItem.RegisterInputField();

Action action = ContextMenu.GetContextMenuForActionAddItem(this, item.controlLayout, i);
addBindingButton.clicked += action;
addBindingButton.userData = action; // Store to use in unbindItem
Expand All @@ -76,6 +81,11 @@ public ActionsTreeView(VisualElement root, StateContainer stateContainer)
treeViewItem.UnregisterInputField();
else
{
// Items in the TreeView which were previously Bindings had input explicitly unregistered.
// Since the input field is normally registered on creation, when using RefreshItem rather than Rebuild
// it must be re-registered here.
treeViewItem.RegisterInputField();

treeViewItem.EditTextFinishedCallback = newName =>
{
ChangeActionOrCompositName(item, newName);
Expand Down Expand Up @@ -205,7 +215,13 @@ public override void RedrawUI(ViewState viewState)
{
m_ActionsTreeView.Clear();
m_ActionsTreeView.SetRootItems(viewState.treeViewData);
// UI toolkit doesn't behave the same on 6000.0 way when refreshing items
// On previous versions, we need to call Rebuild() to refresh the items since refreshItems() is less predicatable
#if UNITY_6000_0_OR_NEWER
m_ActionsTreeView.RefreshItems();
#else
m_ActionsTreeView.Rebuild();
#endif
if (viewState.newElementID != -1)
{
m_ActionsTreeView.SetSelectionById(viewState.newElementID);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,14 @@ public static void GetContextMenuForActionsEmptySpace(ActionsTreeView actionsTre

public static void GetContextMenuForActionItem(ActionsTreeView treeView, InputActionsTreeViewItem treeViewItem, string controlLayout, int index)
{
_ = new ContextualMenuManipulator(menuEvent =>
treeViewItem.OnContextualMenuPopulateEvent = (menuEvent =>
{
menuEvent.menu.AppendAction(add_Binding_String, _ => treeView.AddBinding(index));
AppendCompositeMenuItems(treeView, controlLayout, index, (name, action) => menuEvent.menu.AppendAction(name, _ => action.Invoke()));
menuEvent.menu.AppendSeparator();
AppendRenameAction(menuEvent, treeView, index);
AppendDuplicateDeleteCutAndCopyActionsSection(menuEvent, treeView, index);
}) { target = treeViewItem };
});
}

public static Action GetContextMenuForActionAddItem(ActionsTreeView treeView, string controlLayout, int index)
Expand Down Expand Up @@ -202,19 +202,19 @@ private static void AppendCompositeMenuItems(ActionsTreeView treeView, string ex

public static void GetContextMenuForCompositeItem(ActionsTreeView treeView, InputActionsTreeViewItem treeViewItem, int index)
{
_ = new ContextualMenuManipulator(menuEvent =>
treeViewItem.OnContextualMenuPopulateEvent = (menuEvent =>
{
AppendRenameAction(menuEvent, treeView, index);
AppendDuplicateDeleteCutAndCopyActionsSection(menuEvent, treeView, index);
}) { target = treeViewItem };
});
}

public static void GetContextMenuForBindingItem(ActionsTreeView treeView, InputActionsTreeViewItem treeViewItem, int index)
{
_ = new ContextualMenuManipulator(menuEvent =>
treeViewItem.OnContextualMenuPopulateEvent = (menuEvent =>
{
AppendDuplicateDeleteCutAndCopyActionsSection(menuEvent, treeView, index);
}) { target = treeViewItem };
});
}

private static void AppendRenameAction(ContextualMenuPopulateEvent menuEvent, ActionsTreeView treeView, int index)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// UITK TreeView is not supported in earlier versions
// Therefore the UITK version of the InputActionAsset Editor is not available on earlier Editor versions either.
#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
using System;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine.UIElements;
Expand All @@ -16,6 +17,10 @@ internal class InputActionsTreeViewItem : VisualElement

private const string kRenameTextField = "rename-text-field";
public event EventCallback<string> EditTextFinished;
public Action<ContextualMenuPopulateEvent> OnContextualMenuPopulateEvent;

// for testing purposes to know if the item is focused to accept input
internal bool IsFocused { get; private set; } = false;

private bool m_IsEditing;
private static InputActionsTreeViewItem s_EditingItem = null;
Expand All @@ -33,17 +38,32 @@ public InputActionsTreeViewItem()
focusable = true;
delegatesFocus = false;

renameTextfield.selectAllOnFocus = true;
renameTextfield.selectAllOnMouseUp = false;


RegisterCallback<MouseDownEvent>(OnMouseDownEventForRename);
renameTextfield.RegisterCallback<FocusOutEvent>(e => OnEditTextFinished());
RegisterInputField();
_ = new ContextualMenuManipulator(menuBuilder =>
{
OnContextualMenuPopulateEvent?.Invoke(menuBuilder);
})
{ target = this };
}

public Label label => this.Q<Label>();
private TextField renameTextfield => this.Q<TextField>(kRenameTextField);

public void RegisterInputField()
{
renameTextfield.SetEnabled(true);
renameTextfield.selectAllOnFocus = true;
RegisterCallback<MouseDownEvent>(OnMouseDownEventForRename);
renameTextfield.RegisterCallback<FocusInEvent>(e => IsFocused = true);
renameTextfield.RegisterCallback<FocusOutEvent>(e =>
{
OnEditTextFinished();
IsFocused = false;
});
}

public void UnregisterInputField()
{
renameTextfield.SetEnabled(false);
Expand All @@ -52,28 +72,38 @@ public void UnregisterInputField()
renameTextfield.UnregisterCallback<FocusOutEvent>(e => OnEditTextFinished());
}

private float lastSingleClick;
private double lastSingleClick;
private static InputActionsTreeViewItem selected;

private void OnMouseDownEventForRename(MouseDownEvent e)
{
if (e.clickCount != 1 || e.button != (int)MouseButton.LeftMouse || e.target == null)
return;

if (selected == this && Time.time - lastSingleClick < 3f)
var now = EditorApplication.timeSinceStartup;
if (selected == this && now - lastSingleClick < 3)
{
FocusOnRenameTextField();
e.StopImmediatePropagation();
lastSingleClick = 0;
return;
}
lastSingleClick = Time.time;
lastSingleClick = now;
selected = this;
}

public void Reset()
{
if (m_IsEditing)
{
lastSingleClick = 0;
delegatesFocus = false;

renameTextfield.AddToClassList(InputActionsEditorConstants.HiddenStyleClassName);
label.RemoveFromClassList(InputActionsEditorConstants.HiddenStyleClassName);
s_EditingItem = null;
m_IsEditing = false;
}
EditTextFinished = null;
m_IsEditing = false;
}

public void FocusOnRenameTextField()
Expand All @@ -88,7 +118,7 @@ public void FocusOnRenameTextField()

//a bit hacky - e.StopImmediatePropagation() for events does not work like expected on ListViewItems or TreeViewItems because
//the listView/treeView reclaims the focus - this is a workaround with less overhead than rewriting the events
DelayCall();
schedule.Execute(() => renameTextfield.Q<TextField>().Focus()).StartingIn(120);
renameTextfield.SelectAll();

s_EditingItem = this;
Expand All @@ -100,12 +130,6 @@ public static void CancelRename()
s_EditingItem?.OnEditTextFinished();
}

async void DelayCall()
{
await Task.Delay(120);
renameTextfield.Q<TextField>().Focus();
}

private void OnEditTextFinished()
{
if (!m_IsEditing)
Expand All @@ -124,6 +148,7 @@ private void OnEditTextFinished()
renameTextfield.schedule.Execute(() => renameTextfield.SetValueWithoutNotify(text));
return;
}

EditTextFinished?.Invoke(text);
}
}
Expand Down