using System;
using System.Collections.Generic;
using System.Text;
using UnityEditor.Rendering.Analytics;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering
{
///
/// Attribute specifying wich type of Debug Item should this drawer be used with.
///
public class DebugUIDrawerAttribute : Attribute
{
internal readonly Type type;
///
/// Constructor for DebugUIDraw Attribute
///
/// Type of Debug Item this draw should be used with.
public DebugUIDrawerAttribute(Type type)
{
this.type = type;
}
}
///
/// Debug Item Drawer
///
public class DebugUIDrawer
{
///
/// Cast into the proper type.
///
/// Type of the drawer
/// Object to be cast
/// Returns o cast to type T
protected T Cast(object o)
where T : class
{
if (o == null) return null;
if (o is T casted)
return casted;
StringBuilder info = new StringBuilder("Cast Exception:");
switch (o)
{
case DebugUI.Widget value:
info.AppendLine($"Query Path : {value.queryPath}");
break;
case DebugState state:
info.AppendLine($"Query Path : {state.queryPath}");
break;
}
info.AppendLine($"Object to Cast Type : {o.GetType().AssemblyQualifiedName}");
info.AppendLine($"Target Cast Type : {typeof(T).AssemblyQualifiedName}");
throw new InvalidCastException(info.ToString());
}
///
/// Implement this to execute processing before UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
public virtual void Begin(DebugUI.Widget widget, DebugState state)
{ }
///
/// Implement this to execute UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
/// Returns the state of the widget.
public virtual bool OnGUI(DebugUI.Widget widget, DebugState state)
{
return true;
}
///
/// Implement this to execute processing after UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
public virtual void End(DebugUI.Widget widget, DebugState state)
{ }
///
/// Applies a value to the widget and the Debug State of the Debug Item.
///
/// Debug Item widget.
/// Debug State associated with the Debug Item
/// Input value.
protected void Apply(DebugUI.IValueField widget, DebugState state, object value)
{
Undo.RegisterCompleteObjectUndo(state, $"Modified Value '{state.queryPath}'");
state.SetValue(value, widget);
widget.SetValue(value);
EditorUtility.SetDirty(state);
DebugState.m_CurrentDirtyState = state;
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
}
///
/// Prepares the rendering Rect of the Drawer.
///
/// Height of the rect.
/// Whether to reserve full width for the element.
/// Appropriate Rect for drawing.
protected Rect PrepareControlRect(float height = -1, bool fullWidth = false)
{
if (height < 0)
height = EditorGUIUtility.singleLineHeight;
var rect = GUILayoutUtility.GetRect(1f, 1f, height, height);
const float paddingLeft = 4f;
rect.width -= paddingLeft;
rect.xMin += paddingLeft;
EditorGUIUtility.labelWidth = fullWidth ? rect.width : rect.width / 2f;
return rect;
}
}
///
/// Common class to help drawing fields
///
/// The internal value of the field
/// The type of the field widget
/// The state of the field
public abstract class DebugUIFieldDrawer : DebugUIDrawer
where TField : DebugUI.Field
where TState : DebugState
{
private TValue value { get; set; }
///
/// Implement this to execute processing before UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
public override void Begin(DebugUI.Widget widget, DebugState state)
{
EditorGUI.BeginChangeCheck();
}
///
/// Implement this to execute UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
/// Returns the state of the widget.
public override bool OnGUI(DebugUI.Widget widget, DebugState state)
{
value = DoGUI(
PrepareControlRect(),
EditorGUIUtility.TrTextContent(widget.displayName, widget.tooltip),
Cast(widget),
Cast(state)
);
return true;
}
///
/// Does the field of the given type
///
/// The rect to draw the field
/// The label for the field
/// The field
/// The state
/// The current value from the UI
protected abstract TValue DoGUI(Rect rect, GUIContent label, TField field, TState state);
struct WidgetChangedAction
{
public string query_path;
public TValue previous_value;
public TValue new_value;
}
static List s_Analytic = new List();
///
/// Implement this to execute processing after UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
public override void End(DebugUI.Widget widget, DebugState state)
{
if (EditorGUI.EndChangeCheck())
{
var w = Cast(widget);
var s = Cast(state);
s_Analytic.Clear();
s_Analytic.Add(new()
{
query_path = widget.queryPath,
previous_value = w.GetValue(),
new_value = value
});
Apply(w, s, value);
GraphicsToolUsageAnalytic.ActionPerformed("Widget Value Changed", s_Analytic.ToNestedColumn());
}
}
}
///
/// Common class to help drawing widgets
///
/// The widget
public abstract class DebugUIWidgetDrawer : DebugUIDrawer
where TWidget : DebugUI.Widget
{
///
/// Implement this to execute processing before UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
public override void Begin(DebugUI.Widget widget, DebugState state)
{
}
///
/// Implement this to execute UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
/// Returns the state of the widget.
public override bool OnGUI(DebugUI.Widget widget, DebugState state)
{
DoGUI(
PrepareControlRect(),
EditorGUIUtility.TrTextContent(widget.displayName, widget.tooltip),
Cast(widget)
);
return true;
}
///
/// Does the field of the given type
///
/// The rect to draw the field
/// The label for the field
/// The widget
protected abstract void DoGUI(Rect rect, GUIContent label, TWidget w);
///
/// Implement this to execute processing after UI rendering.
///
/// Widget that is going to be rendered.
/// Debug State associated with the Debug Item.
public override void End(DebugUI.Widget widget, DebugState state)
{
}
}
}