UI List System
Document Summary: Technical guide for lists in UnrealEngine3.
Document Changelog: Created by Ron Prestenback?; maintained by Richard Nalezynski?.
Lists are containers for data. In general "list configuration" is any data which affects how a collection of abstract, untyped data records are arranged and displayed.
Working with Lists
Conceptually, a UIList contains an array of untyped data. Each record in your dataset corresponds to a single element. Depending on how your list is configured to present its data, a single element may correspond to a single cell in the list. If your record has multiple fields, a single element will usually correspond to a single row with multiple columns (the traditional "report" mode). In very rare cases, a single element will correspond to a single column with multiple rows, which is sort of like a multi-column or report view turned sideways.
So the first thing you'll need to do is to determine the element index based on the row and column clicked on. The algorithm for this will be driven by the list's CellLinkType. In the simplest case where each cell represents a single data record, the element index is something like:
NewIndex = (NumColumns * ClickedRow) + ClickedColumn;
For a list using linked columns, where each row is a single element, the algorithm goes something like this:
NewIndex = ClickedRow;
These calculations are just examples so they won't actually give a good result. For the real algorithm you'd also need to take stuff like the list's current TopIndex into account, but you get the idea.
Once you have the index for the selected element, just pass that value to SetIndex().
The data presentation vehicle we know as a list is composed of three parts: the data source, the presenter, and a container for presenting the data.
UIListElementProvider: The data source is responsible for providing the list with its elements. It handles supplying the initial set of selected items; publishing user selections to a storage location (which could be a profile, a remote server, or some actor instance in a live game); and in some cases might e.g. veto selection of certain elements depending on the current game's state.
UIComp_ListPresenter: The presenter is responsible for formatting and rendering the list data. It determines which elements to render; the order in which the elements should be rendered; creates the underlying cells for each list element; propagates styles and style changes to those cells; ensures that the cells are in the correct state; etc.
UIList: The container defines the parameters that the presenter uses for rendering the list data. The container manages:
Examples of list configuration data you may need to take into account are CellLinkType, TopIndex, MaxVisibleItems, WrapType, bAllowDisabledItemSelection, and bEnableMultiSelect.
The difference in how list elements are mapped to columns/rows is described in the comments of the ECellLinkType enum, in UIList.uc:
- list layout: number of elements; number of columns and rows; defines the bounding region that the presenter uses for rendering the list data; controls how a single element is mapped to list columns and rows; manages any additional list helpers, such as scrollbars, column headers, etc.
- list interaction: tracks the indices for the selected elements; tracks the maximum number of elements that can be visible at once; tracks the index of the first element that is visible; handles all the "housecleaning" that goes along with insertion & removal of elements (such as updating the list's selected index if it becomes invalid due to a removal, and adjusting the TopIndex if necessary); stores and applies user configuration data which affects how the list operates, such as whether list element navigation should "wrap" and whether selection of disabled elements is allowed.
- input processing: Updates the selected index based on user input, such as pressing up/down or mouse clicks; updates the visible elements when the user moves the scrollbar; notifies the presenter when the user resizes columns; etc.
- LINKED_None means one list element maps to one cell in the list, regardless of the number of columns and rows the list is displaying.
- LINKED_Columns means the rows are varied; but the columns stay the same (linked). The configured
RowCount will only be respected if the list's
RowAutoSizeMode is set to None.
- LINKED_Rows means the columns are varied; but the rows stay the same (linked).
Since a UIList can be bound to any type of data, the list itself works only with indices. UIList.GetCurrentItem() will return the value of the current index in the Items array, which corresponds to an index into whatever array the data store is using to provide the list with data from. Widgets and data stores communicate through interfaces, in this case the UIListElementProvider and UIListElementCellProvider interfaces.
The UIListElementProvider is an interface for the object which provides the UIList with the indices that go into the UIList.Items array, and is cached in the UIList.DataProvider variable.
UIListElementCellProvider is an interface for the object which provides the actual values for each column in a particular row. Depending on the type of data bound to the list, the data for each element in the list might come from a different object (in the case of the OnlineGameSearch data store, for example, where each server received has its own UIDataProvider_Settings to provide the data for that server), or they could all come from the same object. For this reason, UIListElementProvider interface has a method for retrieving the correct cell value provider for a given index [into the data store value array] - GetElementCellValueProvider(). Once you have retrieved the correct cell value provider for the selected element, you can call the GetCellFieldValue() method to retrieve the value of whatever data field you're looking for, for that particular element. So it goes something like this:
if ( MyList->DataProvider )
INT CurrentItem = MyList->GetCurrentItem();
if(CurrentItem != INDEX_NONE )
TScriptInterface<IUIListElementCellProvider> CellProvider = MyList->DataProvider->GetElementCellValueProvider(MyList->DataSource.DataStoreFieldName, CurrentItem);
if ( CellProvider )
// call GetCellFieldValue to get the value of the field I want