123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
-
- namespace UnityEngine.Rendering
- {
- public partial class DebugUI
- {
- /// <summary>
- /// 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)
- /// </summary>
- public class Container : Widget, IContainer
- {
- const string k_IDToken = "#";
- internal bool hideDisplayName => string.IsNullOrEmpty(displayName) || displayName.StartsWith(k_IDToken);
-
- /// <summary>
- /// List of children.
- /// </summary>
- public ObservableList<Widget> children { get; private set; }
-
- /// <summary>
- /// Panel the container is attached to.
- /// </summary>
- 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;
- }
- }
-
- /// <summary>
- /// Constructor
- /// </summary>
- public Container()
- : this(string.Empty, new ObservableList<Widget>())
- {
- }
-
- /// <summary>
- /// Constructor for a container without header
- /// </summary>
- /// <param name="id">The id of the container</param>
- public Container(string id)
- : this($"{k_IDToken}{id}", new ObservableList<Widget>())
- {
- }
-
- /// <summary>
- /// Constructor.
- /// </summary>
- /// <param name="displayName">Display name of the container.</param>
- /// <param name="children">List of attached children.</param>
- public Container(string displayName, ObservableList<Widget> 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<Widget>(i, this.children[i]));
- }
-
- internal override void GenerateQueryPath()
- {
- base.GenerateQueryPath();
-
- int numChildren = children.Count;
- for (int i = 0; i < numChildren; i++)
- children[i].GenerateQueryPath();
- }
-
- /// <summary>
- /// Method called when a children is added.
- /// </summary>
- /// <param name="sender">Sender widget.</param>
- /// <param name="e">List of added children.</param>
- protected virtual void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
- {
- if (e.item != null)
- {
- e.item.panel = m_Panel;
- e.item.parent = this;
- }
-
- if (m_Panel != null)
- m_Panel.SetDirty();
- }
-
- /// <summary>
- /// Method called when a children is removed.
- /// </summary>
- /// <param name="sender">Sender widget.</param>
- /// <param name="e">List of removed children.</param>
- protected virtual void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
- {
- if (e.item != null)
- {
- e.item.panel = null;
- e.item.parent = null;
- }
-
- if (m_Panel != null)
- m_Panel.SetDirty();
- }
-
- /// <summary>
- /// Returns the hash code of the widget.
- /// </summary>
- /// <returns>Hash code of the widget.</returns>
- 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;
- }
- }
-
- /// <summary>
- /// Unity-like foldout that can be collapsed.
- /// </summary>
- public class Foldout : Container, IValueField
- {
- /// <summary>
- /// Context menu item.
- /// </summary>
- public struct ContextMenuItem
- {
- /// <summary>
- /// Name of the item displayed in context menu dropdown.
- /// </summary>
- public string displayName;
-
- /// <summary>
- /// Callback when context menu item is selected.
- /// </summary>
- public Action action;
- }
-
- /// <summary>
- /// Always false.
- /// </summary>
- public bool isReadOnly { get { return false; } }
-
- /// <summary>
- /// Opened state of the foldout.
- /// </summary>
- public bool opened;
-
- /// <summary>
- /// Draw the foldout in full width using a header style.
- /// </summary>
- public bool isHeader;
-
- /// <summary>
- /// Optional list of context menu items. If the list is not provided, no context menu button will be displayed.
- /// </summary>
- public List<ContextMenuItem> contextMenuItems = null;
-
- /// <summary>
- /// List of columns labels.
- /// </summary>
- public string[] columnLabels { get; set; } = null;
-
- /// <summary>
- /// List of columns label tooltips.
- /// </summary>
- public string[] columnTooltips { get; set; } = null;
-
- /// <summary>
- /// Constructor.
- /// </summary>
- public Foldout() : base() { }
- /// <summary>
- /// Constructor.
- /// </summary>
- /// <param name="displayName">Display name of the foldout.</param>
- /// <param name="children">List of attached children.</param>
- /// <param name="columnLabels">Optional list of column names.</param>
- /// <param name="columnTooltips">Optional list of tooltips for column name labels.</param>
- public Foldout(string displayName, ObservableList<Widget> children, string[] columnLabels = null, string[] columnTooltips = null)
- : base(displayName, children)
- {
- this.columnLabels = columnLabels;
- this.columnTooltips = columnTooltips;
- }
-
- /// <summary>
- /// Get the opened state of the foldout.
- /// </summary>
- /// <returns>True if the foldout is opened.</returns>
- public bool GetValue() => opened;
-
- /// <summary>
- /// Get the opened state of the foldout.
- /// </summary>
- /// <returns>True if the foldout is opened.</returns>
- object IValueField.GetValue() => GetValue();
-
- /// <summary>
- /// Set the opened state of the foldout.
- /// </summary>
- /// <param name="value">True to open the foldout, false to close it.</param>
- public void SetValue(object value) => SetValue((bool)value);
-
- /// <summary>
- /// Validates the value of the widget before setting it.
- /// </summary>
- /// <param name="value">Input value.</param>
- /// <returns>The validated value.</returns>
- public object ValidateValue(object value) => value;
-
- /// <summary>
- /// Set the value of the widget.
- /// </summary>
- /// <param name="value">Input value.</param>
- public void SetValue(bool value) => opened = value;
- }
-
- /// <summary>
- /// Horizontal Layout Container.
- /// </summary>
- public class HBox : Container
- {
- /// <summary>
- /// Constructor.
- /// </summary>
- public HBox()
- {
- displayName = "HBox";
- }
- }
-
- /// <summary>
- /// Vertical Layout Container.
- /// </summary>
- public class VBox : Container
- {
- /// <summary>
- /// Constructor.
- /// </summary>
- public VBox()
- {
- displayName = "VBox";
- }
- }
-
- /// <summary>
- /// Array Container.
- /// </summary>
- public class Table : Container
- {
- static GUIStyle columnHeaderStyle = new GUIStyle()
- {
- alignment = TextAnchor.MiddleCenter
- };
-
- /// <summary>Row Container.</summary>
- public class Row : Foldout
- {
- /// <summary>Constructor.</summary>
- public Row() { displayName = "Row"; }
- }
-
- /// <summary>
- /// True if the table is read only.
- /// </summary>
- public bool isReadOnly = false;
-
- /// <summary>Constructor.</summary>
- public Table() { displayName = "Array"; }
-
- /// <summary>
- /// Set column visibility.
- /// </summary>
- /// <param name="index">Index of the column.</param>
- /// <param name="visible">True if the column should be visible.</param>
- 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<int>(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
- }
-
- /// <summary>
- /// Get column visibility.
- /// </summary>
- /// <param name="index">Index of the column.</param>
- /// <returns>True if the column is visible.</returns>
- 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
- /// <summary>
- /// The scroll position of the table.
- /// </summary>
- public Vector2 scroll = Vector2.zero;
-
- int m_ColumnCount;
- UnityEditor.IMGUI.Controls.MultiColumnHeader m_Header = null;
-
- /// <summary>
- /// The table header for drawing
- /// </summary>
- 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;
-
- /// <summary>
- /// The visible columns
- /// </summary>
- 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
-
- /// <summary>
- /// Method called when a children is added.
- /// </summary>
- /// <param name="sender">Sender widget.</param>
- /// <param name="e">List of added children.</param>
- protected override void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
- {
- base.OnItemAdded(sender, e);
- m_Header = null;
- }
-
- /// <summary>
- /// Method called when a children is removed.
- /// </summary>
- /// <param name="sender">Sender widget.</param>
- /// <param name="e">List of removed children.</param>
- protected override void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
- {
- base.OnItemRemoved(sender, e);
- m_Header = null;
- }
- }
- }
- }
|