GUIListView.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using System.Collections.Generic;
  2. namespace BansheeEngine
  3. {
  4. // TODO - Doc
  5. public class GUIListView<TEntry, TData>
  6. where TEntry : GUIListViewEntry<TData>, new()
  7. where TData : GUIListViewData
  8. {
  9. // TODO - Only fixed size is supported. It should be nice if this object could just be placed in layout like any
  10. // other GUI element. Would likely need some kind of a way to get notified when parent layout changes.
  11. // (Possibly add a callback to GUIPanel when updateLayout is called?)
  12. private List<TEntry> visibleEntries = new List<TEntry>();
  13. private List<TData> entries = new List<TData>();
  14. private GUIScrollArea scrollArea;
  15. private GUIPanel topPadding;
  16. private GUIPanel bottomPadding;
  17. private int width;
  18. private int height;
  19. private int entryHeight;
  20. private float scrollPct = 0.0f;
  21. private bool contentsDirty = true;
  22. public int NumEntries
  23. {
  24. get { return entries.Count; }
  25. }
  26. public int EntryHeight
  27. {
  28. get { return entryHeight; }
  29. set { entryHeight = value; }
  30. }
  31. public GUIListView(int width, int height, int entryHeight, GUILayout layout)
  32. {
  33. scrollArea = new GUIScrollArea(GUIOption.FixedWidth(width), GUIOption.FixedHeight(height));
  34. layout.AddElement(scrollArea);
  35. topPadding = scrollArea.Layout.AddPanel();
  36. bottomPadding = scrollArea.Layout.AddPanel();
  37. this.width = width;
  38. this.height = height;
  39. this.entryHeight = entryHeight;
  40. }
  41. public void AddEntry(TData data)
  42. {
  43. entries.Add(data);
  44. contentsDirty = true;
  45. }
  46. public void RemoveEntry(int index)
  47. {
  48. if (index >= 0 && index < entries.Count)
  49. {
  50. entries.RemoveAt(index);
  51. contentsDirty = true;
  52. }
  53. }
  54. public void Clear()
  55. {
  56. entries.Clear();
  57. contentsDirty = true;
  58. }
  59. public int FindEntry(TData data)
  60. {
  61. return entries.FindIndex(x => x == data);
  62. }
  63. public void InsertEntry(int index, TData data)
  64. {
  65. if (index >= 0 && index <= entries.Count)
  66. entries.Insert(index, data);
  67. else
  68. entries.Add(data);
  69. contentsDirty = true;
  70. }
  71. public void SetSize(int width, int height)
  72. {
  73. if (width != this.width || height != this.height)
  74. {
  75. this.width = width;
  76. this.height = height;
  77. Rect2I bounds = scrollArea.Bounds;
  78. bounds.width = width;
  79. bounds.height = height;
  80. scrollArea.Bounds = bounds;
  81. }
  82. }
  83. public void Update()
  84. {
  85. int numVisibleEntries = MathEx.CeilToInt(height / (float)entryHeight) + 1;
  86. numVisibleEntries = MathEx.Max(numVisibleEntries, entries.Count);
  87. while (visibleEntries.Count < numVisibleEntries)
  88. {
  89. TEntry newEntry = new TEntry();
  90. newEntry.Initialize(scrollArea);
  91. newEntry.panel.SetHeight(entryHeight);
  92. visibleEntries.Add(newEntry);
  93. contentsDirty = true;
  94. }
  95. while (numVisibleEntries < visibleEntries.Count)
  96. {
  97. int lastIdx = visibleEntries.Count - 1;
  98. visibleEntries[lastIdx].Destroy();
  99. visibleEntries.RemoveAt(lastIdx);
  100. contentsDirty = true;
  101. }
  102. if (scrollPct != scrollArea.VerticalScroll)
  103. {
  104. scrollPct = scrollArea.VerticalScroll;
  105. contentsDirty = true;
  106. }
  107. if (contentsDirty)
  108. {
  109. int totalElementHeight = entries.Count*entryHeight;
  110. int scrollableHeight = MathEx.Max(0, totalElementHeight - height);
  111. int startPos = MathEx.FloorToInt(scrollPct*scrollableHeight);
  112. int startIndex = startPos/entryHeight;
  113. topPadding.SetPosition(0, 0);
  114. topPadding.SetHeight(startIndex*entryHeight);
  115. for (int i = 0; i < visibleEntries.Count; i++)
  116. {
  117. visibleEntries[i].UpdateContents(entries[startIndex + i]);
  118. visibleEntries[i].panel.SetPosition(0, i * entryHeight);
  119. }
  120. int bottomPosition = (startIndex + visibleEntries.Count)*entryHeight;
  121. bottomPadding.SetPosition(0, bottomPosition);
  122. bottomPadding.SetHeight(totalElementHeight - bottomPosition);
  123. contentsDirty = false;
  124. }
  125. }
  126. }
  127. public class GUIListViewData
  128. {
  129. }
  130. public abstract class GUIListViewEntry<TData>
  131. where TData : GUIListViewData
  132. {
  133. internal GUIPanel panel;
  134. internal GUILayoutY layout;
  135. protected GUILayout Layout { get { return layout; } }
  136. internal void Initialize(GUIScrollArea parent)
  137. {
  138. int numElements = parent.Layout.ChildCount;
  139. // Last panel is always the padding panel, so keep it there
  140. panel = parent.Layout.InsertPanel(numElements - 1);
  141. layout = panel.AddLayoutY();
  142. }
  143. internal void Destroy()
  144. {
  145. panel.Destroy();
  146. }
  147. public abstract void BuildGUI();
  148. public abstract void UpdateContents(TData data);
  149. }
  150. }