GUI Scripting: Syntax, Semantics, & Usage
From The DarkMod Wiki
This page is part of a series. See GUI Scripting Language for overview.
WORK IN PROGRESS
Introduction
This is a comprehensive listing of "rules of thumb" to successfully author a GUI. Much of this is also discussed elsewhere, but gathered here for convenience. Aspects that need attention because they are most likely to cause problems are in bold.
CAUTION: Some of these "rules" are based on behavioral and usage observations, in some cases generalizations made from scant experimental evidence. So future refinement may be in order.
Properties
Registers and Non-Registers - Types, Defaults, Scope of Access
- This "GUI Scripting" series treats "Properties" as a category including "Registers" and non-registers, as opposed to applying only to non-registers.
- Property names and meanings are predefined.
- A property has a predefined type, either float (including int and bool), string, vect4, or (rarely) vect2. This type must be respected during initialization and use.
- Properties and their values appear in a property list, by convention at the beginning of the body of a guiDef.
- By convention, each property (name and value) is on its own line. NO TRAILING ";"
- However, a group of properties may be on a single line, separated by spaces (not semicolons), as part of a #define.
- By convention, the "rect" register is first, and the "visible" register is second; thus these are not defaulted.
- Within a particular guiDef, if a property is not listed, it is still present, with a defined default value.
- Default values are generally float 0, vector of zeros, or empty string, but there are exceptions, so check the itemized descriptions.
- Properties that are Registers are true variables, that can be read or written throughout the GUI (taking scope into account).
- Properties that are non-Registers are more like tags. They can only be initialized, not read or written anywhere else. Specifically not in event handlers.
- The scope to access a register by its unprefixed name is the current guiDef only, including its event handlers, but not higher or lower levels of guiDef nesting.
- Within a given GUI, one guiDef can access another’s register with a "<guiDef-name>::" prefix.
Initializing
- Property initialization can be done from:
- a literal value
- a GUI:: Parameter (set by an associated .script function, entity spawnarg, or globally by the SDK)
- for non-strings, mathematical combinations of those, using common C-language operators and parenthetical nesting.
- Property initialization may be problematic from other local registers, those accessed by a "guiDef-name::" prefix, or user variables. If you need to have a register reflect the value of one of those sources, do it in a "set" command.
- When initializing a string property, the value MUST be in double-quotes. Examples:
text "Heading" font "fonts/stone" background "gui::BriefingVideoMaterial" // background string bound to and initialized by a script function of associated entity. text "gui::gui_parm1" // "gui_parm" prefix used so that text can be initialized from an associated entity’s spawnarg.
- When initializing a float (or variant such as int or bool) with a literal, the number is NOT quoted:
visible 0 // Boolean visible 1 // tabs used as separator textalign 2 // enum represented by int textscale 1.5
- However, if the value comes from a GUI:: Parameter (or one or more of them in a math expression), the source names are in double quotes:
visible "gui::LootIconVisible" visible 1*"gui::GridItem0_ItemStackable" visible "gui::Inventory_GroupVisible"*"gui::Inventory_ItemVisible" visible ("gui::WeaponNameVisible") visible ("gui::video_aspectratio" == 3) visible "gui::HUD_LightgemVisible" // This is a global GUI:: Parameter set by the SDK and mirroring a CVar
- When initializing a vector, use comma-separated numbers, with or without white space:
forecolor 1,1,1,1 // RGBA of text forecolor 0, 0, 0, 0.66 // with spaces too matcolor 1, 1, 1, "gui::HUD_Opacity" // alpha channel set by global GUI:: parameter in Settings #define S1 "gui::iconSize" rect 5*S1, (80 - 7 * "gui::Inventory_ItemNameMultiline")*S1, 110*S1, 22*S1 shear 0.5,0 // vect2
Scope of Influence
- In general, the values of properties affect only the layout and behavior of the area defined by the guiDef’s rectangle. But there are exceptions.
- For instance, "rect" is a relative location. So to calculate the absolute screen location, the parent’s guiDef’s absolute location must be known, calculated from its "rect". And so on recursively up the hierarchy.
- "Bordersize" draws pixels outside the "rect" dimensions.
- Properties "forceaspectx" and "forceaspecty" in some cases need to be set in the parent guiDef to take effect in a child guiDef.
User Variables
- You invent the name (unique within its guiDef) of a user variable. It should start with a letter and be composed of alphanumeric characters or underline. Avoid names of properties or other keywords.
- Syntax for "float" (or "definefloat") user variable is:
float winAlpha 0 float isDown; float crouch_scale=0;
- A float user variable is ALWAYS initialized to zero. Attempt to use any other number is ignored.
- It must be ended in either ";" or " 0". Ignore any documentation suggesting otherwise.
- The "definevect" user variable seems to be avoided, so maybe you should too.
- User variables are true variables, like registers.
- The scope to access a user variable by its unprefixed name is the current guiDef only, including its event handlers, but not higher or lower levels of guiDef nesting.
- Within a given GUI, one guiDef can access another’s user variable with a "<guiDef-name>::" prefix.
- The primary use case is as a Boolean, to control actions that should only be done once, or for processing across multiple event handlers.
Event Handlers
Unknown Identifiers
- An alphanumeric token that is unknown (such as a misspelled property or non-register used as if a register) will sometimes quietly be taken as a float with value 0, messing up your logic in an event handler. That’s the best case scenario.
MORE TO COME