using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
public partial class DebugUI
{
///
/// Generic field - will be serialized in the editor if it's not read-only
///
///
public abstract class Field : Widget, IValueField
{
///
/// Getter for this field.
///
public Func getter { get; set; }
///
/// Setter for this field.
///
public Action setter { get; set; }
// This should be an `event` but they don't play nice with object initializers in the
// version of C# we use.
///
/// Callback used when the value of the field changes.
///
public Action, T> onValueChanged;
///
/// Function used to validate the value when updating the field.
///
/// Input value.
/// Validated value.
object IValueField.ValidateValue(object value)
{
return ValidateValue((T)value);
}
///
/// Function used to validate the value when updating the field.
///
/// Input value.
/// Validated value.
public virtual T ValidateValue(T value)
{
return value;
}
///
/// Get the value of the field.
///
/// Value of the field.
object IValueField.GetValue()
{
return GetValue();
}
///
/// Get the value of the field.
///
/// Value of the field.
public T GetValue()
{
Assert.IsNotNull(getter);
return getter();
}
///
/// Set the value of the field.
///
/// Input value.
public void SetValue(object value)
{
SetValue((T)value);
}
///
/// Set the value of the field.
///
/// Input value.
public void SetValue(T value)
{
Assert.IsNotNull(setter);
var v = ValidateValue(value);
if (!v.Equals(getter()))
{
setter(v);
onValueChanged?.Invoke(this, v);
}
}
}
///
/// Boolean field.
///
public class BoolField : Field { }
///
/// Boolean field with history.
///
public class HistoryBoolField : BoolField
{
///
/// History getter for this field.
///
public Func[] historyGetter { get; set; }
///
/// Depth of the field's history.
///
public int historyDepth => historyGetter?.Length ?? 0;
///
/// Get the value of the field at a certain history index.
///
/// Index of the history to query.
/// Value of the field at the provided history index.
public bool GetHistoryValue(int historyIndex)
{
Assert.IsNotNull(historyGetter);
Assert.IsTrue(historyIndex >= 0 && historyIndex < historyGetter.Length, "out of range historyIndex");
Assert.IsNotNull(historyGetter[historyIndex]);
return historyGetter[historyIndex]();
}
}
///
/// Integer field.
///
public class IntField : Field
{
///
/// Minimum value function.
///
public Func min;
///
/// Maximum value function.
///
public Func max;
// Runtime-only
///
/// Step increment.
///
public int incStep = 1;
///
/// Step increment multiplier.
///
public int intStepMult = 10;
///
/// Function used to validate the value when updating the field.
///
/// Input value.
/// Validated value.
public override int ValidateValue(int value)
{
if (min != null) value = Mathf.Max(value, min());
if (max != null) value = Mathf.Min(value, max());
return value;
}
}
///
/// Unsigned integer field.
///
public class UIntField : Field
{
///
/// Minimum value function.
///
public Func min;
///
/// Maximum value function.
///
public Func max;
// Runtime-only
///
/// Step increment.
///
public uint incStep = 1u;
///
/// Step increment multiplier.
///
public uint intStepMult = 10u;
///
/// Function used to validate the value when updating the field.
///
/// Input value.
/// Validated value.
public override uint ValidateValue(uint value)
{
if (min != null) value = (uint)Mathf.Max((int)value, (int)min());
if (max != null) value = (uint)Mathf.Min((int)value, (int)max());
return value;
}
}
///
/// Float field.
///
public class FloatField : Field
{
///
/// Minimum value function.
///
public Func min;
///
/// Maximum value function.
///
public Func max;
// Runtime-only
///
/// Step increment.
///
public float incStep = 0.1f;
///
/// Step increment multiplier.
///
public float incStepMult = 10f;
///
/// Number of decimals.
///
public int decimals = 3;
///
/// Function used to validate the value when updating the field.
///
/// Input value.
/// Validated value.
public override float ValidateValue(float value)
{
if (min != null) value = Mathf.Max(value, min());
if (max != null) value = Mathf.Min(value, max());
return value;
}
}
static class EnumUtility
{
internal static GUIContent[] MakeEnumNames(Type enumType)
{
return enumType.GetFields(BindingFlags.Public | BindingFlags.Static).Select(fieldInfo =>
{
var description = fieldInfo.GetCustomAttributes(typeof(InspectorNameAttribute), false);
if (description.Length > 0)
{
return new GUIContent(((InspectorNameAttribute)description.First()).displayName);
}
// Space-delimit PascalCase (https://stackoverflow.com/questions/155303/net-how-can-you-split-a-caps-delimited-string-into-an-array)
var niceName = Regex.Replace(fieldInfo.Name, "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 ");
return new GUIContent(niceName);
}).ToArray();
}
internal static int[] MakeEnumValues(Type enumType)
{
// Linq.Cast on a typeless Array breaks the JIT on PS4/Mono so we have to do it manually
//enumValues = Enum.GetValues(value).Cast().ToArray();
var values = Enum.GetValues(enumType);
var enumValues = new int[values.Length];
for (int i = 0; i < values.Length; i++)
enumValues[i] = (int)values.GetValue(i);
return enumValues;
}
}
///
/// Enumerator field.
///
public class EnumField : Field
{
///
/// List of names of the enumerator entries.
///
public GUIContent[] enumNames;
///
/// List of values of the enumerator entries.
///
public int[] enumValues;
internal int[] quickSeparators;
internal int[] indexes;
///
/// Get the enumeration value index.
///
public Func getIndex { get; set; }
///
/// Set the enumeration value index.
///
public Action setIndex { get; set; }
///
/// Current enumeration value index.
///
public int currentIndex { get => getIndex(); set => setIndex(value); }
///
/// Generates enumerator values and names automatically based on the provided type.
///
public Type autoEnum
{
set
{
enumNames = EnumUtility.MakeEnumNames(value);
enumValues = EnumUtility.MakeEnumValues(value);
InitIndexes();
InitQuickSeparators();
}
}
internal void InitQuickSeparators()
{
var enumNamesPrefix = enumNames.Select(x =>
{
string[] splitted = x.text.Split('/');
if (splitted.Length == 1)
return "";
else
return splitted[0];
});
quickSeparators = new int[enumNamesPrefix.Distinct().Count()];
string lastPrefix = null;
for (int i = 0, wholeNameIndex = 0; i < quickSeparators.Length; ++i)
{
var currentTestedPrefix = enumNamesPrefix.ElementAt(wholeNameIndex);
while (lastPrefix == currentTestedPrefix)
{
currentTestedPrefix = enumNamesPrefix.ElementAt(++wholeNameIndex);
}
lastPrefix = currentTestedPrefix;
quickSeparators[i] = wholeNameIndex++;
}
}
internal void InitIndexes()
{
if (enumNames == null)
enumNames = new GUIContent[0];
indexes = new int[enumNames.Length];
for (int i = 0; i < enumNames.Length; i++)
{
indexes[i] = i;
}
}
}
///
/// Enumerator field with history.
///
public class HistoryEnumField : EnumField
{
///
/// History getter for this field.
///
public Func[] historyIndexGetter { get; set; }
///
/// Depth of the field's history.
///
public int historyDepth => historyIndexGetter?.Length ?? 0;
///
/// Get the value of the field at a certain history index.
///
/// Index of the history to query.
/// Value of the field at the provided history index.
public int GetHistoryValue(int historyIndex)
{
Assert.IsNotNull(historyIndexGetter);
Assert.IsTrue(historyIndex >= 0 && historyIndex < historyIndexGetter.Length, "out of range historyIndex");
Assert.IsNotNull(historyIndexGetter[historyIndex]);
return historyIndexGetter[historyIndex]();
}
}
///
/// Bitfield enumeration field.
///
public class BitField : Field
{
///
/// List of names of the enumerator entries.
///
public GUIContent[] enumNames { get; private set; }
///
/// List of values of the enumerator entries.
///
public int[] enumValues { get; private set; }
Type m_EnumType;
///
/// Generates bitfield values and names automatically based on the provided type.
///
public Type enumType
{
get => m_EnumType;
set
{
m_EnumType = value;
enumNames = EnumUtility.MakeEnumNames(value);
enumValues = EnumUtility.MakeEnumValues(value);
}
}
}
///
/// Color field.
///
public class ColorField : Field
{
///
/// HDR color.
///
public bool hdr = false;
///
/// Show alpha of the color field.
///
public bool showAlpha = true;
// Editor-only
///
/// Show the color picker.
///
public bool showPicker = true;
// Runtime-only
///
/// Step increment.
///
public float incStep = 0.025f;
///
/// Step increment multiplier.
///
public float incStepMult = 5f;
///
/// Number of decimals.
///
public int decimals = 3;
///
/// Function used to validate the value when updating the field.
///
/// Input value.
/// Validated value.
public override Color ValidateValue(Color value)
{
if (!hdr)
{
value.r = Mathf.Clamp01(value.r);
value.g = Mathf.Clamp01(value.g);
value.b = Mathf.Clamp01(value.b);
value.a = Mathf.Clamp01(value.a);
}
return value;
}
}
///
/// Vector2 field.
///
public class Vector2Field : Field
{
// Runtime-only
///
/// Step increment.
///
public float incStep = 0.025f;
///
/// Step increment multiplier.
///
public float incStepMult = 10f;
///
/// Number of decimals.
///
public int decimals = 3;
}
///
/// Vector3 field.
///
public class Vector3Field : Field
{
// Runtime-only
///
/// Step increment.
///
public float incStep = 0.025f;
///
/// Step increment multiplier.
///
public float incStepMult = 10f;
///
/// Number of decimals.
///
public int decimals = 3;
}
///
/// Vector4 field.
///
public class Vector4Field : Field
{
// Runtime-only
///
/// Step increment.
///
public float incStep = 0.025f;
///
/// Step increment multiplier.
///
public float incStepMult = 10f;
///
/// Number of decimals.
///
public int decimals = 3;
}
///
/// Simple message box widget, providing a couple of different styles.
///
public class MessageBox : Widget
{
///
/// Label style defines text color and background.
///
public enum Style
{
///
/// Info
///
Info,
///
/// Warning
///
Warning,
///
/// Error
///
Error
}
///
/// Style used to render displayName.
///
public Style style = Style.Info;
}
}
}