A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
2using System;
3using System.Linq;
4using UnityEditor;
5using UnityEngine.UIElements;
6
7namespace UnityEngine.InputSystem.Editor
8{
9 internal class PropertiesView : ViewBase<PropertiesView.ViewState>
10 {
11 private ActionPropertiesView m_ActionPropertyView;
12 private BindingPropertiesView m_BindingPropertyView;
13 private NameAndParametersListView m_InteractionsListView;
14 private NameAndParametersListView m_ProcessorsListView;
15
16 private Foldout interactionsFoldout => rootElement.Q<Foldout>("interactions-foldout");
17 private Foldout processorsFoldout => rootElement.Q<Foldout>("processors-foldout");
18
19 private TextElement addInteractionButton;
20 private TextElement addProcessorButton;
21
22 public PropertiesView(VisualElement root, StateContainer stateContainer)
23 : base(root, stateContainer)
24 {
25 CreateSelector(
26 Selectors.GetSelectedAction,
27 Selectors.GetSelectedBinding,
28 state => state.selectionType,
29 (inputAction, inputBinding, selectionType, s) => new ViewState()
30 {
31 selectionType = selectionType,
32 serializedInputAction = inputAction,
33 inputBinding = inputBinding,
34 relatedInputAction = Selectors.GetRelatedInputAction(s)
35 });
36
37 var interactionsToggle = interactionsFoldout.Q<Toggle>();
38 interactionsToggle.AddToClassList("properties-foldout-toggle");
39 if (addInteractionButton == null)
40 {
41 addInteractionButton = CreateAddButton(interactionsToggle, "add-new-interaction-button");
42 new ContextualMenuManipulator(_ => {}){target = addInteractionButton, activators = {new ManipulatorActivationFilter(){button = MouseButton.LeftMouse}}};
43 }
44 var processorToggle = processorsFoldout.Q<Toggle>();
45 processorToggle.AddToClassList("properties-foldout-toggle");
46 if (addProcessorButton == null)
47 {
48 addProcessorButton = CreateAddButton(processorToggle, "add-new-processor-button");
49 new ContextualMenuManipulator(_ => {}){target = addProcessorButton, activators = {new ManipulatorActivationFilter(){button = MouseButton.LeftMouse}}};
50 }
51 }
52
53 private TextElement CreateAddButton(Toggle toggle, string name)
54 {
55 var addButton = new Button();
56 addButton.text = "+";
57 addButton.name = name;
58 addButton.focusable = false;
59 #if UNITY_EDITOR_OSX
60 addButton.clickable.activators.Clear();
61 #endif
62 addButton.AddToClassList("add-interaction-processor-button");
63 toggle.Add(addButton);
64 return addButton;
65 }
66
67 private void CreateContextMenuProcessor(string expectedControlType)
68 {
69 var processors = InputProcessor.s_Processors;
70 Type expectedValueType = string.IsNullOrEmpty(expectedControlType) ? null : EditorInputControlLayoutCache.GetValueType(expectedControlType);
71 addProcessorButton.RegisterCallback<ContextualMenuPopulateEvent>(evt =>
72 {
73 evt.menu.ClearItems();
74 foreach (var name in processors.internedNames.Where(x => !processors.ShouldHideInUI(x)).OrderBy(x => x.ToString()))
75 {
76 // Skip if not compatible with value type.
77 if (!IsValidProcessorForControl(expectedValueType, name))
78 continue;
79 var niceName = ObjectNames.NicifyVariableName(name);
80 evt.menu.AppendAction(niceName, _ => m_ProcessorsListView.OnAddElement(name.ToString()));
81 }
82 });
83 }
84
85 private bool IsValidProcessorForControl(Type expectedValueType, string name)
86 {
87 if (expectedValueType == null) return true;
88 var type = InputProcessor.s_Processors.LookupTypeRegistration(name);
89 var valueType = InputProcessor.GetValueTypeFromType(type);
90 if (valueType != null && !expectedValueType.IsAssignableFrom(valueType))
91 return false;
92 return true;
93 }
94
95 private void CreateContextMenuInteraction(string expectedControlType)
96 {
97 var interactions = InputInteraction.s_Interactions;
98 Type expectedValueType = string.IsNullOrEmpty(expectedControlType) ? null : EditorInputControlLayoutCache.GetValueType(expectedControlType);
99 addInteractionButton.RegisterCallback<ContextualMenuPopulateEvent>(evt =>
100 {
101 evt.menu.ClearItems();
102 foreach (var name in interactions.internedNames.Where(x => !interactions.ShouldHideInUI(x)).OrderBy(x => x.ToString()))
103 {
104 // Skip if not compatible with value type.
105 if (!IsValidInteractionForControl(expectedValueType, name))
106 continue;
107 var niceName = ObjectNames.NicifyVariableName(name);
108 evt.menu.AppendAction(niceName, _ => m_InteractionsListView.OnAddElement(name.ToString()));
109 }
110 });
111 }
112
113 private bool IsValidInteractionForControl(Type expectedValueType, string name)
114 {
115 if (expectedValueType == null) return true;
116 var type = InputInteraction.s_Interactions.LookupTypeRegistration(name);
117 var valueType = InputInteraction.GetValueType(type);
118 if (valueType != null && !expectedValueType.IsAssignableFrom(valueType))
119 return false;
120 return true;
121 }
122
123 public override void RedrawUI(ViewState viewState)
124 {
125 DestroyChildView(m_ActionPropertyView);
126 DestroyChildView(m_BindingPropertyView);
127 DestroyChildView(m_InteractionsListView);
128 DestroyChildView(m_ProcessorsListView);
129
130 var propertiesContainer = rootElement.Q<VisualElement>("properties-container");
131
132 var foldout = propertiesContainer.Q<Foldout>("properties-foldout");
133 foldout.Clear();
134
135 var visualElement = new VisualElement();
136 foldout.Add(visualElement);
137 foldout.Q<Toggle>().AddToClassList("properties-foldout-toggle");
138
139 var inputAction = viewState.serializedInputAction;
140 var inputActionOrBinding = inputAction?.wrappedProperty;
141
142 switch (viewState.selectionType)
143 {
144 case SelectionType.Action:
145 rootElement.Q<Label>("properties-header-label").text = "Action Properties";
146 m_ActionPropertyView = CreateChildView(new ActionPropertiesView(visualElement, foldout, stateContainer));
147 break;
148
149 case SelectionType.Binding:
150 rootElement.Q<Label>("properties-header-label").text = "Binding Properties";
151 m_BindingPropertyView = CreateChildView(new BindingPropertiesView(visualElement, foldout, stateContainer));
152 inputAction = viewState.relatedInputAction;
153 inputActionOrBinding = viewState.inputBinding?.wrappedProperty;
154 break;
155 }
156
157 CreateContextMenuProcessor(inputAction?.expectedControlType);
158 CreateContextMenuInteraction(inputAction?.expectedControlType);
159
160 var isPartOfComposite = viewState.selectionType == SelectionType.Binding &&
161 viewState.inputBinding?.isPartOfComposite == true;
162 //don't show for Bindings in Composites
163 if (!isPartOfComposite)
164 {
165 interactionsFoldout.style.display = DisplayStyle.Flex;
166 m_InteractionsListView = CreateChildView(new NameAndParametersListView(
167 interactionsFoldout,
168 stateContainer,
169 inputActionOrBinding?.FindPropertyRelative(nameof(InputAction.m_Interactions)),
170 state => Selectors.GetInteractionsAsParameterListViews(state, inputAction)));
171 }
172 else
173 interactionsFoldout.style.display = DisplayStyle.None;
174
175
176 m_ProcessorsListView = CreateChildView(new NameAndParametersListView(
177 processorsFoldout,
178 stateContainer,
179 inputActionOrBinding?.FindPropertyRelative(nameof(InputAction.m_Processors)),
180 state => Selectors.GetProcessorsAsParameterListViews(state, inputAction)));
181 }
182
183 internal class ViewState
184 {
185 public SerializedInputAction? relatedInputAction;
186 public SerializedInputBinding? inputBinding;
187 public SerializedInputAction? serializedInputAction;
188 public SelectionType selectionType;
189 }
190 }
191}
192
193#endif