using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace UnityEngine.Rendering
{
public partial class DebugUI
{
///
/// Base class for "container" type widgets, although it can be used on its own (if a display name is set then it'll behave as a group with a header)
///
public class Container : Widget, IContainer
{
const string k_IDToken = "#";
internal bool hideDisplayName => string.IsNullOrEmpty(displayName) || displayName.StartsWith(k_IDToken);
///
/// List of children.
///
public ObservableList children { get; private set; }
///
/// Panel the container is attached to.
///
public override Panel panel
{
get { return m_Panel; }
internal set
{
/// Frequenlty used panels do now own widgets
if (value != null && value.flags.HasFlag(DebugUI.Flags.FrequentlyUsed))
return;
m_Panel = value;
// Bubble down
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
children[i].panel = value;
}
}
///
/// Constructor
///
public Container()
: this(string.Empty, new ObservableList())
{
}
///
/// Constructor for a container without header
///
/// The id of the container
public Container(string id)
: this($"{k_IDToken}{id}", new ObservableList())
{
}
///
/// Constructor.
///
/// Display name of the container.
/// List of attached children.
public Container(string displayName, ObservableList children)
{
this.displayName = displayName;
this.children = children;
children.ItemAdded += OnItemAdded;
children.ItemRemoved += OnItemRemoved;
// Call OnAdded callback for already existing items to ensure their panel & parent are set
for (int i = 0; i < this.children.Count; i++)
OnItemAdded(this.children, new ListChangedEventArgs(i, this.children[i]));
}
internal override void GenerateQueryPath()
{
base.GenerateQueryPath();
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
children[i].GenerateQueryPath();
}
///
/// Method called when a children is added.
///
/// Sender widget.
/// List of added children.
protected virtual void OnItemAdded(ObservableList sender, ListChangedEventArgs e)
{
if (e.item != null)
{
e.item.panel = m_Panel;
e.item.parent = this;
}
if (m_Panel != null)
m_Panel.SetDirty();
}
///
/// Method called when a children is removed.
///
/// Sender widget.
/// List of removed children.
protected virtual void OnItemRemoved(ObservableList sender, ListChangedEventArgs e)
{
if (e.item != null)
{
e.item.panel = null;
e.item.parent = null;
}
if (m_Panel != null)
m_Panel.SetDirty();
}
///
/// Returns the hash code of the widget.
///
/// Hash code of the widget.
public override int GetHashCode()
{
int hash = 17;
hash = hash * 23 + queryPath.GetHashCode();
hash = hash * 23 + isHidden.GetHashCode();
int numChildren = children.Count;
for (int i = 0; i < numChildren; i++)
hash = hash * 23 + children[i].GetHashCode();
return hash;
}
}
///
/// Unity-like foldout that can be collapsed.
///
public class Foldout : Container, IValueField
{
///
/// Context menu item.
///
public struct ContextMenuItem
{
///
/// Name of the item displayed in context menu dropdown.
///
public string displayName;
///
/// Callback when context menu item is selected.
///
public Action action;
}
///
/// Always false.
///
public bool isReadOnly { get { return false; } }
///
/// Opened state of the foldout.
///
public bool opened;
///
/// Draw the foldout in full width using a header style.
///
public bool isHeader;
///
/// Optional list of context menu items. If the list is not provided, no context menu button will be displayed.
///
public List contextMenuItems = null;
///
/// List of columns labels.
///
public string[] columnLabels { get; set; } = null;
///
/// List of columns label tooltips.
///
public string[] columnTooltips { get; set; } = null;
///
/// Constructor.
///
public Foldout() : base() { }
///
/// Constructor.
///
/// Display name of the foldout.
/// List of attached children.
/// Optional list of column names.
/// Optional list of tooltips for column name labels.
public Foldout(string displayName, ObservableList children, string[] columnLabels = null, string[] columnTooltips = null)
: base(displayName, children)
{
this.columnLabels = columnLabels;
this.columnTooltips = columnTooltips;
}
///
/// Get the opened state of the foldout.
///
/// True if the foldout is opened.
public bool GetValue() => opened;
///
/// Get the opened state of the foldout.
///
/// True if the foldout is opened.
object IValueField.GetValue() => GetValue();
///
/// Set the opened state of the foldout.
///
/// True to open the foldout, false to close it.
public void SetValue(object value) => SetValue((bool)value);
///
/// Validates the value of the widget before setting it.
///
/// Input value.
/// The validated value.
public object ValidateValue(object value) => value;
///
/// Set the value of the widget.
///
/// Input value.
public void SetValue(bool value) => opened = value;
}
///
/// Horizontal Layout Container.
///
public class HBox : Container
{
///
/// Constructor.
///
public HBox()
{
displayName = "HBox";
}
}
///
/// Vertical Layout Container.
///
public class VBox : Container
{
///
/// Constructor.
///
public VBox()
{
displayName = "VBox";
}
}
///
/// Array Container.
///
public class Table : Container
{
static GUIStyle columnHeaderStyle = new GUIStyle()
{
alignment = TextAnchor.MiddleCenter
};
/// Row Container.
public class Row : Foldout
{
/// Constructor.
public Row() { displayName = "Row"; }
}
///
/// True if the table is read only.
///
public bool isReadOnly = false;
/// Constructor.
public Table() { displayName = "Array"; }
///
/// Set column visibility.
///
/// Index of the column.
/// True if the column should be visible.
public void SetColumnVisibility(int index, bool visible)
{
#if UNITY_EDITOR
var header = Header;
if (index < 0 || index >= m_ColumnCount)
return;
index++;
if (header.IsColumnVisible(index) != visible)
{
var newVisibleColumns = new System.Collections.Generic.List(header.state.visibleColumns);
if (newVisibleColumns.Contains(index))
{
newVisibleColumns.Remove(index);
}
else
{
newVisibleColumns.Add(index);
newVisibleColumns.Sort();
}
header.state.visibleColumns = newVisibleColumns.ToArray();
var cols = header.state.columns;
for (int i = 0; i < cols.Length; i++)
cols[i].width = 50f;
header.ResizeToFit();
}
#else
var columns = VisibleColumns;
if (index < 0 || index > columns.Length)
return;
columns[index] = visible;
#endif
}
///
/// Get column visibility.
///
/// Index of the column.
/// True if the column is visible.
public bool GetColumnVisibility(int index)
{
#if UNITY_EDITOR
var header = Header;
if (index < 0 || index >= m_ColumnCount)
return false;
return header.IsColumnVisible(index + 1);
#else
var columns = VisibleColumns;
if (index < 0 || index > columns.Length)
return false;
return columns[index];
#endif
}
#if UNITY_EDITOR
///
/// The scroll position of the table.
///
public Vector2 scroll = Vector2.zero;
int m_ColumnCount;
UnityEditor.IMGUI.Controls.MultiColumnHeader m_Header = null;
///
/// The table header for drawing
///
public UnityEditor.IMGUI.Controls.MultiColumnHeader Header
{
get
{
if (m_Header != null)
return m_Header;
if (children.Count != 0)
{
m_ColumnCount = ((Container)children[0]).children.Count;
for (int i = 1; i < children.Count; i++)
{
if (((Container)children[i]).children.Count != m_ColumnCount)
{
Debug.LogError("All rows must have the same number of children.");
return null;
}
}
}
UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column CreateColumn(string name, string tooltip)
{
var col = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column()
{
canSort = false,
headerTextAlignment = TextAlignment.Center,
headerContent = new GUIContent(name, tooltip ?? string.Empty)
};
columnHeaderStyle.CalcMinMaxWidth(col.headerContent, out col.width, out float _);
col.width = Mathf.Min(col.width, 50f);
return col;
}
var cols = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column[m_ColumnCount + 1];
cols[0] = CreateColumn(displayName, tooltip);
cols[0].allowToggleVisibility = false;
for (int i = 0; i < m_ColumnCount; i++)
{
var elem = ((Container) children[0]).children[i];
cols[i + 1] = CreateColumn(elem.displayName, elem.tooltip);
}
var state = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState(cols);
m_Header = new UnityEditor.IMGUI.Controls.MultiColumnHeader(state) { height = 23 };
m_Header.ResizeToFit();
return m_Header;
}
}
#else
bool[] m_Header = null;
///
/// The visible columns
///
public bool[] VisibleColumns
{
get
{
if (m_Header != null)
return m_Header;
int columnCount = 0;
if (children.Count != 0)
{
columnCount = ((Container)children[0]).children.Count;
for (int i = 1; i < children.Count; i++)
{
if (((Container)children[i]).children.Count != columnCount)
{
Debug.LogError("All rows must have the same number of children.");
return null;
}
}
}
m_Header = new bool[columnCount];
for (int i = 0; i < columnCount; i++)
m_Header[i] = true;
return m_Header;
}
}
#endif
///
/// Method called when a children is added.
///
/// Sender widget.
/// List of added children.
protected override void OnItemAdded(ObservableList sender, ListChangedEventArgs e)
{
base.OnItemAdded(sender, e);
m_Header = null;
}
///
/// Method called when a children is removed.
///
/// Sender widget.
/// List of removed children.
protected override void OnItemRemoved(ObservableList sender, ListChangedEventArgs e)
{
base.OnItemRemoved(sender, e);
m_Header = null;
}
}
}
}