Переглянути джерело

Clearning the log window will no longer break "scroll to latest" functionality

BearishSun 10 роки тому
батько
коміт
b97ad20fcc
2 змінених файлів з 381 додано та 393 видалено
  1. 47 61
      BansheeEngine/Include/BsPaths.h
  2. 334 332
      MBansheeEngine/GUI/GUIListView.cs

+ 47 - 61
BansheeEngine/Include/BsPaths.h

@@ -1,62 +1,48 @@
-#pragma once
-
-namespace BansheeEngine
-{
-	static const char* ENGINE_ASSEMBLY = "MBansheeEngine";
-	static const char* SCRIPT_GAME_ASSEMBLY = "MScriptGame";
-	static const char* GAME_RESOURCES_FOLDER_NAME = "Resources\\";
-	static const char* GAME_SETTINGS_NAME = "GameSettings.asset";
-	static const char* GAME_RESOURCE_MANIFEST_NAME = "ResourceManifest.asset";
-
-	/**
-	 * @brief	Contains common engine paths.
-	 */
-	class BS_EXPORT Paths
-	{
-	public:
-		/**
-		 * @brief	Returns a path where the managed release assemblies are located. Relative to working directory.
-		 */
-		static const Path& getReleaseAssemblyPath();
-
-		/**
-		 * @brief	Returns a path where the managed debug assemblies are located. Relative to working directory.
-		 */
-		static const Path& getDebugAssemblyPath();
-
-		/**
-		 * @brief	Returns a path where the builtin assets are located. Relative to working directory.
-		 */
-		static const Path& getRuntimeDataPath();
-
-		/**
-		 * @brief	Returns a path where the builtin engine-specific assets are located. Relative to working directory.
-		 */
-		static const Path& getEngineDataPath();
-
-		/**
-		 * @brief	Returns a path to the game settings file used by editor-built executables. Relative to working directory.
-		 */
-		static const Path& getGameSettingsPath();
-
-		/**
-		 * @brief	Returns a path to the game resources folder used by editor-built executables. Relative to working directory.
-		 */
-		static const Path& getGameResourcesPath();
-
-		/**
-		 * @brief	Searches common locations for a specified path by querying if the file/directory exists and 
-		 * 			returns the found path.
-		 * 			
-		 * @param	path	Relative path to search for (e.g. "Data\").
-		 * 					
-		 * @returns	Path at which the relative path was found at. This path will be relative to the working directory.
-		 */
-		static Path findPath(const Path& path);
-
-		static const Path RELEASE_ASSEMBLY_PATH;
-		static const Path DEBUG_ASSEMBLY_PATH;
-		static const Path RUNTIME_DATA_PATH;
-		static const Path ENGINE_DATA_PATH;
-	};
+#pragma once
+
+namespace BansheeEngine
+{
+	static const char* ENGINE_ASSEMBLY = "MBansheeEngine";
+	static const char* SCRIPT_GAME_ASSEMBLY = "MScriptGame";
+	static const char* GAME_RESOURCES_FOLDER_NAME = "Resources\\";
+	static const char* GAME_SETTINGS_NAME = "GameSettings.asset";
+	static const char* GAME_RESOURCE_MANIFEST_NAME = "ResourceManifest.asset";
+
+	/** Contains common engine paths. */
+	class BS_EXPORT Paths
+	{
+	public:
+		/**	Returns a path where the managed release assemblies are located. Relative to working directory. */
+		static const Path& getReleaseAssemblyPath();
+
+		/**	Returns a path where the managed debug assemblies are located. Relative to working directory. */
+		static const Path& getDebugAssemblyPath();
+
+		/**	Returns a path where the builtin assets are located. Relative to working directory. */
+		static const Path& getRuntimeDataPath();
+
+		/**	Returns a path where the builtin engine-specific assets are located. Relative to working directory. */
+		static const Path& getEngineDataPath();
+
+		/**	Returns a path to the game settings file used by editor-built executables. Relative to working directory. */
+		static const Path& getGameSettingsPath();
+
+		/**	Returns a path to the game resources folder used by editor-built executables. Relative to working directory. */
+		static const Path& getGameResourcesPath();
+
+		/**
+		 * Searches common locations for a specified path by querying if the file/directory exists and returns the found 
+		 * path.
+		 * 			
+		 * @param[in]	path	Relative path to search for (e.g. "Data\").				
+		 * @return				Path at which the relative path was found at. This path will be relative to the working 
+		 *						directory.
+		 */
+		static Path findPath(const Path& path);
+
+		static const Path RELEASE_ASSEMBLY_PATH;
+		static const Path DEBUG_ASSEMBLY_PATH;
+		static const Path RUNTIME_DATA_PATH;
+		static const Path ENGINE_DATA_PATH;
+	};
 }

+ 334 - 332
MBansheeEngine/GUI/GUIListView.cs

@@ -1,332 +1,334 @@
-using System.Collections.Generic;
-
-namespace BansheeEngine
-{
-    /// <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.
-    /// 
-    /// Contains shared functionality used by all instances of <see cref="GUIListView{TEntry,TData}"/>.
-    /// </summary>
-    /// <typeparam name="TData">>Type used for storing the data for all list entries.</typeparam>
-    public abstract class GUIListViewBase<TData>
-        where TData : GUIListViewData
-    {
-        // TODO - Only fixed size is supported. It should be nice if this object could just be placed in layout like any
-        // other GUI element. Would likely need some kind of a way to get notified when parent layout changes.
-        // (Possibly add a callback to GUIPanel when updateLayout is called?)
-
-        protected List<TData> entries = new List<TData>();
-        protected GUIScrollArea scrollArea;
-        protected GUILabel topPadding;
-        protected GUILabel bottomPadding;
-        protected int width;
-        protected int height;
-        protected int entryHeight;
-        protected float scrollPct = 0.0f;
-        protected bool scrollToLatest = true;
-        protected internal 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>
-        /// Primary GUI scroll area that all entries are contained within.
-        /// </summary>
-        internal GUIScrollArea ScrollArea 
-        {
-            get { return scrollArea; }
-        }
-
-        /// <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>
-        protected GUIListViewBase(int width, int height, int entryHeight, GUILayout layout)
-        {
-            scrollArea = new GUIScrollArea(ScrollBarType.ShowIfDoesntFit, ScrollBarType.NeverShow, 
-                GUIOption.FixedWidth(width), GUIOption.FixedHeight(height));
-            layout.AddElement(scrollArea);
-
-            topPadding = new GUILabel(new LocString());
-            bottomPadding = new GUILabel(new LocString());
-
-            scrollArea.Layout.AddElement(topPadding);
-            scrollArea.Layout.AddElement(bottomPadding);
-
-            this.width = width;
-            this.height = height;
-            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)
-            {
-                entries.RemoveAt(index);
-                contentsDirty = true;
-            }
-        }
-
-        /// <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.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)
-                entries.Insert(index, data);
-            else
-                entries.Add(data);
-
-            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)
-            {
-                this.width = width;
-                this.height = height;
-
-                Rect2I bounds = scrollArea.Bounds;
-                bounds.width = width;
-                bounds.height = height;
-                scrollArea.Bounds = bounds;
-            }
-        }
-
-        /// <summary>
-        /// Updates the visuals of the list view. Should be called once per frame.
-        /// </summary>
-        public abstract void Update();
-    }
-
-    /// <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> : GUIListViewBase<TData>
-        where TEntry : GUIListViewEntry<TData>, new()
-        where TData : GUIListViewData
-    {
-        private List<TEntry> visibleEntries = new List<TEntry>();
-
-        /// <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)
-            :base(width, height, entryHeight, layout)
-        { }
-
-        /// <inheritdoc/>
-        public override void Update()
-        {
-            int numVisibleEntries = MathEx.CeilToInt(height / (float)entryHeight) + 1;
-            numVisibleEntries = MathEx.Min(numVisibleEntries, entries.Count);
-
-            while (visibleEntries.Count < numVisibleEntries)
-            {
-                TEntry newEntry = new TEntry();
-                newEntry.Initialize(this);
-                newEntry.panel.SetHeight(entryHeight);
-
-                visibleEntries.Add(newEntry);
-                contentsDirty = true;
-            }
-
-            while (numVisibleEntries < visibleEntries.Count)
-            {
-                int lastIdx = visibleEntries.Count - 1;
-
-                visibleEntries[lastIdx].Destroy();
-                visibleEntries.RemoveAt(lastIdx);
-
-                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 maxScrollOffset = MathEx.Max(0, totalElementHeight - height - 1);
-
-                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(startIndex + i, entries[startIndex + i]);
-                    visibleEntries[i].panel.SetPosition(0, i * 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
-    {
-        private GUIListViewBase<TData> parent;
-        internal GUIPanel panel;
-        internal GUILayoutY layout;
-
-        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(GUIListViewBase<TData> parent)
-        {
-            this.parent = parent;
-
-            GUIScrollArea scrollArea = parent.ScrollArea;
-            int numElements = scrollArea.Layout.ChildCount;
-
-            // Last panel is always the padding panel, so keep it there
-            panel = scrollArea.Layout.InsertPanel(numElements - 1);
-            layout = panel.AddLayoutY();
-
-            BuildGUI();
-        }
-
-        /// <summary>
-        /// Destoys the GUI elements for the entry.
-        /// </summary>
-        internal void Destroy()
-        {
-            panel.Destroy();
-        }
-
-        /// <summary>
-        /// Causes all visible entries in the parent list to be updated.
-        /// </summary>
-        protected void RefreshEntries()
-        {
-            parent.contentsDirty = true;
-        }
-
-        /// <summary>
-        /// Allows child classes to create GUI elements required by their entry specialization.
-        /// </summary>
-        public abstract void BuildGUI();
-
-        /// <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);
-    }
-}
+using System.Collections.Generic;
+
+namespace BansheeEngine
+{
+    /// <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.
+    /// 
+    /// Contains shared functionality used by all instances of <see cref="GUIListView{TEntry,TData}"/>.
+    /// </summary>
+    /// <typeparam name="TData">>Type used for storing the data for all list entries.</typeparam>
+    public abstract class GUIListViewBase<TData>
+        where TData : GUIListViewData
+    {
+        // TODO - Only fixed size is supported. It should be nice if this object could just be placed in layout like any
+        // other GUI element. Would likely need some kind of a way to get notified when parent layout changes.
+        // (Possibly add a callback to GUIPanel when updateLayout is called?)
+
+        protected List<TData> entries = new List<TData>();
+        protected GUIScrollArea scrollArea;
+        protected GUILabel topPadding;
+        protected GUILabel bottomPadding;
+        protected int width;
+        protected int height;
+        protected int entryHeight;
+        protected float scrollPct;
+        protected bool scrollToLatest;
+        protected int totalHeight;
+        protected internal 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>
+        /// Primary GUI scroll area that all entries are contained within.
+        /// </summary>
+        internal GUIScrollArea ScrollArea 
+        {
+            get { return scrollArea; }
+        }
+
+        /// <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>
+        protected GUIListViewBase(int width, int height, int entryHeight, GUILayout layout)
+        {
+            scrollArea = new GUIScrollArea(ScrollBarType.ShowIfDoesntFit, ScrollBarType.NeverShow, 
+                GUIOption.FixedWidth(width), GUIOption.FixedHeight(height));
+            layout.AddElement(scrollArea);
+
+            topPadding = new GUILabel(new LocString());
+            bottomPadding = new GUILabel(new LocString());
+
+            scrollArea.Layout.AddElement(topPadding);
+            scrollArea.Layout.AddElement(bottomPadding);
+
+            this.width = width;
+            this.height = height;
+            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)
+            {
+                entries.RemoveAt(index);
+                contentsDirty = true;
+            }
+        }
+
+        /// <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.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)
+                entries.Insert(index, data);
+            else
+                entries.Add(data);
+
+            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)
+            {
+                this.width = width;
+                this.height = height;
+
+                Rect2I bounds = scrollArea.Bounds;
+                bounds.width = width;
+                bounds.height = height;
+                scrollArea.Bounds = bounds;
+            }
+        }
+
+        /// <summary>
+        /// Updates the visuals of the list view. Should be called once per frame.
+        /// </summary>
+        public abstract void Update();
+    }
+
+    /// <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> : GUIListViewBase<TData>
+        where TEntry : GUIListViewEntry<TData>, new()
+        where TData : GUIListViewData
+    {
+        private List<TEntry> visibleEntries = new List<TEntry>();
+
+        /// <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)
+            :base(width, height, entryHeight, layout)
+        { }
+
+        /// <inheritdoc/>
+        public override void Update()
+        {
+            int numVisibleEntries = MathEx.CeilToInt(height / (float)entryHeight) + 1;
+            numVisibleEntries = MathEx.Min(numVisibleEntries, entries.Count);
+
+            while (visibleEntries.Count < numVisibleEntries)
+            {
+                TEntry newEntry = new TEntry();
+                newEntry.Initialize(this);
+                newEntry.panel.SetHeight(entryHeight);
+
+                visibleEntries.Add(newEntry);
+                contentsDirty = true;
+            }
+
+            while (numVisibleEntries < visibleEntries.Count)
+            {
+                int lastIdx = visibleEntries.Count - 1;
+
+                visibleEntries[lastIdx].Destroy();
+                visibleEntries.RemoveAt(lastIdx);
+
+                contentsDirty = true;
+            }
+
+            if (scrollPct != scrollArea.VerticalScroll)
+            {
+                scrollPct = scrollArea.VerticalScroll;
+                contentsDirty = true;
+            }
+
+            if (contentsDirty)
+            {
+                int newHeight = entries.Count * entryHeight;
+                if (scrollToLatest)
+                {
+                    if (totalHeight > height && scrollPct < 1.0f)
+                        scrollToLatest = false;
+                }
+                else
+                {
+                    if (totalHeight <= height || scrollPct >= 1.0f)
+                        scrollToLatest = true;
+                }
+
+                totalHeight = newHeight;
+                int maxScrollOffset = MathEx.Max(0, totalHeight - height - 1);
+
+                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(startIndex + i, entries[startIndex + i]);
+                    visibleEntries[i].panel.SetPosition(0, i * entryHeight);
+                }
+
+                int bottomPosition = MathEx.Min(totalHeight, (startIndex + visibleEntries.Count) * entryHeight);
+                bottomPadding.SetHeight(totalHeight - bottomPosition);
+
+                if (scrollToLatest)
+                {
+                    if (newHeight <= 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
+    {
+        private GUIListViewBase<TData> parent;
+        internal GUIPanel panel;
+        internal GUILayoutY layout;
+
+        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(GUIListViewBase<TData> parent)
+        {
+            this.parent = parent;
+
+            GUIScrollArea scrollArea = parent.ScrollArea;
+            int numElements = scrollArea.Layout.ChildCount;
+
+            // Last panel is always the padding panel, so keep it there
+            panel = scrollArea.Layout.InsertPanel(numElements - 1);
+            layout = panel.AddLayoutY();
+
+            BuildGUI();
+        }
+
+        /// <summary>
+        /// Destoys the GUI elements for the entry.
+        /// </summary>
+        internal void Destroy()
+        {
+            panel.Destroy();
+        }
+
+        /// <summary>
+        /// Causes all visible entries in the parent list to be updated.
+        /// </summary>
+        protected void RefreshEntries()
+        {
+            parent.contentsDirty = true;
+        }
+
+        /// <summary>
+        /// Allows child classes to create GUI elements required by their entry specialization.
+        /// </summary>
+        public abstract void BuildGUI();
+
+        /// <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);
+    }
+}