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; } } }