A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Text;
4using UnityEditor.Rendering.Analytics;
5using UnityEngine;
6using UnityEngine.Rendering;
7
8namespace UnityEditor.Rendering
9{
10 /// <summary>
11 /// Attribute specifying wich type of Debug Item should this drawer be used with.
12 /// </summary>
13 public class DebugUIDrawerAttribute : Attribute
14 {
15 internal readonly Type type;
16
17 /// <summary>
18 /// Constructor for DebugUIDraw Attribute
19 /// </summary>
20 /// <param name="type">Type of Debug Item this draw should be used with.</param>
21 public DebugUIDrawerAttribute(Type type)
22 {
23 this.type = type;
24 }
25 }
26
27 /// <summary>
28 /// Debug Item Drawer
29 /// </summary>
30 public class DebugUIDrawer
31 {
32 /// <summary>
33 /// Cast into the proper type.
34 /// </summary>
35 /// <typeparam name="T">Type of the drawer</typeparam>
36 /// <param name="o">Object to be cast</param>
37 /// <returns>Returns o cast to type T</returns>
38 protected T Cast<T>(object o)
39 where T : class
40 {
41 if (o == null) return null;
42
43 if (o is T casted)
44 return casted;
45
46 StringBuilder info = new StringBuilder("Cast Exception:");
47 switch (o)
48 {
49 case DebugUI.Widget value:
50 info.AppendLine($"Query Path : {value.queryPath}");
51 break;
52 case DebugState state:
53 info.AppendLine($"Query Path : {state.queryPath}");
54 break;
55 }
56 info.AppendLine($"Object to Cast Type : {o.GetType().AssemblyQualifiedName}");
57 info.AppendLine($"Target Cast Type : {typeof(T).AssemblyQualifiedName}");
58
59 throw new InvalidCastException(info.ToString());
60 }
61
62 /// <summary>
63 /// Implement this to execute processing before UI rendering.
64 /// </summary>
65 /// <param name="widget">Widget that is going to be rendered.</param>
66 /// <param name="state">Debug State associated with the Debug Item.</param>
67 public virtual void Begin(DebugUI.Widget widget, DebugState state)
68 { }
69
70 /// <summary>
71 /// Implement this to execute UI rendering.
72 /// </summary>
73 /// <param name="widget">Widget that is going to be rendered.</param>
74 /// <param name="state">Debug State associated with the Debug Item.</param>
75 /// <returns>Returns the state of the widget.</returns>
76 public virtual bool OnGUI(DebugUI.Widget widget, DebugState state)
77 {
78 return true;
79 }
80
81 /// <summary>
82 /// Implement this to execute processing after UI rendering.
83 /// </summary>
84 /// <param name="widget">Widget that is going to be rendered.</param>
85 /// <param name="state">Debug State associated with the Debug Item.</param>
86 public virtual void End(DebugUI.Widget widget, DebugState state)
87 { }
88
89 /// <summary>
90 /// Applies a value to the widget and the Debug State of the Debug Item.
91 /// </summary>
92 /// <param name="widget">Debug Item widget.</param>
93 /// <param name="state">Debug State associated with the Debug Item</param>
94 /// <param name="value">Input value.</param>
95 protected void Apply(DebugUI.IValueField widget, DebugState state, object value)
96 {
97 Undo.RegisterCompleteObjectUndo(state, $"Modified Value '{state.queryPath}'");
98 state.SetValue(value, widget);
99 widget.SetValue(value);
100 EditorUtility.SetDirty(state);
101 DebugState.m_CurrentDirtyState = state;
102 UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
103 }
104
105 /// <summary>
106 /// Prepares the rendering Rect of the Drawer.
107 /// </summary>
108 /// <param name="height">Height of the rect.</param>
109 /// <param name="fullWidth">Whether to reserve full width for the element.</param>
110 /// <returns>Appropriate Rect for drawing.</returns>
111 protected Rect PrepareControlRect(float height = -1, bool fullWidth = false)
112 {
113 if (height < 0)
114 height = EditorGUIUtility.singleLineHeight;
115 var rect = GUILayoutUtility.GetRect(1f, 1f, height, height);
116
117 const float paddingLeft = 4f;
118 rect.width -= paddingLeft;
119 rect.xMin += paddingLeft;
120
121 EditorGUIUtility.labelWidth = fullWidth ? rect.width : rect.width / 2f;
122
123 return rect;
124 }
125 }
126
127 /// <summary>
128 /// Common class to help drawing fields
129 /// </summary>
130 /// <typeparam name="TValue">The internal value of the field</typeparam>
131 /// <typeparam name="TField">The type of the field widget</typeparam>
132 /// <typeparam name="TState">The state of the field</typeparam>
133 public abstract class DebugUIFieldDrawer<TValue, TField, TState> : DebugUIDrawer
134 where TField : DebugUI.Field<TValue>
135 where TState : DebugState
136 {
137 private TValue value { get; set; }
138
139 /// <summary>
140 /// Implement this to execute processing before UI rendering.
141 /// </summary>
142 /// <param name="widget">Widget that is going to be rendered.</param>
143 /// <param name="state">Debug State associated with the Debug Item.</param>
144 public override void Begin(DebugUI.Widget widget, DebugState state)
145 {
146 EditorGUI.BeginChangeCheck();
147 }
148
149 /// <summary>
150 /// Implement this to execute UI rendering.
151 /// </summary>
152 /// <param name="widget">Widget that is going to be rendered.</param>
153 /// <param name="state">Debug State associated with the Debug Item.</param>
154 /// <returns>Returns the state of the widget.</returns>
155 public override bool OnGUI(DebugUI.Widget widget, DebugState state)
156 {
157 value = DoGUI(
158 PrepareControlRect(),
159 EditorGUIUtility.TrTextContent(widget.displayName, widget.tooltip),
160 Cast<TField>(widget),
161 Cast<TState>(state)
162 );
163
164 return true;
165 }
166
167 /// <summary>
168 /// Does the field of the given type
169 /// </summary>
170 /// <param name="rect">The rect to draw the field</param>
171 /// <param name="label">The label for the field</param>
172 /// <param name="field">The field</param>
173 /// <param name="state">The state</param>
174 /// <returns>The current value from the UI</returns>
175 protected abstract TValue DoGUI(Rect rect, GUIContent label, TField field, TState state);
176
177 struct WidgetChangedAction
178 {
179 public string query_path;
180 public TValue previous_value;
181 public TValue new_value;
182 }
183
184 static List<WidgetChangedAction> s_Analytic = new List<WidgetChangedAction>();
185 /// <summary>
186 /// Implement this to execute processing after UI rendering.
187 /// </summary>
188 /// <param name="widget">Widget that is going to be rendered.</param>
189 /// <param name="state">Debug State associated with the Debug Item.</param>
190 public override void End(DebugUI.Widget widget, DebugState state)
191 {
192 if (EditorGUI.EndChangeCheck())
193 {
194 var w = Cast<TField>(widget);
195 var s = Cast<TState>(state);
196
197 s_Analytic.Clear();
198 s_Analytic.Add(new()
199 {
200 query_path = widget.queryPath,
201 previous_value = w.GetValue(),
202 new_value = value
203 });
204
205 Apply(w, s, value);
206 GraphicsToolUsageAnalytic.ActionPerformed<DebugWindow>("Widget Value Changed", s_Analytic.ToNestedColumn());
207 }
208 }
209 }
210
211 /// <summary>
212 /// Common class to help drawing widgets
213 /// </summary>
214 /// <typeparam name="TWidget">The widget</typeparam>
215 public abstract class DebugUIWidgetDrawer<TWidget> : DebugUIDrawer
216 where TWidget : DebugUI.Widget
217 {
218 /// <summary>
219 /// Implement this to execute processing before UI rendering.
220 /// </summary>
221 /// <param name="widget">Widget that is going to be rendered.</param>
222 /// <param name="state">Debug State associated with the Debug Item.</param>
223 public override void Begin(DebugUI.Widget widget, DebugState state)
224 {
225 }
226
227 /// <summary>
228 /// Implement this to execute UI rendering.
229 /// </summary>
230 /// <param name="widget">Widget that is going to be rendered.</param>
231 /// <param name="state">Debug State associated with the Debug Item.</param>
232 /// <returns>Returns the state of the widget.</returns>
233 public override bool OnGUI(DebugUI.Widget widget, DebugState state)
234 {
235 DoGUI(
236 PrepareControlRect(),
237 EditorGUIUtility.TrTextContent(widget.displayName, widget.tooltip),
238 Cast<TWidget>(widget)
239 );
240
241 return true;
242 }
243
244 /// <summary>
245 /// Does the field of the given type
246 /// </summary>
247 /// <param name="rect">The rect to draw the field</param>
248 /// <param name="label">The label for the field</param>
249 /// <param name="w">The widget</param>
250 protected abstract void DoGUI(Rect rect, GUIContent label, TWidget w);
251
252 /// <summary>
253 /// Implement this to execute processing after UI rendering.
254 /// </summary>
255 /// <param name="widget">Widget that is going to be rendered.</param>
256 /// <param name="state">Debug State associated with the Debug Item.</param>
257 public override void End(DebugUI.Widget widget, DebugState state)
258 {
259 }
260 }
261}