Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
326a442
DROP Enable UITK Asset Editor
jfreire-unity Aug 14, 2023
c8f3ea5
WIP Add Drag And Drop UI functionality
jfreire-unity Aug 14, 2023
5d4ae4c
SQUASH
jfreire-unity Aug 16, 2023
7c18777
Add drag and drop respective manipulators to deal with UI events
jfreire-unity Aug 17, 2023
083b4d3
WIP Add data manipulation logic based on UI drag and drop actions
jfreire-unity Aug 17, 2023
6af4d0b
Merge remote-tracking branch 'origin/develop' into isx-1141-uitk-drag…
ritamerkl Jan 23, 2024
68565d2
fixed visualizations
ritamerkl Jan 26, 2024
5605382
fixed reordering and drag between lists
ritamerkl Jan 29, 2024
b843619
fixed focusing MapListItems during drag
ritamerkl Jan 29, 2024
5a4b176
clean up branch
ritamerkl Jan 30, 2024
d76cb23
clean up drag/drop
ritamerkl Jan 30, 2024
420bf9e
implemented Drag & Drop between action tree view and action map list …
ritamerkl Jan 30, 2024
30282e5
Merge remote-tracking branch 'origin/develop' into isx-1141-uitk-drag…
ritamerkl Jan 30, 2024
e9aaa22
cleared focus during drag/drop between lists
ritamerkl Jan 30, 2024
d5500b7
fixed freeze after drag
ritamerkl Jan 30, 2024
9f1bf83
drag and drop of action maps to reorder ActionMap list
ritamerkl Jan 31, 2024
949077e
WIP drag & drop of actions, bindings & composites
ritamerkl Feb 1, 2024
8a18181
Merge remote-tracking branch 'origin/develop' into isx-1141-uitk-drag…
ritamerkl Feb 2, 2024
e5f7d54
fix after merge
ritamerkl Feb 2, 2024
f9d2bea
added moving of bindings and move composite (WIP)
ritamerkl Feb 2, 2024
0a6a44c
implemented move part of composites and fixed move binding to empty a…
ritamerkl Feb 2, 2024
4e70360
fixed moving bindings
ritamerkl Feb 2, 2024
23fef72
added discard drag on invalid
ritamerkl Feb 2, 2024
7fd0078
fixed selection after drag & dragging bindings into actionMapView
ritamerkl Feb 5, 2024
aa60f59
refactor & fixed not allow drag of composites to action maps
ritamerkl Feb 5, 2024
7f1dd90
fixed moving of composites
ritamerkl Feb 5, 2024
0e0f1e7
fixed discard of drag between items
ritamerkl Feb 5, 2024
1dbad3a
change action name for composites
ritamerkl Feb 5, 2024
7c3660b
fixed bindings are moved down one index to far
ritamerkl Feb 5, 2024
1ea2ba9
added action item
ritamerkl Feb 5, 2024
3d02a5d
added changelog
ritamerkl Feb 5, 2024
cfbd28b
fix formatting & adjusted changelog
ritamerkl Feb 6, 2024
eeeb302
Merge remote-tracking branch 'origin/develop' into isx-1141-uitk-drag…
ritamerkl Feb 7, 2024
0582d99
refactor commands & fixed analyzer
ritamerkl Feb 7, 2024
32a3a64
added comments and fixed accessibility
ritamerkl Feb 7, 2024
8031068
added comments & named parameters to ActionsTreeView
ritamerkl Feb 7, 2024
30b7ed7
fixed formatting
ritamerkl Feb 7, 2024
de92b06
removed plus sign for dragging action to action map
ritamerkl Feb 7, 2024
2175b70
added safety check - fixed nullpointer for adding actions in empty ac…
ritamerkl Feb 7, 2024
87faaef
fix picking of elements for dropping on ActionMapView
ritamerkl Feb 8, 2024
7e507fb
fixed naming and enhanced break conditions for TreeViewDrag
ritamerkl Feb 8, 2024
b0f2c7d
added safety assert & debug pasting bindings
ritamerkl Feb 14, 2024
b060518
changed dropManipulator to be internal
ritamerkl Feb 14, 2024
a1816dc
Merge branch 'develop' into isx-1141-uitk-drag-and-drop
ritamerkl Feb 14, 2024
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
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ however, it has to be formatted properly to pass verification tests.
- [`InputAction.WasCompletedThisFrame`](xref:UnityEngine.InputSystem.InputAction.WasCompletedThisFrame) returns `true` on the frame that the action stopped being in the performed phase. This allows for similar functionality to [`WasPressedThisFrame`](xref:UnityEngine.InputSystem.InputAction.WasPressedThisFrame)/[`WasReleasedThisFrame`](xref:UnityEngine.InputSystem.InputAction.WasReleasedThisFrame) when paired with [`WasPerformedThisFrame`](xref:UnityEngine.InputSystem.InputAction.WasPerformedThisFrame) except it is directly based on the interactions driving the action. For example, you can use it to distinguish between the button being released or whether it was released after being held for long enough to perform when using the Hold interaction.
- Added Copy, Paste and Cut support for Action Maps, Actions and Bindings via context menu and key command shortcuts.
- Added Dual Sense Edge controller to be mapped to the same layout as the Dual Sense controller
- Added drag and drop support in the Input Action Asset Editor for Action Maps, Actions and Bindings.

