A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
2using System;
3using System.ComponentModel;
4using System.Linq;
5using System.Reflection;
6using UnityEditor;
7using UnityEngine.InputSystem.Layouts;
8using UnityEngine.InputSystem.Utilities;
9using UnityEngine.UIElements;
10
11namespace UnityEngine.InputSystem.Editor
12{
13 internal static class ContextMenu
14 {
15 private static readonly string copy_String = "Copy";
16 private static readonly string cut_String = "Cut";
17 private static readonly string paste_String = "Paste";
18
19 private static readonly string rename_String = "Rename";
20 private static readonly string duplicate_String = "Duplicate";
21 private static readonly string delete_String = "Delete";
22
23 private static readonly string add_Action_Map_String = "Add Action Map";
24 private static readonly string add_Action_String = "Add Action";
25 private static readonly string add_Binding_String = "Add Binding";
26
27 #region ActionMaps
28 public static void GetContextMenuForActionMapItem(ActionMapsView mapView, InputActionMapsTreeViewItem treeViewItem, int index)
29 {
30 treeViewItem.OnContextualMenuPopulateEvent = (menuEvent =>
31 {
32 // TODO: AddAction should enable m_RenameOnActionAdded
33 menuEvent.menu.AppendAction(add_Action_String, _ => mapView.Dispatch(Commands.AddAction()));
34 menuEvent.menu.AppendSeparator();
35 menuEvent.menu.AppendAction(rename_String, _ => mapView.RenameActionMap(index));
36 menuEvent.menu.AppendAction(duplicate_String, _ => mapView.DuplicateActionMap(index));
37 menuEvent.menu.AppendAction(delete_String, _ => mapView.DeleteActionMap(index));
38 menuEvent.menu.AppendSeparator();
39 menuEvent.menu.AppendAction(copy_String, _ => mapView.CopyItems());
40 menuEvent.menu.AppendAction(cut_String, _ => mapView.CutItems());
41
42 var copiedAction = CopyPasteHelper.GetCopiedClipboardType() == typeof(InputAction);
43 if (CopyPasteHelper.HasPastableClipboardData(typeof(InputActionMap)))
44 menuEvent.menu.AppendAction(paste_String, _ => mapView.PasteItems(copiedAction));
45 });
46 }
47
48 // Add "Add Action Map" option to empty space under the ListView. Matches with old IMGUI style (ISX-1519).
49 // Include Paste here as well, since it makes sense for adding ActionMaps.
50 public static void GetContextMenuForActionMapsEmptySpace(ActionMapsView mapView, VisualElement element, bool onlyShowIfListIsEmpty = false)
51 {
52 _ = new ContextualMenuManipulator(menuEvent =>
53 {
54 if (!onlyShowIfListIsEmpty || mapView.GetMapCount() == 0)
55 {
56 var copiedAction = CopyPasteHelper.GetCopiedClipboardType() == typeof(InputAction);
57 if (CopyPasteHelper.HasPastableClipboardData(typeof(InputActionMap)))
58 menuEvent.menu.AppendAction(paste_String, _ => mapView.PasteItems(copiedAction));
59
60 menuEvent.menu.AppendSeparator();
61 menuEvent.menu.AppendAction(add_Action_Map_String, _ => mapView.AddActionMap());
62 }
63 }) { target = element };
64 }
65
66 #endregion
67
68 #region Actions
69 // Add the "Paste" option to all elements in the Action area.
70 public static void GetContextMenuForActionListView(ActionsTreeView actionsTreeView, TreeView treeView, VisualElement target)
71 {
72 _ = new ContextualMenuManipulator(menuEvent =>
73 {
74 var item = treeView.GetItemDataForIndex<ActionOrBindingData>(treeView.selectedIndex);
75 var hasPastableData = CopyPasteHelper.HasPastableClipboardData(item.isAction ? typeof(InputAction) : typeof(InputBinding));
76 if (hasPastableData)
77 menuEvent.menu.AppendAction(paste_String, _ => actionsTreeView.PasteItems());
78 }) { target = target };
79 }
80
81 // Add "Add Action" option to empty space under the TreeView. Matches with old IMGUI style (ISX-1519).
82 // Include Paste here as well, since it makes sense for Actions; thus users would expect it for Bindings too.
83 public static void GetContextMenuForActionsEmptySpace(ActionsTreeView actionsTreeView, TreeView treeView, VisualElement target, bool onlyShowIfTreeIsEmpty = false)
84 {
85 _ = new ContextualMenuManipulator(menuEvent =>
86 {
87 if (actionsTreeView.GetMapCount() > 0 && (!onlyShowIfTreeIsEmpty || treeView.GetTreeCount() == 0))
88 {
89 var item = treeView.GetItemDataForIndex<ActionOrBindingData>(treeView.selectedIndex);
90 if (CopyPasteHelper.HasPastableClipboardData(item.isAction ? typeof(InputAction) : typeof(InputBinding)))
91 menuEvent.menu.AppendAction(paste_String, _ => actionsTreeView.PasteItems());
92
93 menuEvent.menu.AppendSeparator();
94 menuEvent.menu.AppendAction(add_Action_String, _ => actionsTreeView.AddAction());
95 }
96 }) { target = target };
97 }
98
99 public static void GetContextMenuForActionItem(ActionsTreeView treeView, InputActionsTreeViewItem treeViewItem, string controlLayout, int index)
100 {
101 _ = new ContextualMenuManipulator(menuEvent =>
102 {
103 menuEvent.menu.AppendAction(add_Binding_String, _ => treeView.AddBinding(index));
104 AppendCompositeMenuItems(treeView, controlLayout, index, (name, action) => menuEvent.menu.AppendAction(name, _ => action.Invoke()));
105 menuEvent.menu.AppendSeparator();
106 AppendRenameAction(menuEvent, treeView, index);
107 AppendDuplicateDeleteCutAndCopyActionsSection(menuEvent, treeView, index);
108 }) { target = treeViewItem };
109 }
110
111 public static Action GetContextMenuForActionAddItem(ActionsTreeView treeView, string controlLayout, int index)
112 {
113 return () =>
114 {
115 GenericMenu menu = new GenericMenu();
116 menu.AddItem(new GUIContent(add_Binding_String), false, () => treeView.AddBinding(index));
117 AppendCompositeMenuItems(treeView, controlLayout, index, (name, action) => menu.AddItem(new GUIContent(name), false, action.Invoke));
118 menu.ShowAsContext();
119 };
120 }
121
122 private static void AppendCompositeMenuItems(ActionsTreeView treeView, string expectedControlLayout, int index, Action<string, Action> addToMenuAction)
123 {
124 foreach (var compositeName in InputBindingComposite.s_Composites.internedNames.Where(x =>
125 !InputBindingComposite.s_Composites.aliases.Contains(x)).OrderBy(x => x))
126 {
127 // Skip composites we should hide
128 var compositeType = InputBindingComposite.s_Composites.LookupTypeRegistration(compositeName);
129 var designTimeVisible = compositeType.GetCustomAttribute<DesignTimeVisibleAttribute>();
130 if (designTimeVisible != null && !designTimeVisible.Visible)
131 continue;
132
133 // Skip composites that don't match the expected control layout
134 // NOTE: "Any" is a special case and expected to be null
135 if (!string.IsNullOrEmpty(expectedControlLayout))
136 {
137 var valueType = InputBindingComposite.GetValueType(compositeName);
138 if (valueType != null &&
139 !InputControlLayout.s_Layouts.ValueTypeIsAssignableFrom(
140 new InternedString(expectedControlLayout), valueType))
141 continue;
142 }
143
144 var displayName = compositeType.GetCustomAttribute<DisplayNameAttribute>();
145 var niceName = displayName != null ? displayName.DisplayName.Replace('/', '\\') : ObjectNames.NicifyVariableName(compositeName) + " Composite";
146 addToMenuAction.Invoke($"Add {niceName}", () => treeView.AddComposite(index, compositeName));
147 }
148 }
149
150 public static void GetContextMenuForCompositeItem(ActionsTreeView treeView, InputActionsTreeViewItem treeViewItem, int index)
151 {
152 _ = new ContextualMenuManipulator(menuEvent =>
153 {
154 AppendRenameAction(menuEvent, treeView, index);
155 AppendDuplicateDeleteCutAndCopyActionsSection(menuEvent, treeView, index);
156 }) { target = treeViewItem };
157 }
158
159 public static void GetContextMenuForBindingItem(ActionsTreeView treeView, InputActionsTreeViewItem treeViewItem, int index)
160 {
161 _ = new ContextualMenuManipulator(menuEvent =>
162 {
163 AppendDuplicateDeleteCutAndCopyActionsSection(menuEvent, treeView, index);
164 }) { target = treeViewItem };
165 }
166
167 private static void AppendRenameAction(ContextualMenuPopulateEvent menuEvent, ActionsTreeView treeView, int index)
168 {
169 menuEvent.menu.AppendAction(rename_String, _ => treeView.RenameActionItem(index));
170 }
171
172 // These actions are always either all present, or all missing, so we can group their Append calls here.
173 private static void AppendDuplicateDeleteCutAndCopyActionsSection(ContextualMenuPopulateEvent menuEvent, ActionsTreeView actionsTreeView, int index)
174 {
175 menuEvent.menu.AppendAction(duplicate_String, _ => actionsTreeView.DuplicateItem(index));
176 menuEvent.menu.AppendAction(delete_String, _ => actionsTreeView.DeleteItem(index));
177 menuEvent.menu.AppendSeparator();
178 menuEvent.menu.AppendAction(copy_String, _ => actionsTreeView.CopyItems());
179 menuEvent.menu.AppendAction(cut_String, _ => actionsTreeView.CutItems());
180 }
181
182 #endregion
183 }
184}
185#endif