GUI Scripting: ChoiceDef

From The DarkMod Wiki
Revision as of 17:42, 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.


A choiceDef is a menu component that allows the user to select the value of a particular attribute by cycling through a list of available choices. The current value of the attribute is shown as text within the choiceDef's rectangle. By convention, the text is localized to the user's language, as is a tooltip (a TDM extension to choiceDef) and the associated field prompt (provided by a separate windowDef).

To select a choiceDef of interest, hover over its text, which will highlight, and click with the left mouse button (LMB). Then to cycle, press one of these repeatedly:

  • Forwards: LMB; right arrow; keypad right arrow
  • Backwards: RMB, left arrow; keypad left arrow

Alternatively, with focus set on the control, press an alphanumeric key that matches the first letter of an available choice, e.g. Y for "Yes", N for "No". (Note: If more than one choice starts with the same character, typing that character will cycle among choices with that starting letter.)

In TDM practice, the changed value takes effect immediately. This change will propagate to a designated Cvar and/or designated GUI:: Parameter, often followed by invocation of an appropriate engine command. A designed Cvar, like all Cvars, can be manipulated in the console and is persisted to Darkmod.cfg. When those capabilities are unwanted, a GUI:Parameter can serve instead.

Features Shared with All GuiDef Types

A choiceDef will make use of Properties in common to sculpt the presentation of its text. User variables were not seen in practice, possibly because the designated Cvar/GUI:: Parameter can fulfill the role.

ChoiceDefs routinely have an onAction event handler, whose event block invokes the internal C++ task necessitated by a change in choice. onAction is called when LMB is first clicked and during subsequently value changes. It actually fires after the Cvar and/or GUI:: Parameter are updated, during the key (or mouse button) release.

Occasionally, a namedEvent handler is useful to provide feedback from the C++ code to the GUI.

Tool tips (implemented by event handlers) are provided for a number of choiceDefs and sliderDefs. See GUI Scripting:Tooltip Macro for more.

Additional Properties of ChoiceDefs

® = 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.

Lists of Valid Choices

Choices "choice_string" ®

Defines the human-readable list of choices to be shown. In some cases, also used as source for the bound Cvar's content (discussed below). It is recommended to use an i18n #str_ value, to point to a string with choices separated by semicolons. Common two-value vector strings, and their English translations, are:

choices "#str_04221" // No;Yes
choices "#str_07300" // Disabled;Enabled
choices "#str_07216" // Enabled;Disabled
choices "#str_07217" // Off;On
choices "#str_07317" // Simple;Enhanced
choices "#str_07314" // Enhanced;Default

Less general choice lists:

choices "#str_07241" // Off;Low;Medium;High
choices "#str_02948" // 32 bits;64 bits
choices "#str_07313" // Lowest;Low;Normal;Better;High;Highest
choices "#str_07305" // 1x;2x;4x;8x;16x
choices "#str_04231" // Off;2;4;8;16
choices "#str_menu_NoYesAdaptive" // No;Yes;Adaptive

A singleton, set in another control's event handler as part of disabling an inappropriate control:

set "MeleeAutoParry::choices" "#str_07306"; // Disabled

See tdm_base01.pk4/strings/all.lang for additional possibilities.

Values "values_string"

Optional semicolon-separated list of numeric values. If not given, defaults to empty. If given, it must have the same number of values as 'choices'. Individual values are floats, each paired with the same location within "choices". Values may be signed or unsigned integers or real numbers, in any numeric order. Examples:

values "0;1"
values "2;0"
values "0;1;4;2;3;5"
values "-1;22;23;24;25;26;27"
values "0.022;-0.022"

Binding to a CVar or GUI::Parameter

A choiceDef should be bound to a Cvar and/or a GUI:: Parameter. If both "cvar" and "gui" Properties are specified, then the value of the cvar will rule, but upon a change, both are updated. In practice, only a cvar or gui is specified, as follows.

Cvar "cvar_name" ®

Defines the choiceDef's Cvar. The Cvar's value may be numeric or string, and represents a selection from one of the lists of valid choices. (More about this below.)

Gui "gui_parameter_name"

Defines the choiceDef's GUI:: Parameter. Gui_parameter_name here skips the "gui::" prefix. The value will contain a numeric 0-based index into the list(s) of choices. This value is equal to that of the currentchoice property. It provides a way to pass a choice to the engine, without necessarily using an exposed individualized Cvar. (Arguably, you could accomplish the same thing with something like:

set "gui::myparam" "currentchoice"; // Then do a set "cmd"... call that will read myparam


The choiceDefs of TDM's core uses "gui" only 4 times (all without cvars and with choiceType 0), within tdm_gui01\guis\mainmenu_settings_gameplay.gui:

gui "ai_vision" // in choiceDef AIVision. Similarly in AIHearing, Lockpicking,  MeleeDifficulty

That last examples uses the gui to support complex control interdependencies


Related to the foregoing, currentchoice is the currently selected choice, as a zero-based integer index. No TDM core GUI currently uses this Property, though the parsing and corresponding C++ variable is implemented. Possibly intended to be a read-only variable, for event handler if-conditions.

