GUI Scripting: ListDef

From The DarkMod Wiki
Revision as of 17:44, 4 November 2022 by Geep (talk | contribs) (Add category tag)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

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.

Properties Shared with All GuiDef Types

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, nor appear within event handlers.

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.

Internationalization? Sorry

There appears to be no inherent i18n support in listDef. That is, in the prepopulated string array "listItems", with tab-separated substrings, the prefix "#str_" has no special meaning or handling. So if you want to localize the list contents, this must be done by detecting the TDM language in effect, then prepopulating listItems with the language-specific content. This evidently is not done currently, e.g., in DownloadMenu.cpp or MissionManager.cpp or server-side, so the FM titles displayed are not translated.

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."