Keine Beschreibung
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

DebugUI.Containers.cs 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. namespace UnityEngine.Rendering
  5. {
  6. public partial class DebugUI
  7. {
  8. /// <summary>
  9. /// 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)
  10. /// </summary>
  11. public class Container : Widget, IContainer
  12. {
  13. const string k_IDToken = "#";
  14. internal bool hideDisplayName => string.IsNullOrEmpty(displayName) || displayName.StartsWith(k_IDToken);
  15. /// <summary>
  16. /// List of children.
  17. /// </summary>
  18. public ObservableList<Widget> children { get; private set; }
  19. /// <summary>
  20. /// Panel the container is attached to.
  21. /// </summary>
  22. public override Panel panel
  23. {
  24. get { return m_Panel; }
  25. internal set
  26. {
  27. /// Frequenlty used panels do now own widgets
  28. if (value != null && value.flags.HasFlag(DebugUI.Flags.FrequentlyUsed))
  29. return;
  30. m_Panel = value;
  31. // Bubble down
  32. int numChildren = children.Count;
  33. for (int i = 0; i < numChildren; i++)
  34. children[i].panel = value;
  35. }
  36. }
  37. /// <summary>
  38. /// Constructor
  39. /// </summary>
  40. public Container()
  41. : this(string.Empty, new ObservableList<Widget>())
  42. {
  43. }
  44. /// <summary>
  45. /// Constructor for a container without header
  46. /// </summary>
  47. /// <param name="id">The id of the container</param>
  48. public Container(string id)
  49. : this($"{k_IDToken}{id}", new ObservableList<Widget>())
  50. {
  51. }
  52. /// <summary>
  53. /// Constructor.
  54. /// </summary>
  55. /// <param name="displayName">Display name of the container.</param>
  56. /// <param name="children">List of attached children.</param>
  57. public Container(string displayName, ObservableList<Widget> children)
  58. {
  59. this.displayName = displayName;
  60. this.children = children;
  61. children.ItemAdded += OnItemAdded;
  62. children.ItemRemoved += OnItemRemoved;
  63. // Call OnAdded callback for already existing items to ensure their panel & parent are set
  64. for (int i = 0; i < this.children.Count; i++)
  65. OnItemAdded(this.children, new ListChangedEventArgs<Widget>(i, this.children[i]));
  66. }
  67. internal override void GenerateQueryPath()
  68. {
  69. base.GenerateQueryPath();
  70. int numChildren = children.Count;
  71. for (int i = 0; i < numChildren; i++)
  72. children[i].GenerateQueryPath();
  73. }
  74. /// <summary>
  75. /// Method called when a children is added.
  76. /// </summary>
  77. /// <param name="sender">Sender widget.</param>
  78. /// <param name="e">List of added children.</param>
  79. protected virtual void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
  80. {
  81. if (e.item != null)
  82. {
  83. e.item.panel = m_Panel;
  84. e.item.parent = this;
  85. }
  86. if (m_Panel != null)
  87. m_Panel.SetDirty();
  88. }
  89. /// <summary>
  90. /// Method called when a children is removed.
  91. /// </summary>
  92. /// <param name="sender">Sender widget.</param>
  93. /// <param name="e">List of removed children.</param>
  94. protected virtual void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
  95. {
  96. if (e.item != null)
  97. {
  98. e.item.panel = null;
  99. e.item.parent = null;
  100. }
  101. if (m_Panel != null)
  102. m_Panel.SetDirty();
  103. }
  104. /// <summary>
  105. /// Returns the hash code of the widget.
  106. /// </summary>
  107. /// <returns>Hash code of the widget.</returns>
  108. public override int GetHashCode()
  109. {
  110. int hash = 17;
  111. hash = hash * 23 + queryPath.GetHashCode();
  112. hash = hash * 23 + isHidden.GetHashCode();
  113. int numChildren = children.Count;
  114. for (int i = 0; i < numChildren; i++)
  115. hash = hash * 23 + children[i].GetHashCode();
  116. return hash;
  117. }
  118. }
  119. /// <summary>
  120. /// Unity-like foldout that can be collapsed.
  121. /// </summary>
  122. public class Foldout : Container, IValueField
  123. {
  124. /// <summary>
  125. /// Context menu item.
  126. /// </summary>
  127. public struct ContextMenuItem
  128. {
  129. /// <summary>
  130. /// Name of the item displayed in context menu dropdown.
  131. /// </summary>
  132. public string displayName;
  133. /// <summary>
  134. /// Callback when context menu item is selected.
  135. /// </summary>
  136. public Action action;
  137. }
  138. /// <summary>
  139. /// Always false.
  140. /// </summary>
  141. public bool isReadOnly { get { return false; } }
  142. /// <summary>
  143. /// Opened state of the foldout.
  144. /// </summary>
  145. public bool opened;
  146. /// <summary>
  147. /// Draw the foldout in full width using a header style.
  148. /// </summary>
  149. public bool isHeader;
  150. /// <summary>
  151. /// Optional list of context menu items. If the list is not provided, no context menu button will be displayed.
  152. /// </summary>
  153. public List<ContextMenuItem> contextMenuItems = null;
  154. /// <summary>
  155. /// List of columns labels.
  156. /// </summary>
  157. public string[] columnLabels { get; set; } = null;
  158. /// <summary>
  159. /// List of columns label tooltips.
  160. /// </summary>
  161. public string[] columnTooltips { get; set; } = null;
  162. /// <summary>
  163. /// Constructor.
  164. /// </summary>
  165. public Foldout() : base() { }
  166. /// <summary>
  167. /// Constructor.
  168. /// </summary>
  169. /// <param name="displayName">Display name of the foldout.</param>
  170. /// <param name="children">List of attached children.</param>
  171. /// <param name="columnLabels">Optional list of column names.</param>
  172. /// <param name="columnTooltips">Optional list of tooltips for column name labels.</param>
  173. public Foldout(string displayName, ObservableList<Widget> children, string[] columnLabels = null, string[] columnTooltips = null)
  174. : base(displayName, children)
  175. {
  176. this.columnLabels = columnLabels;
  177. this.columnTooltips = columnTooltips;
  178. }
  179. /// <summary>
  180. /// Get the opened state of the foldout.
  181. /// </summary>
  182. /// <returns>True if the foldout is opened.</returns>
  183. public bool GetValue() => opened;
  184. /// <summary>
  185. /// Get the opened state of the foldout.
  186. /// </summary>
  187. /// <returns>True if the foldout is opened.</returns>
  188. object IValueField.GetValue() => GetValue();
  189. /// <summary>
  190. /// Set the opened state of the foldout.
  191. /// </summary>
  192. /// <param name="value">True to open the foldout, false to close it.</param>
  193. public void SetValue(object value) => SetValue((bool)value);
  194. /// <summary>
  195. /// Validates the value of the widget before setting it.
  196. /// </summary>
  197. /// <param name="value">Input value.</param>
  198. /// <returns>The validated value.</returns>
  199. public object ValidateValue(object value) => value;
  200. /// <summary>
  201. /// Set the value of the widget.
  202. /// </summary>
  203. /// <param name="value">Input value.</param>
  204. public void SetValue(bool value) => opened = value;
  205. }
  206. /// <summary>
  207. /// Horizontal Layout Container.
  208. /// </summary>
  209. public class HBox : Container
  210. {
  211. /// <summary>
  212. /// Constructor.
  213. /// </summary>
  214. public HBox()
  215. {
  216. displayName = "HBox";
  217. }
  218. }
  219. /// <summary>
  220. /// Vertical Layout Container.
  221. /// </summary>
  222. public class VBox : Container
  223. {
  224. /// <summary>
  225. /// Constructor.
  226. /// </summary>
  227. public VBox()
  228. {
  229. displayName = "VBox";
  230. }
  231. }
  232. /// <summary>
  233. /// Array Container.
  234. /// </summary>
  235. public class Table : Container
  236. {
  237. static GUIStyle columnHeaderStyle = new GUIStyle()
  238. {
  239. alignment = TextAnchor.MiddleCenter
  240. };
  241. /// <summary>Row Container.</summary>
  242. public class Row : Foldout
  243. {
  244. /// <summary>Constructor.</summary>
  245. public Row() { displayName = "Row"; }
  246. }
  247. /// <summary>
  248. /// True if the table is read only.
  249. /// </summary>
  250. public bool isReadOnly = false;
  251. /// <summary>Constructor.</summary>
  252. public Table() { displayName = "Array"; }
  253. /// <summary>
  254. /// Set column visibility.
  255. /// </summary>
  256. /// <param name="index">Index of the column.</param>
  257. /// <param name="visible">True if the column should be visible.</param>
  258. public void SetColumnVisibility(int index, bool visible)
  259. {
  260. #if UNITY_EDITOR
  261. var header = Header;
  262. if (index < 0 || index >= m_ColumnCount)
  263. return;
  264. index++;
  265. if (header.IsColumnVisible(index) != visible)
  266. {
  267. var newVisibleColumns = new System.Collections.Generic.List<int>(header.state.visibleColumns);
  268. if (newVisibleColumns.Contains(index))
  269. {
  270. newVisibleColumns.Remove(index);
  271. }
  272. else
  273. {
  274. newVisibleColumns.Add(index);
  275. newVisibleColumns.Sort();
  276. }
  277. header.state.visibleColumns = newVisibleColumns.ToArray();
  278. var cols = header.state.columns;
  279. for (int i = 0; i < cols.Length; i++)
  280. cols[i].width = 50f;
  281. header.ResizeToFit();
  282. }
  283. #else
  284. var columns = VisibleColumns;
  285. if (index < 0 || index > columns.Length)
  286. return;
  287. columns[index] = visible;
  288. #endif
  289. }
  290. /// <summary>
  291. /// Get column visibility.
  292. /// </summary>
  293. /// <param name="index">Index of the column.</param>
  294. /// <returns>True if the column is visible.</returns>
  295. public bool GetColumnVisibility(int index)
  296. {
  297. #if UNITY_EDITOR
  298. var header = Header;
  299. if (index < 0 || index >= m_ColumnCount)
  300. return false;
  301. return header.IsColumnVisible(index + 1);
  302. #else
  303. var columns = VisibleColumns;
  304. if (index < 0 || index > columns.Length)
  305. return false;
  306. return columns[index];
  307. #endif
  308. }
  309. #if UNITY_EDITOR
  310. /// <summary>
  311. /// The scroll position of the table.
  312. /// </summary>
  313. public Vector2 scroll = Vector2.zero;
  314. int m_ColumnCount;
  315. UnityEditor.IMGUI.Controls.MultiColumnHeader m_Header = null;
  316. /// <summary>
  317. /// The table header for drawing
  318. /// </summary>
  319. public UnityEditor.IMGUI.Controls.MultiColumnHeader Header
  320. {
  321. get
  322. {
  323. if (m_Header != null)
  324. return m_Header;
  325. if (children.Count != 0)
  326. {
  327. m_ColumnCount = ((Container)children[0]).children.Count;
  328. for (int i = 1; i < children.Count; i++)
  329. {
  330. if (((Container)children[i]).children.Count != m_ColumnCount)
  331. {
  332. Debug.LogError("All rows must have the same number of children.");
  333. return null;
  334. }
  335. }
  336. }
  337. UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column CreateColumn(string name, string tooltip)
  338. {
  339. var col = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column()
  340. {
  341. canSort = false,
  342. headerTextAlignment = TextAlignment.Center,
  343. headerContent = new GUIContent(name, tooltip ?? string.Empty)
  344. };
  345. columnHeaderStyle.CalcMinMaxWidth(col.headerContent, out col.width, out float _);
  346. col.width = Mathf.Min(col.width, 50f);
  347. return col;
  348. }
  349. var cols = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState.Column[m_ColumnCount + 1];
  350. cols[0] = CreateColumn(displayName, tooltip);
  351. cols[0].allowToggleVisibility = false;
  352. for (int i = 0; i < m_ColumnCount; i++)
  353. {
  354. var elem = ((Container) children[0]).children[i];
  355. cols[i + 1] = CreateColumn(elem.displayName, elem.tooltip);
  356. }
  357. var state = new UnityEditor.IMGUI.Controls.MultiColumnHeaderState(cols);
  358. m_Header = new UnityEditor.IMGUI.Controls.MultiColumnHeader(state) { height = 23 };
  359. m_Header.ResizeToFit();
  360. return m_Header;
  361. }
  362. }
  363. #else
  364. bool[] m_Header = null;
  365. /// <summary>
  366. /// The visible columns
  367. /// </summary>
  368. public bool[] VisibleColumns
  369. {
  370. get
  371. {
  372. if (m_Header != null)
  373. return m_Header;
  374. int columnCount = 0;
  375. if (children.Count != 0)
  376. {
  377. columnCount = ((Container)children[0]).children.Count;
  378. for (int i = 1; i < children.Count; i++)
  379. {
  380. if (((Container)children[i]).children.Count != columnCount)
  381. {
  382. Debug.LogError("All rows must have the same number of children.");
  383. return null;
  384. }
  385. }
  386. }
  387. m_Header = new bool[columnCount];
  388. for (int i = 0; i < columnCount; i++)
  389. m_Header[i] = true;
  390. return m_Header;
  391. }
  392. }
  393. #endif
  394. /// <summary>
  395. /// Method called when a children is added.
  396. /// </summary>
  397. /// <param name="sender">Sender widget.</param>
  398. /// <param name="e">List of added children.</param>
  399. protected override void OnItemAdded(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
  400. {
  401. base.OnItemAdded(sender, e);
  402. m_Header = null;
  403. }
  404. /// <summary>
  405. /// Method called when a children is removed.
  406. /// </summary>
  407. /// <param name="sender">Sender widget.</param>
  408. /// <param name="e">List of removed children.</param>
  409. protected override void OnItemRemoved(ObservableList<Widget> sender, ListChangedEventArgs<Widget> e)
  410. {
  411. base.OnItemRemoved(sender, e);
  412. m_Header = null;
  413. }
  414. }
  415. }
  416. }