Establishing the Source and Nature of Cvar Values

Choicetype type_number

Takes a value of 0 or 1, that determines the source of the value that gets passed to the specified cvar. Default is 0 (but TDM menus #defines a CHOICE_DEF that specifies 1 unless further overwritten).

With ChoiceType 0

The bound cvar's value always represents a zero-based index into the choices list. The value is the same as that of currentchoice and (if present) the bound gui.

Any "values" list is treated as just a comment for documentation.

With ChoiceType 1

The bound cvar's value comes from the appropriate value within a list, where the list is either:

  • "values" if defined (in which case the value is a float, most often an int); otherwise
  • "choices" (in which case the value is a human-readable string).

Other Choice Def Properties

Liveupdate bool

This Property is also available for editDefs and sliderDefs. If set to 1, the cvar (and gui parameter) is changed as the text is changed, and the text changes as the cvar changes. Otherwise only changes when "cvar read group_name" or "cvar write group_name" is sent.

This is always initialized to 1 (true) in TDM's classes for choiceDef, editDef, and sliderDef, and is never seen to be changed explicitly thereafter in any GUIs. Speculatively, if you wanted to design menus that used that had "Done" and "Cancel" buttons, you might use:

liveupdate 0

Unused in TDM?

Updategroup group_name.

Makes this choiceDef part of a named update-group. Within the GUI, editDefs and sliderDefs may also be part of the group, but that assignment is done with cvargroup "group_name". With a group established, all the group's guiDefs may be updated with a single cvar read/write command. For example, "cvar read audio" would update all controls of the "audio" group. Specifically, an update concerns the item bound to the "cvar" property and (for a choiceDef) "gui" property.

In the choiceDefs of mainmenu_settings_video.gui of TDM 2.10, this group is tagged:

updategroup render

However, there is no evidence that any cvar read/write commands were being issued anywhere. Likewise, no issuing of "systemCvars", a Session_menu.cpp command that in turn evokes "cvar read render" and "cvar read sound". Perhaps "systemCvars" is used for testing, or just a leftover from Doom 3.

(Choicevar "string" ®)

Undefined. Probably can be parsed in a GUI but not fully implemented. In the list of Registers in Windows.cpp of Doom 3 and TDM, and mentioned but not explained in Ref 1 of GUI Scripting: References & Resources.

Two Basic Examples

In TDM, choiceDefs are used in the mainmenu_settings_<submenu>.gui, where <submenu> is gameplay, video, _audio, or controls. Also mainmenu_quit and tdm_invgrid_parchment.gui (choiceDef Category).

Common #Defines

TDM main "Settings" menus widely use the following #define (in mainmenu_defs.gui), to style choice controls:

#define CHOICE_DEF  font CHOICES_FONT  textscale SETTINGS_FONT_SCALE_CHOICE  textalign 2  textalignx -10  forecolor SETTINGS_FONT_COLOUR  choiceType 1

Then CHOICE_DEF appears within any appropriate property list. But following that, any individual property can be overwritten. For instance, this is done to make "choiceType 0" the style for Settings/Gameplay/Difficulty items AI Vision, AI Hearing, Lockpicking, Melee Difficulty, and Melee Auto-Parry

AI Vision – Example of ChoiceType 0

This choiceDef, from mainmenu_settings_gameplay.gui and due to grayman, offers a range for 'AI Vision' so the player can adjust how sensitive the AI will be to spotting the player.

choiceDef AIVision
    rect         SETTINGS_X_OFFSET, 0, 80, MM_LINE_H
    choices      "#str_07323"  // Nearly Blind;Forgiving;Challenging;Hardcore
    values       "0;1;2;3"
    gui          "ai_vision"
    choiceType   0

       set "cmd" "log 'AI Vision changed by user.'";
       set "cmd" "updateAIVision";

Invert Mouse - Example of ChoiceType 1

This is from the Settings/Controls/General menu (and mainmenu_settings_controls.gui). ChoiceType 1 sets the cvar m_pitch to either 0.022 or -0.022. Here, there's no event handler to call the engine after the change; it's enough to set the cvar, which the mouse-motion code will read in subsequent frames.

choiceDef SCGeneralBind5
    rect         SETTINGS_X_OFFSET, 68, 80, MM_LINE_H
    choices      "#str_04221"  // No;Yes
    values       "0.022;-0.022"
    cvar         "m_pitch"

Additional Notes

  • If you want to delve into complex examples of control interdependencies, check out the GUIs and source code of either gameplay/melee or video controls.
  • Use of the keyboard alone to operate menus (e.g., those with choiceDef controls) is weak. While TAB and Shift-TAB appear to cause the expected change of focus between a menu's choiceDefs, they skip over sliderDefs, do not include top-level menu selection fields, unnecessarily invoke popups, etc.
  • Content here is primarily from Ref 1 of GUI Scripting: References & Resources, heavily augmented by inspection of TDM 2.10 source code and GUI files. Ref 3 has a conflicting meaning of what choiceType denotes (as simply "Boolean versus multiple choice"), but appears sketchy and less authoritative than usual, so may be discounted as describing a real difference between Doom 3 and Quake 4.