|
|
@@ -2,7 +2,13 @@
|
|
|
|
|
|
namespace BansheeEngine
|
|
|
{
|
|
|
- // TODO - Doc
|
|
|
+ /// <summary>
|
|
|
+ /// GUI element that can efficiently display a list of entries that share the same height. This element is mostly an
|
|
|
+ /// optimization as only visible entries have actual GUI elements, as opposed to just adding GUI elements directly
|
|
|
+ /// in a vertical GUI layout. This allows the list view to have thousands of elements with little performance impact.
|
|
|
+ /// </summary>
|
|
|
+ /// <typeparam name="TEntry">Type used for creating and updating the GUI elements of the visible entries.</typeparam>
|
|
|
+ /// <typeparam name="TData">Type used for storing the data for all list entries.</typeparam>
|
|
|
public class GUIListView<TEntry, TData>
|
|
|
where TEntry : GUIListViewEntry<TData>, new()
|
|
|
where TData : GUIListViewData
|
|
|
@@ -20,19 +26,33 @@ namespace BansheeEngine
|
|
|
private int height;
|
|
|
private int entryHeight;
|
|
|
private float scrollPct = 0.0f;
|
|
|
+ private bool scrollToLatest = true;
|
|
|
private bool contentsDirty = true;
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Total number of entries in the list.
|
|
|
+ /// </summary>
|
|
|
public int NumEntries
|
|
|
{
|
|
|
get { return entries.Count; }
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Height of a single entry in the list, in pixels.
|
|
|
+ /// </summary>
|
|
|
public int EntryHeight
|
|
|
{
|
|
|
get { return entryHeight; }
|
|
|
set { entryHeight = value; }
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Creates a new empty list view.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="width">Width of the list view, in pixels.</param>
|
|
|
+ /// <param name="height">Height of the list view, in pixels.</param>
|
|
|
+ /// <param name="entryHeight">Height of a single element in the list, in pixels.</param>
|
|
|
+ /// <param name="layout">GUI layout into which the list view will be placed into.</param>
|
|
|
public GUIListView(int width, int height, int entryHeight, GUILayout layout)
|
|
|
{
|
|
|
scrollArea = new GUIScrollArea(GUIOption.FixedWidth(width), GUIOption.FixedHeight(height));
|
|
|
@@ -49,12 +69,20 @@ namespace BansheeEngine
|
|
|
this.entryHeight = entryHeight;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Adds a new entry to the end of the list.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="data">Data of the entry to add.</param>
|
|
|
public void AddEntry(TData data)
|
|
|
{
|
|
|
entries.Add(data);
|
|
|
contentsDirty = true;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Removes an entry from the specified index. If the index is out of range nothing happens.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="index">Sequential index of the element to remove from the list.</param>
|
|
|
public void RemoveEntry(int index)
|
|
|
{
|
|
|
if (index >= 0 && index < entries.Count)
|
|
|
@@ -64,17 +92,30 @@ namespace BansheeEngine
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Removes all entries from the list.
|
|
|
+ /// </summary>
|
|
|
public void Clear()
|
|
|
{
|
|
|
entries.Clear();
|
|
|
contentsDirty = true;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Finds an index of the specified entry in the list.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="data">Data of the entry to search for.</param>
|
|
|
+ /// <returns>Index of the entry if found, -1 otherwise.</returns>
|
|
|
public int FindEntry(TData data)
|
|
|
{
|
|
|
- return entries.FindIndex(x => x == data);
|
|
|
+ return entries.FindIndex(x => x.Equals(data));
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Adds a new entry at the specified index. If the index is out of range the entry is added at the end of the list.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="index">Sequential index at which to insert the entry. </param>
|
|
|
+ /// <param name="data">Data of the entry to insert.</param>
|
|
|
public void InsertEntry(int index, TData data)
|
|
|
{
|
|
|
if (index >= 0 && index <= entries.Count)
|
|
|
@@ -85,6 +126,11 @@ namespace BansheeEngine
|
|
|
contentsDirty = true;
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Changes the size of the list view.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="width">Width in pixels.</param>
|
|
|
+ /// <param name="height">Height in pixels.</param>
|
|
|
public void SetSize(int width, int height)
|
|
|
{
|
|
|
if (width != this.width || height != this.height)
|
|
|
@@ -99,6 +145,9 @@ namespace BansheeEngine
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Updates the visuals of the list view. Should be called once per frame.
|
|
|
+ /// </summary>
|
|
|
public void Update()
|
|
|
{
|
|
|
int numVisibleEntries = MathEx.CeilToInt(height / (float)entryHeight) + 1;
|
|
|
@@ -124,41 +173,71 @@ namespace BansheeEngine
|
|
|
contentsDirty = true;
|
|
|
}
|
|
|
|
|
|
+ int totalElementHeight = entries.Count * entryHeight;
|
|
|
if (scrollPct != scrollArea.VerticalScroll)
|
|
|
{
|
|
|
scrollPct = scrollArea.VerticalScroll;
|
|
|
contentsDirty = true;
|
|
|
+
|
|
|
+ if (scrollToLatest)
|
|
|
+ {
|
|
|
+ if (scrollPct < 1.0f)
|
|
|
+ scrollToLatest = false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (totalElementHeight <= height || scrollPct >= 1.0f)
|
|
|
+ scrollToLatest = true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (contentsDirty)
|
|
|
{
|
|
|
- int totalElementHeight = entries.Count*entryHeight;
|
|
|
- int scrollableHeight = MathEx.Max(0, totalElementHeight - height - 1);
|
|
|
+ int maxScrollOffset = MathEx.Max(0, totalElementHeight - height - 1);
|
|
|
|
|
|
- int startPos = MathEx.FloorToInt(scrollPct*scrollableHeight);
|
|
|
- int startIndex = startPos/entryHeight;
|
|
|
+ int startPos = MathEx.FloorToInt(scrollPct*maxScrollOffset);
|
|
|
+ int startIndex = MathEx.FloorToInt(startPos/(float)entryHeight);
|
|
|
+
|
|
|
+ // Check if we're at the list bottom and the extra element is out of bounds
|
|
|
+ if ((startIndex + visibleEntries.Count) > entries.Count)
|
|
|
+ startIndex--; // Keep the extra element at the top always
|
|
|
|
|
|
topPadding.SetHeight(startIndex*entryHeight);
|
|
|
|
|
|
for (int i = 0; i < visibleEntries.Count; i++)
|
|
|
{
|
|
|
- visibleEntries[i].UpdateContents(entries[startIndex + i]);
|
|
|
+ visibleEntries[i].UpdateContents(startIndex + i, entries[startIndex + i]);
|
|
|
visibleEntries[i].panel.SetPosition(0, i * entryHeight);
|
|
|
}
|
|
|
|
|
|
- int bottomPosition = (startIndex + visibleEntries.Count)*entryHeight;
|
|
|
+ int bottomPosition = MathEx.Min(totalElementHeight, (startIndex + visibleEntries.Count)*entryHeight);
|
|
|
bottomPadding.SetHeight(totalElementHeight - bottomPosition);
|
|
|
|
|
|
+ if (scrollToLatest)
|
|
|
+ {
|
|
|
+ if (totalElementHeight <= height)
|
|
|
+ scrollArea.VerticalScroll = 0.0f;
|
|
|
+ else
|
|
|
+ scrollArea.VerticalScroll = 1.0f;
|
|
|
+ }
|
|
|
+
|
|
|
contentsDirty = false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Base class that contains data for individual entries used in <see cref="GUIListView{TEntry,TData}"/>.
|
|
|
+ /// </summary>
|
|
|
public class GUIListViewData
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Base class that displays GUI elements for visible entries used in <see cref="GUIListView{TEntry,TData}"/>.
|
|
|
+ /// </summary>
|
|
|
+ /// <typeparam name="TData">Type of object that contains data used for initializing the GUI elements.</typeparam>
|
|
|
public abstract class GUIListViewEntry<TData>
|
|
|
where TData : GUIListViewData
|
|
|
{
|
|
|
@@ -167,6 +246,10 @@ namespace BansheeEngine
|
|
|
|
|
|
protected GUILayout Layout { get { return layout; } }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes the GUI elements for the entry.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="parent">Scroll area into whose layout to insert the GUI elements.</param>
|
|
|
internal void Initialize(GUIScrollArea parent)
|
|
|
{
|
|
|
int numElements = parent.Layout.ChildCount;
|
|
|
@@ -178,12 +261,24 @@ namespace BansheeEngine
|
|
|
BuildGUI();
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Destoys the GUI elements for the entry.
|
|
|
+ /// </summary>
|
|
|
internal void Destroy()
|
|
|
{
|
|
|
panel.Destroy();
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Allows child classes to create GUI elements required by their entry specialization.
|
|
|
+ /// </summary>
|
|
|
public abstract void BuildGUI();
|
|
|
- public abstract void UpdateContents(TData data);
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Allows child classes to update GUI element(s) with new contents.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="index">Sequential index of the entry in the list.</param>
|
|
|
+ /// <param name="data">Data of the entry to display.</param>
|
|
|
+ public abstract void UpdateContents(int index, TData data);
|
|
|
}
|
|
|
}
|