### Fixed
- Fixed syntax of code examples in API documentation for [`AxisComposite`](xref:UnityEngine.InputSystem.Composites.AxisComposite).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,24 @@ public static void DeleteActionMap(SerializedObject asset, Guid id)
mapArrayProperty.DeleteArrayElementAtIndex(mapIndex);
}

public static void MoveActionMap(SerializedObject asset, int fromIndex, int toIndex)
{
var mapArrayProperty = asset.FindProperty("m_ActionMaps");
mapArrayProperty.MoveArrayElement(fromIndex, toIndex);
}

public static void MoveAction(SerializedProperty actionMap, int fromIndex, int toIndex)
{
var actionArrayProperty = actionMap.FindPropertyRelative(nameof(InputActionMap.m_Actions));
actionArrayProperty.MoveArrayElement(fromIndex, toIndex);
}

public static void MoveBinding(SerializedProperty actionMap, int fromIndex, int toIndex)
{
var arrayProperty = actionMap.FindPropertyRelative(nameof(InputActionMap.m_Bindings));
arrayProperty.MoveArrayElement(fromIndex, toIndex);
}

// Append a new action to the end of the set.
public static SerializedProperty AddAction(SerializedProperty actionMap, int index = -1)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,17 @@ public static Command PasteActionMaps()
};
}

public static Command PasteActionIntoActionMap(int actionMapIndex)
{
return (in InputActionsEditorState state) =>
{
var lastPastedElement = CopyPasteHelper.PasteActionsOrBindingsFromClipboard(state, true, actionMapIndex);
if (lastPastedElement != null)
state.serializedObject.ApplyModifiedProperties();
return state;
};
}

