using System; using System.Linq; using UnityEngine.Assertions; namespace UnityEngine.Rendering { /// /// Debug UI Class /// public partial class DebugUI { /// /// Flags for Debug UI widgets. /// [Flags] public enum Flags { /// /// None. /// None = 0, /// /// This widget is Editor only. /// EditorOnly = 1 << 1, /// /// This widget is Runtime only. /// RuntimeOnly = 1 << 2, /// /// This widget will force the Debug Editor Window refresh. /// EditorForceUpdate = 1 << 3, /// /// This widget will appear in the section "Frequently Used" /// FrequentlyUsed = 1 << 4 } /// /// Base class for all debug UI widgets. /// public abstract class Widget { // Set to null until it's added to a panel, be careful /// /// Panels containing the widget. /// protected Panel m_Panel; /// /// Panels containing the widget. /// public virtual Panel panel { get { return m_Panel; } internal set { m_Panel = value; } } /// /// Parent container. /// protected IContainer m_Parent; /// /// Parent container. /// public virtual IContainer parent { get { return m_Parent; } internal set { m_Parent = value; } } /// /// Flags for the widget. /// public Flags flags { get; set; } /// /// Display name. /// public string displayName { get; set; } /// /// Tooltip. /// public string tooltip { get; set; } /// /// Path of the widget. /// public string queryPath { get; private set; } /// /// True if the widget is Editor only. /// public bool isEditorOnly => flags.HasFlag(Flags.EditorOnly); /// /// True if the widget is Runtime only. /// public bool isRuntimeOnly => flags.HasFlag(Flags.RuntimeOnly); /// /// True if the widget is inactive in the editor (i.e. widget is runtime only and the application is not 'Playing'). /// public bool isInactiveInEditor => (isRuntimeOnly && !Application.isPlaying); /// /// Optional delegate that can be used to conditionally hide widgets at runtime (e.g. due to state of other widgets). /// public Func isHiddenCallback; /// /// If shouldHideDelegate has been set and returns true, the widget is hidden from the UI. /// public bool isHidden => isHiddenCallback?.Invoke() ?? false; internal virtual void GenerateQueryPath() { queryPath = displayName.Trim(); if (m_Parent != null) queryPath = m_Parent.queryPath + " -> " + queryPath; } /// /// Returns the hash code of the widget. /// /// The hash code of the widget. public override int GetHashCode() { return queryPath.GetHashCode() ^ isHidden.GetHashCode(); } /// /// Helper struct to allow more compact initialization of widgets. /// public struct NameAndTooltip { /// /// The name /// public string name; /// /// The tooltip /// public string tooltip; } /// /// Helper setter to allow more compact initialization of widgets. /// public NameAndTooltip nameAndTooltip { set { displayName = value.name; tooltip = value.tooltip; } } } /// /// Interface for widgets that can contain other widgets. /// public interface IContainer { /// /// List of children of the container. /// ObservableList children { get; } /// /// Display name of the container. /// string displayName { get; set; } /// /// Path of the container. /// string queryPath { get; } } /// /// Any widget that implements this will be considered for serialization (only if the setter is set and thus is not read-only) /// public interface IValueField { /// /// Return the value of the field. /// /// Value of the field. object GetValue(); /// /// Set the value of the field. /// /// Input value. void SetValue(object value); /// /// Function used to validate the value when setting it. /// /// /// object ValidateValue(object value); } // Miscellaneous /// /// Button widget. /// public class Button : Widget { /// /// Action performed by the button. /// public Action action { get; set; } } /// /// Read only Value widget. /// public class Value : Widget { /// /// Getter for the Value. /// public Func getter { get; set; } /// /// Refresh rate for the read-only value (runtime only) /// public float refreshRate = 0.1f; /// /// Optional C# numeric format string, using following syntax: "{0[:numericFormatString]}" /// See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings /// and https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting /// Example: 123.45678 with formatString "{0:F2} ms" --> "123.45 ms". /// public string formatString = null; /// /// Constructor. /// public Value() { displayName = ""; } /// /// Returns the value of the widget. /// /// The value of the widget. public virtual object GetValue() { Assert.IsNotNull(getter); return getter(); } /// /// Returns the formatted value string for display purposes. /// /// Value to be formatted. /// The formatted value string. public virtual string FormatString(object value) { return string.IsNullOrEmpty(formatString) ? $"{value}" : string.Format(formatString, value); } } /// /// Progress bar value. /// public class ProgressBarValue : Value { /// /// Minimum value. /// public float min = 0f; /// /// Maximum value. /// public float max = 1f; /// /// Get the current progress string, remapped to [0, 1] range, representing the progress between min and max. /// /// Value to be formatted. /// Formatted progress percentage string between 0% and 100%. public override string FormatString(object value) { static float Remap01(float v, float x0, float y0) => (v - x0) / (y0 - x0); float clamped = Mathf.Clamp((float)value, min, max); float percentage = Remap01(clamped, min, max); return $"{percentage:P1}"; } } /// /// Tuple of Value widgets for creating tabular UI. /// public class ValueTuple : Widget { /// /// Number of elements in the tuple. /// public int numElements { get { Assert.IsTrue(values.Length > 0); return values.Length; } } /// /// Value widgets. /// public Value[] values; /// /// Refresh rate for the read-only values (runtime only) /// public float refreshRate => values.FirstOrDefault()?.refreshRate ?? 0.1f; /// /// The currently pinned element index, or -1 if none are pinned. /// public int pinnedElementIndex = -1; } } }