-
Notifications
You must be signed in to change notification settings - Fork 316
/
InputRecorderInspector.cs
227 lines (204 loc) · 11.8 KB
/
InputRecorderInspector.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.Events;
////TODO: add ability to inspect contents of event traces in a separate window
namespace UnityEngine.InputSystem.Editor
{
/// <summary>
/// A custom inspector for <see cref="InputRecorder"/>. Adds UI elements to store captures in files, to load them from
/// there, and to initiate replays from within the editor. It also shows information for when captures or replays are
/// in progress.
/// </summary>
[CustomEditor(typeof(InputRecorder))]
internal class InputRecorderInspector : UnityEditor.Editor
{
protected void OnEnable()
{
m_DevicePathProperty = serializedObject.FindProperty("m_DevicePath");
m_RecordButtonPath = serializedObject.FindProperty("m_RecordButtonPath");
m_PlayButtonPathProperty = serializedObject.FindProperty("m_PlayButtonPath");
m_RecordFramesProperty = serializedObject.FindProperty("m_RecordFrames");
m_RecordStateEventsOnlyProperty = serializedObject.FindProperty("m_RecordStateEventsOnly");
m_ReplayOnNewDevicesProperty = serializedObject.FindProperty("m_ReplayOnNewDevices");
m_SimulateTimingOnReplayProperty = serializedObject.FindProperty("m_SimulateOriginalTimingOnReplay");
m_CaptureMemoryDefaultSizeProperty = serializedObject.FindProperty("m_CaptureMemoryDefaultSize");
m_CaptureMemoryMaxSizeProperty = serializedObject.FindProperty("m_CaptureMemoryMaxSize");
m_StartRecordingWhenEnabledProperty = serializedObject.FindProperty("m_StartRecordingWhenEnabled");
m_AllInput = string.IsNullOrEmpty(m_DevicePathProperty.stringValue);
m_PlayText = EditorGUIUtility.TrIconContent("PlayButton", "Play the current input capture.");
m_PauseText = EditorGUIUtility.TrIconContent("PauseButton", "Pause the current input playback.");
m_ResumeText = EditorGUIUtility.TrIconContent("PauseButton On", "Resume the current input playback.");
m_StepForwardText = EditorGUIUtility.TrIconContent("d_StepButton", "Play the next input event.");
m_StepBackwardText = EditorGUIUtility.TrIconContent("d_StepLeftButton", "Play the previous input event.");
m_StopText = EditorGUIUtility.TrIconContent("PlayButton On", "Stop the current input playback.");
m_RecordText = EditorGUIUtility.TrIconContent("Animation.Record", "Start recording input.");
var recorder = (InputRecorder)serializedObject.targetObject;
m_OnRecordEvent = _ => Repaint();
recorder.changeEvent.AddListener(m_OnRecordEvent);
}
protected void OnDisable()
{
var recorder = (InputRecorder)serializedObject.targetObject;
recorder.changeEvent.RemoveListener(m_OnRecordEvent);
}
public override void OnInspectorGUI()
{
var recorder = (InputRecorder)serializedObject.targetObject;
using (var scope = new EditorGUI.ChangeCheckScope())
{
var newAllInput = EditorGUILayout.Toggle(m_AllInputText, m_AllInput);
if (!newAllInput)
{
using (new EditorGUI.IndentLevelScope())
{
EditorGUILayout.PropertyField(m_DevicePathProperty, m_DeviceText);
}
}
else if (newAllInput != m_AllInput)
{
m_DevicePathProperty.stringValue = string.Empty;
}
m_AllInput = newAllInput;
EditorGUILayout.PropertyField(m_RecordFramesProperty);
EditorGUILayout.PropertyField(m_RecordStateEventsOnlyProperty);
EditorGUILayout.PropertyField(m_ReplayOnNewDevicesProperty);
EditorGUILayout.PropertyField(m_SimulateTimingOnReplayProperty);
EditorGUILayout.PropertyField(m_StartRecordingWhenEnabledProperty, m_RecordWhenEnabledText);
var defaultSizeInMB = m_CaptureMemoryDefaultSizeProperty.intValue / (1024 * 1024);
var newDefaultSizeInMB = EditorGUILayout.IntSlider(m_DefaultSizeText, defaultSizeInMB, 1, 100);
if (newDefaultSizeInMB != defaultSizeInMB)
m_CaptureMemoryDefaultSizeProperty.intValue = newDefaultSizeInMB * 1024 * 1024;
var maxSizeInMB = m_CaptureMemoryMaxSizeProperty.intValue / (1024 * 1024);
var newMaxSizeInMB = maxSizeInMB < newDefaultSizeInMB
? newDefaultSizeInMB
: EditorGUILayout.IntSlider(m_MaxSizeText, maxSizeInMB, 1, 100);
if (newMaxSizeInMB != maxSizeInMB)
m_CaptureMemoryMaxSizeProperty.intValue = newMaxSizeInMB * 1024 * 1024;
EditorGUILayout.PropertyField(m_RecordButtonPath, m_RecordButtonText);
EditorGUILayout.PropertyField(m_PlayButtonPathProperty, m_PlayButtonText);
if (scope.changed)
serializedObject.ApplyModifiedProperties();
}
EditorGUILayout.Space();
using (new EditorGUILayout.HorizontalScope())
{
////TODO: go-to-next and go-to-previous button
// Play and pause buttons.
EditorGUI.BeginDisabledGroup(recorder.eventCount == 0 || recorder.captureIsRunning);
var oldIsPlaying = recorder.replayIsRunning;
var newIsPlaying = GUILayout.Toggle(oldIsPlaying, !oldIsPlaying ? m_PlayText : m_StopText, EditorStyles.miniButton,
GUILayout.Width(50));
if (oldIsPlaying != newIsPlaying)
{
if (newIsPlaying)
recorder.StartReplay();
else
recorder.StopReplay();
}
if (newIsPlaying && recorder.replay != null && GUILayout.Button(recorder.replay.paused ? m_ResumeText : m_PauseText, EditorStyles.miniButton,
GUILayout.Width(50)))
{
if (recorder.replay.paused)
recorder.StartReplay();
else
recorder.PauseReplay();
}
EditorGUI.EndDisabledGroup();
// Record button.
EditorGUI.BeginDisabledGroup(recorder.replayIsRunning);
var oldIsRecording = recorder.captureIsRunning;
var newIsRecording = GUILayout.Toggle(oldIsRecording, m_RecordText, EditorStyles.miniButton, GUILayout.Width(50));
if (oldIsRecording != newIsRecording)
{
if (newIsRecording)
recorder.StartCapture();
else
recorder.StopCapture();
}
EditorGUI.EndDisabledGroup();
// Load button.
EditorGUI.BeginDisabledGroup(recorder.replayIsRunning);
if (GUILayout.Button("Load"))
{
var filePath = EditorUtility.OpenFilePanel("Choose Input Event Trace to Load", string.Empty, "inputtrace");
if (!string.IsNullOrEmpty(filePath))
recorder.LoadCaptureFromFile(filePath);
}
EditorGUI.EndDisabledGroup();
// Save button.
EditorGUI.BeginDisabledGroup(recorder.eventCount == 0 || recorder.replayIsRunning);
if (GUILayout.Button("Save"))
{
var filePath = EditorUtility.SaveFilePanel("Choose Where to Save Input Event Trace", string.Empty, $"{recorder.gameObject.name}.inputtrace", "inputtrace");
if (!string.IsNullOrEmpty(filePath))
recorder.SaveCaptureToFile(filePath);
}
// Clear button.
if (GUILayout.Button("Clear"))
{
recorder.ClearCapture();
Repaint();
}
EditorGUI.EndDisabledGroup();
}
////TODO: allow hotscrubbing
// Play bar.
EditorGUILayout.IntSlider(recorder.replayPosition, 0, (int)recorder.eventCount);
EditorGUILayout.Space();
using (new EditorGUI.DisabledScope())
{
EditorGUILayout.LabelField(m_InfoText, EditorStyles.miniBoldLabel);
using (new EditorGUI.IndentLevelScope())
{
EditorGUILayout.LabelField($"{recorder.eventCount} events", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"{recorder.totalEventSizeInBytes / 1024} kb captured", EditorStyles.miniLabel);
EditorGUILayout.LabelField($"{recorder.allocatedSizeInBytes / 1024} kb allocated", EditorStyles.miniLabel);
if (recorder.capture != null)
{
var devices = recorder.capture.deviceInfos;
if (devices.Count > 0)
{
EditorGUILayout.LabelField(m_DevicesText, EditorStyles.miniBoldLabel);
using (new EditorGUI.IndentLevelScope())
{
foreach (var device in devices)
{
EditorGUILayout.LabelField(device.layout, EditorStyles.miniLabel);
}
}
}
}
}
}
}
private bool m_AllInput;
private SerializedProperty m_DevicePathProperty;
private SerializedProperty m_RecordButtonPath;
private SerializedProperty m_PlayButtonPathProperty;
private SerializedProperty m_RecordFramesProperty;
private SerializedProperty m_RecordStateEventsOnlyProperty;
private SerializedProperty m_ReplayOnNewDevicesProperty;
private SerializedProperty m_SimulateTimingOnReplayProperty;
private SerializedProperty m_CaptureMemoryDefaultSizeProperty;
private SerializedProperty m_CaptureMemoryMaxSizeProperty;
private SerializedProperty m_StartRecordingWhenEnabledProperty;
private UnityAction<InputRecorder.Change> m_OnRecordEvent;
private GUIContent m_RecordButtonText = new GUIContent("Record Button", "If set, this button will start and stop capture in play mode.");
private GUIContent m_PlayButtonText = new GUIContent("Play Button", "If set, this button will start and stop replay of the current capture in play mode.");
private GUIContent m_RecordWhenEnabledText = new GUIContent("Capture When Enabled", "If true, recording will start immediately when the component is enabled in play mode.");
private GUIContent m_DevicesText = new GUIContent("Devices");
private GUIContent m_AllInputText = new GUIContent("All Input", "Whether to record input from all devices or from just specific devices.");
private GUIContent m_DeviceText = new GUIContent("Device", "Type of device to record input from.");
private GUIContent m_InfoText = new GUIContent("Info:");
private GUIContent m_DefaultSizeText = new GUIContent("Default Size (MB)", "Memory allocate for capture by default. Will automatically grow up to max memory.");
private GUIContent m_MaxSizeText = new GUIContent("Max Size (MB)", "Maximum memory allocated for capture. Once a capture reaches this limit, new events will start overwriting old ones.");
private GUIContent m_PlayText;
private GUIContent m_PauseText;
private GUIContent m_ResumeText;
private GUIContent m_StepForwardText;
private GUIContent m_StepBackwardText;
private GUIContent m_StopText;
private GUIContent m_RecordText;
}
}
#endif