GUI Scripting: ListDef
Title: GUI Scripting: ListDef This page is part of a series. See GUI Scripting Language for overview.
Introduction
One of the more complex controls, a listDef displays a vertical list of items in rows, from which the user selects by clicking. A row can be composed of multiple data columns, that the listDef defines using "tab stops". A column of this table will have either text or icons in it.
The listDef GUI does not itself populate a list, but relies on associated C++ code to do so, typically with a dynamic list.
ListDefs in TDM's Main Menu System
Load/Save
After hitting "Load/Save", a page with a list of saved missions appears. Each row has 3 text columns: a short name for the save, and the date and time when saved. This employs "listDef LGSaveGameList" in tdm_gui01.pk4\guis\mainmenu_loadsave.gui . When a game is in-process and this menu is invoked, the user is able to select a row. At that point, "Load", "Save", and "Delete" buttons become enabled, along with a field to rename a saved game, e.g., from an autogenerated name like "QuickSave_0" to something more meaningful.
New Mission
After hitting "New Mission", the "Available Missions" list appears, showing FMs installed locally. Games played to completion are marked with a green checkmark icon in the second column. This employs "listDef choosemod_list" in mainmenu_newgame.gui . See "New Mission Example" further below.
Download Missions
From the menu page with "Available Missions" just discussed, the "Download Missions" button brings up two pages. The lefthand one, "Online Mission Archive", has a list "Downloadable Missions" using a single-column listDef populated by the server. The righthand one, "Download Status", has a "Selected for Download" list, populated by the user. These two pages are defined in mainmenu_download.gui, that has "listDef available_list" and "listDef selected_list".
Beneath the lefthand list are two buttons, "Select All >>" and "Select for download >>". These copy entries from the lefthand list to the righthand. Note that the lefthand list is not multiple-selection. Instead, the user populates the righthand list one selection at a time (or all - with a dedicated button). Clicking an FM in the righthand list deletes it, undoing its selection for download. "Selected for Download" has 2 text columns. Once downloading begins, the second column shows the percentage downloaded so far.
Keystrokes and Mouse Actions
This is somewhat simplified. See "Additional Notes" below for more.
Adjusting the Set of Visible Rows within the Scrollable Area
- LMB drag vertical scrollbar thumb: adjust rows.
- LMB click in scrollbar area: thumb jumps to location; adjust rows.
- Up arrow or mouse wheel up or RMB above scrollbar thumb: up 1 row.
- Down arrow or mouse wheel down or RMB below scrollbar thumb: down 1 row.
- Page Up: up by the number of visible rows.
- Page Down: down by the number of visible rows.
Selection
- LMB on a data row: if clear, select it, and clear any other row.
- Alphanumeric string: Find and select topmost row with prefix match in first text column. Adjust set of visible rows as needed. Entry string (which is hidden) can be extended to change selection. Hidden string can be cleared by manually selecting a row.
- Any change of row selection: onAction handler.
- With a row selected, Enter or Keypad Enter: onEnter handler.
- Row LMB double-click: select (if not already) then onEnter handler.
A listDef will employ Properties in Common to size its rectangle and set the attributes of its text, including mouse hover. In practice, User Variables don't seem to be much used. Event Handlers for onAction, onEnter, onMouseEnter, and onMouseExit are important.
Properties Specific to ListDefs
® = A property known as a "register", that can be altered at runtime by binding it to a GUI:: parameter, or in a "set" or "transition" script command. Other properties cannot be so altered.
Data Source
Listname "name"
Specifies the C++ name of the list whose contents the listDef will display. This name may or may not be different from "listDef name". The listname string is referenced by the code to populate the list and to use as a prefix when naming internal state variables.
listname "downloadSelectedList"
For Multi-Column Lists
These Properties take strings with comma-separated values, where each value is for a sequential "tab stop", that is, table column.
Tabaligns "comma_separated_values"
Has "textalign" values for each column: 0 = Left, 1 = Center, 2 = Right.
tabAligns "0, 1" // From manmenu_newgame.gui, indicating "Left, Center"
Tabstops "comma_separated_values"
Has the x-axis offset of each column.
tabstops "0, 228"
For Icons in Multi-Column Lists
These were not mentioned in Ref 1 of GUI Scripting: References & Resources; are they TDM specific?
TabTypes "comma_separated_values"
Content type for each column. 0 = text, 1 = icon. If not given, all columns are 0 (Text).
tabTypes "0, 1" // Text, Icon
Tabvaligns "comma_separated_values"
For icons, not text, has vertical alignment values for each column: 0=Top, 1=Center, 2=Bottom. Default alignments are Top.
Tabvaligns "0, 1"
TabIconSizes "comma_separated_value_pairs"
This should have twice as many values as the number of columns. Text columns should have "0,0".
tabIconSizes "0,0, 14,14" // TabStop 1 size is 15w x 15h
TabIconVOffset "comma_separated_values"
tabIconVOffset "0, 1"
Mtr_suffix "path_to_mtr"
Specifies the material (e.g., image) used for the icon. Property name must start with prefix "mtr_", then be unique. There may be any number of these in a GUI. For columns of tabtype "icon":
- an "mtr..." name appearing in the data will be interpreted as an icon's material definition, not a literal string.
- An empty string will indicate a table cell with no icon.
mtr_complete "guis/assets/mainmenu/buttons_start/icon_completed" // green checkmark
Addition ListDef Properties – Not Used in TDM Core
Horizontal bool
If 1 (true), then the table uses a horizontal scrollbar instead of a vertical one. Untested, says Ref 1 of GUI Scripting: References & Resources. Unclear what behavior was intended. Default: 0 (false).
horizontal 1
Multiplesel bool
Indicates if the user can select more than one row simultaneously. (Requires code support, says Ref 1 of GUI Scripting: References & Resources.) Default: 0 (false).
multilesel 1
New Game Example
This fragment from mainmenu_newgame.gui is a good example of the Properties needed for a 2-column table with text and icons. As for event handlers, when a mission is selected for possible install, a lefthand windowDef, "modToInstallVisible", will appear to describe it. Invocation of that window is done by the onAction handler shown here (though details of the windowDef itself is not).
... windowDef choosemod_parent { rect 330,164,320,245 visible 1 matcolor 1,1,1,1 listDef choosemod_list { rect 0,0,270,245 bordercolor 0.0, 0.0, 0.0, 0.2 forecolor 0, 0, 0, 1 hovercolor 1, 1, 1, 1 textscale 0.29 listname "missionList" font "fonts/carleton" tabstops "0, 228" // Mission Name, Mission Complete Icon tabAligns "0, 1" // Left, Center tabTypes "0, 1" // Text, Icon tabIconSizes "0,0, 14,14" // TabStop 1 size 15,15 tabIconVOffset "0, 1" tabvaligns "0, 1" // Only works for icon columns mtr_complete "guis/assets/mainmenu/buttons_start/icon_completed" onAction { set "cmd" "onMissionSelected"; set "cmd" "play sound/meta/menu/mnu_select"; if ("gui:missionList_sel_0" >= 0) { set "modToInstallVisible" "1"; } } } } ...
Download Selected-List Example
See the "Introduction" above for an overall description of the two download tables. Here, there's a glimpse from mainmenu_download.gui, concerning the righthand "Selected for download" table. The fragment shows a 2-column list defined by tab stops. The user creates a download batch, with the mission names in the left column, download progress (initially blank) in the right column. If a mission needs to be removed from the batch, a LMB click (and the onAction handler shown) does it. Actual download processing, including updating the right column text values, is handled elsewhere.
... listDef selected_list { rect 0,0,240,185 bordercolor 0.0, 0.0, 0.0, 0.2 forecolor 0, 0, 0, 1 hovercolor 1, 1, 1, 1 textscale 0.257 listname "downloadSelectedList" font "fonts/carleton" tabstops "0, 185" // Mission Name, Download Progress tabAligns "0, 0" // Left, Left tabTypes "0, 0" // Text, Text onAction { set "cmd" "play sound/meta/menu/mnu_select;"; set "cmd" "onDeselectMissionForDownload"; } } ...
Additional Notes
Keystrokes and Mouse Actions
If Multiple Selection is Enabled
If "multiplesel 1" is in effect for the listDef, then... Ctrl+LMB on data row: If already selected, clear; if clear, select it too.
Load/Save's Special Handling of Row Double-Click
As indicated in the Introduction, a LMB double-click on a row will select that row (if not already selected) and call an onEnter handler. In the case of mainmenu_loadsave.gui, that handler will then do nothing when this is during a game, because both the Load and Save buttons are visible and it's not clear which is meant. If it's pre-game, though, only the Load button is present and so the double-clicks acts as if that was pushed.
Scrollbar Related
LMB or RMB clicks in the scroller are ignored in ListWindow.cpp, and handled instead in the scrollbar's idSliderWindow class. That is, scrollbars for listDefs and editDefs are implemented by the idSliderWindow class in SliderWindow.cpp (not to be confused with sliderDef.) Mouse actions on the scrollbar are handled there in its HandleEvent function, as follows. As mentioned in the Introduction, while the LMB is dragging the thumb, capture the mouse coordinates to move the thumb. Otherwise...
- RMB clicked above thumb: visible rows up a row
- RMB clicked below thumb: visible rows down a row
The code has a treatment for these additional keys, but these DON'T WORK, presumably because the keystrokes are not reaching this code:
- Right Arrow or Keypad Right Arrow: visible rows up a row
- Left Arrow or Keypad Left Arrow: visible rows down a row
Finally, a usability complaint. Because the implementation of the listDef's vertical scrollbar shows its thumb just as black dot, its behavior (when clicked or dragged) can seem mysterious. It would be better if the thumb was a more-conventional rectangle whose height reflected the percentage of the number of rows that the visible rows exposed.
Layout and Data Implementation
The operations of a listDef are supported by the idListGUILocal class in ListGUI.cpp and idListWindow in ListWindow.cpp. Incoming keystrokes and mouse actions are dealt with in ListWindow.cpp's idListWindow::HandleEvent(...).
Each GUI Parameter with comma-separated information about each column is first parsed, split, and stored into an associated internal array. The information in these arrays are then copied and repackaged together into a "tabInfo" array, representing the columns of any row, where each element describes all the attributes of the cell and its contents, including its border dimensions.
Internally, the actual data representing each table row is in a tab-separated string, placed into string array "listItems". This is pre-populated before rendering. There's also an array of integers, currentSel, that contains the 0-based indices of rows currently selected. This information is also stored in state interger variables named listName_sel_number, with a count in listname_sel_numsel. If the count is zero, listname_sel_0 is -1.
A ListDef that Didn't Make the Cut
There was an initial attempt, In mainmenu_success.gui, to employ a listDef to show a list of statistics. This was replaced by a column of stacked windowDefs instead, because (reports greebo): "listDefs don't get updated in the GUI unless you call gui->StateChanged(), which in turn breaks the main menu. Hence: text fields instead of listDefs."