public static Command PasteActionFromActionMap()
{
return (in InputActionsEditorState state) =>
Expand Down Expand Up @@ -230,6 +241,117 @@ private static InputActionsEditorState SelectPrevActionMap(InputActionsEditorSta
return state.SelectActionMap(index);
}

public static Command ReorderActionMap(int oldIndex, int newIndex)
{
return (in InputActionsEditorState state) =>
{
InputActionSerializationHelpers.MoveActionMap(state.serializedObject, oldIndex, newIndex);
state.serializedObject.ApplyModifiedProperties();
return state.SelectActionMap(newIndex);
};
}

public static Command MoveAction(int oldIndex, int newIndex)
{
return (in InputActionsEditorState state) =>
{
var actionMap = Selectors.GetSelectedActionMap(state)?.wrappedProperty;
InputActionSerializationHelpers.MoveAction(actionMap, oldIndex, newIndex);
state.serializedObject.ApplyModifiedProperties();
return state.SelectAction(newIndex);
};
}

public static Command MoveBinding(int oldIndex, int actionIndex, int childIndex)
{
return (in InputActionsEditorState state) =>
{
var newBindingIndex = MoveBindingOrComposite(state, oldIndex, actionIndex, childIndex);
state.serializedObject.ApplyModifiedProperties();
return state.SelectBinding(newBindingIndex);
};
}

public static Command MoveComposite(int oldIndex, int actionIndex, int childIndex)
{
return (in InputActionsEditorState state) =>
{
var actionMap = Selectors.GetSelectedActionMap(state)?.wrappedProperty;
var compositeBindings = CopyPasteHelper.GetBindingsForComposite(actionMap?.FindPropertyRelative(nameof(InputActionMap.m_Bindings)), oldIndex);
//move the composite element
var newBindingIndex = MoveBindingOrComposite(state, oldIndex, actionIndex, childIndex);
var actionTo = Selectors.GetActionForIndex(actionMap, actionIndex).FindPropertyRelative(nameof(InputAction.m_Name)).stringValue;
var toIndex = newBindingIndex;
foreach (var compositePart in compositeBindings)
{
// the index of the composite part stays the same if composite was moved down as previous elements are shifted down (the index seems to update async so it's safer to use the oldIndex)
// if the composite was moved up, the index of the composite part is not changing so we are safe to use it
var from = oldIndex < newBindingIndex ? oldIndex : compositePart.GetIndexOfArrayElement();
// if added below the old position the array changes as composite parts are added on top (increase the index)
// if added above the oldIndex, the index does not change
var to = oldIndex < newBindingIndex ? newBindingIndex : ++toIndex;
InputActionSerializationHelpers.MoveBinding(actionMap, from, to);
Selectors.GetCompositeOrBindingInMap(actionMap, to).wrappedProperty.FindPropertyRelative("m_Action").stringValue = actionTo;
}
state.serializedObject.ApplyModifiedProperties();
return state.SelectBinding(newBindingIndex);
};
}

private static int MoveBindingOrComposite(InputActionsEditorState state, int oldIndex, int actionIndex, int childIndex)
{
var actionMap = Selectors.GetSelectedActionMap(state)?.wrappedProperty;
var bindingsForAction = Selectors.GetBindingsForAction(state, actionMap, actionIndex);
var allBindings = actionMap?.FindPropertyRelative(nameof(InputActionMap.m_Bindings));
var actionTo = Selectors.GetActionForIndex(actionMap, actionIndex).FindPropertyRelative(nameof(InputAction.m_Name)).stringValue;
var actionFrom = Selectors.GetCompositeOrBindingInMap(actionMap, oldIndex).wrappedProperty.FindPropertyRelative("m_Action");
int newBindingIndex;
if (bindingsForAction.Count == 0) //if there are no bindings for an action retrieve the first binding index of a binding before (iterate previous actions)
newBindingIndex = Selectors.GetBindingIndexBeforeAction(allBindings, actionIndex, allBindings);
else
{
var toSkip = GetNumberOfCompositePartItemsToSkip(bindingsForAction, childIndex, oldIndex); //skip composite parts if there are - avoid moving into a composite
newBindingIndex = bindingsForAction[0].GetIndexOfArrayElement() + Math.Clamp(childIndex + toSkip, 0, bindingsForAction.Count);
newBindingIndex -= newBindingIndex > oldIndex && !actionTo.Equals(actionFrom.stringValue) ? 1 : 0; // reduce index by one in case the moved binding will be shifted underneath to another action
}

actionFrom.stringValue = actionTo;
InputActionSerializationHelpers.MoveBinding(actionMap, oldIndex, newBindingIndex);
return newBindingIndex;
}

private static int GetNumberOfCompositePartItemsToSkip(List<SerializedProperty> bindings, int childIndex, int oldIndex)
{
var toSkip = 0;
var normalBindings = 0;
foreach (var binding in bindings)
{
if (binding.GetIndexOfArrayElement() == oldIndex)
continue;
if (normalBindings > childIndex)
break;
if (binding.FindPropertyRelative(nameof(InputBinding.m_Flags)).intValue ==
(int)InputBinding.Flags.PartOfComposite)
toSkip++;
else
normalBindings++;
}
return toSkip;
}

public static Command MovePartOfComposite(int oldIndex, int newIndex, int compositeIndex)
{
return (in InputActionsEditorState state) =>
{
var actionMap = Selectors.GetSelectedActionMap(state)?.wrappedProperty;
var actionTo = actionMap?.FindPropertyRelative(nameof(InputActionMap.m_Bindings)).GetArrayElementAtIndex(compositeIndex).FindPropertyRelative("m_Action").stringValue;
InputActionSerializationHelpers.MoveBinding(actionMap, oldIndex, newIndex);
Selectors.GetCompositeOrBindingInMap(actionMap, newIndex).wrappedProperty.FindPropertyRelative("m_Action").stringValue = actionTo;
state.serializedObject.ApplyModifiedProperties();
return state.SelectBinding(newIndex);
};
}

public static Command DeleteAction(int actionMapIndex, string actionName)
{
return (in InputActionsEditorState state) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public ActionMapsView(VisualElement root, StateContainer stateContainer)
{
m_ListView = root.Q<ListView>("action-maps-list-view");
m_ListView.selectionType = UIElements.SelectionType.Single;

m_ListView.reorderable = true;
m_ListViewSelectionChangeFilter = new CollectionViewSelectionChangeFilter(m_ListView);
m_ListViewSelectionChangeFilter.selectedIndicesChanged += (selectedIndices) =>
{
Expand All @@ -34,6 +34,7 @@ public ActionMapsView(VisualElement root, StateContainer stateContainer)
treeViewItem.DuplicateCallback = _ => DuplicateActionMap(i);
treeViewItem.OnDeleteItem += treeViewItem.DeleteCallback;
treeViewItem.OnDuplicateItem += treeViewItem.DuplicateCallback;
treeViewItem.userData = i;

ContextMenu.GetContextMenuForActionMapItem(treeViewItem);
};
Expand All @@ -55,6 +56,10 @@ public ActionMapsView(VisualElement root, StateContainer stateContainer)

m_ListView.RegisterCallback<ExecuteCommandEvent>(OnExecuteCommand);
m_ListView.RegisterCallback<ValidateCommandEvent>(OnValidateCommand);
var treeView = root.Q<TreeView>("actions-tree-view");
m_ListView.AddManipulator(new DropManipulator(OnDroppedHandler, treeView));
m_ListView.itemIndexChanged += OnReorder;


CreateSelector(s => new ViewStateCollection<string>(Selectors.GetActionMapNames(s)),
(actionMapNames, state) => new ViewState(Selectors.GetSelectedActionMap(state), actionMapNames));
Expand All @@ -64,6 +69,17 @@ public ActionMapsView(VisualElement root, StateContainer stateContainer)
ContextMenu.GetContextMenuForActionMapListView(this, m_ListView.parent);
}

void OnDroppedHandler(int mapIndex)
{
Dispatch(Commands.CutActionsOrBindings());
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is likely fine but I would recommend when we do a sequence of commands like this that they are implemented as a single command instead but is the combination of the two underneath. Otherwise this would yield two Undo steps "Cut" and "Paste" which might be confusing for a Drop? Another aspect is that since dispatched, if CutActionsOrBindings() throws, the new state is not propagated and the Paste will execute on the previous state. I think I have seen this in more places so might be that we overlook all places at a later point for exception-safety and Undo convenience.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right, I think there your implementation of Dispatch(..,continueWith) would be a great solution, I would wait until this is landed and make use of this.

Dispatch(Commands.PasteActionIntoActionMap(mapIndex));
}

void OnReorder(int oldIndex, int newIndex)
{
Dispatch(Commands.ReorderActionMap(oldIndex, newIndex));
}

public override void RedrawUI(ViewState viewState)
{
m_ListView.itemsSource = viewState.actionMapNames?.ToList() ?? new List<string>();
Expand Down
Loading