GUI Scripting: If Statements: Difference between revisions
create this article |
m Tweaks to string comparison, #defined values |
||
(5 intermediate revisions by 2 users not shown) | |||
Line 3: | Line 3: | ||
The term "if statement" here is an abbreviated name for "if ... else if ... else ... statement". | The term "if statement" here is an abbreviated name for "if ... else if ... else ... statement". | ||
Its nature in GUI scripting is just enough like that of C-family languages to get you into trouble. In a nutshell, here’s guidance on problem areas: | Its nature in GUI scripting is just enough like that of C-family languages to get you into trouble. In a nutshell, here’s guidance on problem areas: | ||
* Curly brackets are not optional. | * Curly brackets are '''not''' optional. | ||
* A closing curly bracket is never followed by ";" | * A closing curly bracket is never followed by ";" | ||
* Each commands within curly brackets should be terminated by ";" | * Each commands within curly brackets should be terminated by ";" | ||
Line 9: | Line 9: | ||
** wrap variables in double-quotes. | ** wrap variables in double-quotes. | ||
** preferably surround any #defined macros with spaces. | ** preferably surround any #defined macros with spaces. | ||
** | ** know that '''expressions are evaluated early''', as the event block begins. This unusual treatment highly constrains the use of multiple-if logic. | ||
** two strings can't be compared, but there is a way to test if a string is empty. | |||
==Overall Structure and Syntax== | ==Overall Structure and Syntax== | ||
Line 26: | Line 27: | ||
} | } | ||
where the "else if" and "else" substructures are optional, and the "else if" substructure can be repeated any number of times. | where the "else if" and "else" substructures are optional, and the "else if" substructure can be repeated any number of times. But there are practical limitations due to early expression evaluation, discussed further below. | ||
The curly brackets can be on the same line as the "if", "else if", and "else", or separate. The degree of white space indentation doesn’t matter. It is a convention to use whitespace (i.e., space or newline) to separate the keywords ("if", "else if", "else) from the punctuation (parentheses and curly brackets). | The curly brackets can be on the same line as the "if", "else if", and "else", or separate. The degree of white space indentation doesn’t matter. It is a convention to use whitespace (i.e., space or newline) to separate the keywords ("if", "else if", "else) from the punctuation (parentheses and curly brackets). | ||
Line 55: | Line 56: | ||
if ("gui::flashbomb_halfblind_enum" >= 0.8) {.... | if ("gui::flashbomb_halfblind_enum" >= 0.8) {.... | ||
if ("gui::player_health" <= 0) {.... | if ("gui::player_health" <= 0) {.... | ||
===Problems due to Early Expression Evaluation=== | |||
As detailed in [[GUI Scripting: Evaluating Expressions & Variables]], for a particular event handler, the run-time values of all its if-conditions are calculated at the moment the handler starts to run, and not again until the handler ends. This "early evaluation" is uncommon among scripting languages. As a result, while you can write sequential ifs (perhaps interspersed with commands) and nested ifs within an event block, early evaluation will often limit their usefulness. See also: [[GUI_Scripting:_Syntax,_Semantics,_%26_Usage#Just-Changed_Value_Not_Immediately_Known_to_%22If%22_Expression | Just-Changed Value Not Immediately Known to "If" Expression]]. | |||
===String Comparison=== | ===String Comparison=== | ||
There is no true capability to compare strings, but you can determine whether a string (like property "text") is empty or not. The preferred form is: | |||
if ("LSGSaveGameNameEntry::text" " | if("text") {/* do this if non-empty */} else {/* do this if empty */} | ||
This | Since this form is not self-documenting, it is suggested you add a comment, indicating which are the non-empty and empty branches. Here's another example, referring to a property declared in a different guiDef from that of the if statement. | ||
if ("LSGSaveGameNameEntry::text") {... | |||
An older form, that worked by happenstance and is now deprecated and with a warning as of TDM 2.11, was: | |||
if("text" == "") {/* do this if empty */} else {/* do this if non-empty */} | |||
This worked because, when used in an expression, a string is always treated as a float, with value 0.0 if empty and 1.0 if non-empty. So with the deprecated form, "text" becomes 0 if empty or 1 otherwise. "" is seen as a variable with empty name, so replaced by 0 and then compared using "==". | |||
===With #Defined Values=== | ===With #Defined Values=== | ||
If you don’t see double quotes, e.g., | If you don’t see double quotes, e.g., | ||
if ( MM_BRIEFING_VIDEO_LENGTH_1 > 0 && MM_BRIEFING_VIDEO_LENGTH_2 == 0 ) | if ( MM_BRIEFING_VIDEO_LENGTH_1 > 0 && MM_BRIEFING_VIDEO_LENGTH_2 == 0 ) | ||
...it means a #define statement either incorporates the quotes around a variable name, or uses a numeric literal, where quotes are optional, e.g.: | ...it means a preceding #define statement either incorporates the quotes around a variable name, or uses a numeric literal, where quotes are optional, e.g.: | ||
#define MM_BRIEFING_VIDEO_LENGTH_1 10000 // In mainmenu_custom_defaults.gui & mainmenu_custom_defs.gui | #define MM_BRIEFING_VIDEO_LENGTH_1 10000 // In mainmenu_custom_defaults.gui & mainmenu_custom_defs.gui | ||
See [[GUI Scripting: Preprocessor Directives]] for more about #define statements and other macros. It helps to think of the textual substitution that macros do as occurring in a preprocessor step, before the main GUI parsing occurs. | |||
With a #defined item in your "if" condition, the macro parsing will probably thank you if you surround it with spaces, e.g.: | |||
if ( MM_BRIEFING_VIDEO_LENGTH_1 > 0 && MM_BRIEFING_VIDEO_LENGTH_2 == 0 ) | if ( MM_BRIEFING_VIDEO_LENGTH_1 > 0 && MM_BRIEFING_VIDEO_LENGTH_2 == 0 ) | ||
NOT: | NOT: | ||
if (MM_BRIEFING_VIDEO_LENGTH_1>0&&MM_BRIEFING_VIDEO_LENGTH_2==0) | if (MM_BRIEFING_VIDEO_LENGTH_1>0&&MM_BRIEFING_VIDEO_LENGTH_2==0) | ||
===Precedence | ===Precedence & Arithmetic Operations?=== | ||
No example of an if-condition in TDM using parenthetical grouping or simple arithmetic operators {+,-,*,/} was seen. It is presumed those are possible; such arithmetic is deployed elsewhere. | No example of an if-condition in TDM using parenthetical grouping or simple arithmetic operators {+,-,*,/} was seen. It is presumed those are possible; such arithmetic is deployed elsewhere. | ||
Line 80: | Line 90: | ||
rect MM_POS_NEW_MISSION_BUTTON | rect MM_POS_NEW_MISSION_BUTTON | ||
{{GUI}} | |||
Latest revision as of 20:30, 27 December 2022
This page is part of a series. See GUI Scripting Language for overview.
Introduction
The term "if statement" here is an abbreviated name for "if ... else if ... else ... statement". Its nature in GUI scripting is just enough like that of C-family languages to get you into trouble. In a nutshell, here’s guidance on problem areas:
- Curly brackets are not optional.
- A closing curly bracket is never followed by ";"
- Each commands within curly brackets should be terminated by ";"
- In if-conditions, within the parentheses:
- wrap variables in double-quotes.
- preferably surround any #defined macros with spaces.
- know that expressions are evaluated early, as the event block begins. This unusual treatment highly constrains the use of multiple-if logic.
- two strings can't be compared, but there is a way to test if a string is empty.
Overall Structure and Syntax
GUI scripting "if statements" are found in an event block (i.e., body of an event handler like onTime). The general form is:
if (boolean condition) { 1 or more valid statements terminated by ";" and executed only if condition was true. } else if (boolean conditionN) { 1 or more valid statements, each terminated by ";" and executed if all preceding conditions were false and conditionN is true. } else { 1 or more valid statements, each terminated by ";" and executed if all conditions were false. }
where the "else if" and "else" substructures are optional, and the "else if" substructure can be repeated any number of times. But there are practical limitations due to early expression evaluation, discussed further below.
The curly brackets can be on the same line as the "if", "else if", and "else", or separate. The degree of white space indentation doesn’t matter. It is a convention to use whitespace (i.e., space or newline) to separate the keywords ("if", "else if", "else) from the punctuation (parentheses and curly brackets).
There is NO semi-colon after any closing bracket "}".
CURLY BRACKETS ARE REQUIRED! So this is NOT OK:
if (boolean condition) command1; else command2;
If-Condition
This must resolve to a boolean value. This could come directly from a variable that only has 0 or non-zero (interpreted as 1) as values, or by a comparison, or by a conjunction of these separated by "&&" (i.e., logical AND) or "||" (i.e., logical OR) as in C.
Common Cases
All variables names MUST be in double quotes, e.g.:
if ("gui::lang_danish") {.... if ("gui::lang_spanish" == 0) {.... if ("gui::av_screenshot_download_in_progress" == "0" && "exit" == 0) {....
Most if-conditions in TDM are used to test gui:: parameters, as shown. The last example, from mainmenu_download.gui, also tests a user variable defined locally in the guiDef.
Tested variables are routinely of type float (representing boolean as above, or integer or real), and comparisons use numeric operators, e.g.:
if ("gui::curPage" < "gui::numPages") {
By convention, numeric literal values are UNQUOTED, although quotes are tolerated. Unquoted examples with various operators:
if ("gui::diffSelect" != 2) {.... if ("gui::numPages" > 1) {.... if ("gui::flashbomb_halfblind_enum" >= 0.8) {.... if ("gui::player_health" <= 0) {....
Problems due to Early Expression Evaluation
As detailed in GUI Scripting: Evaluating Expressions & Variables, for a particular event handler, the run-time values of all its if-conditions are calculated at the moment the handler starts to run, and not again until the handler ends. This "early evaluation" is uncommon among scripting languages. As a result, while you can write sequential ifs (perhaps interspersed with commands) and nested ifs within an event block, early evaluation will often limit their usefulness. See also: Just-Changed Value Not Immediately Known to "If" Expression.
String Comparison
There is no true capability to compare strings, but you can determine whether a string (like property "text") is empty or not. The preferred form is:
if("text") {/* do this if non-empty */} else {/* do this if empty */}
Since this form is not self-documenting, it is suggested you add a comment, indicating which are the non-empty and empty branches. Here's another example, referring to a property declared in a different guiDef from that of the if statement.
if ("LSGSaveGameNameEntry::text") {...
An older form, that worked by happenstance and is now deprecated and with a warning as of TDM 2.11, was:
if("text" == "") {/* do this if empty */} else {/* do this if non-empty */}
This worked because, when used in an expression, a string is always treated as a float, with value 0.0 if empty and 1.0 if non-empty. So with the deprecated form, "text" becomes 0 if empty or 1 otherwise. "" is seen as a variable with empty name, so replaced by 0 and then compared using "==".
With #Defined Values
If you don’t see double quotes, e.g.,
if ( MM_BRIEFING_VIDEO_LENGTH_1 > 0 && MM_BRIEFING_VIDEO_LENGTH_2 == 0 )
...it means a preceding #define statement either incorporates the quotes around a variable name, or uses a numeric literal, where quotes are optional, e.g.:
#define MM_BRIEFING_VIDEO_LENGTH_1 10000 // In mainmenu_custom_defaults.gui & mainmenu_custom_defs.gui
See GUI Scripting: Preprocessor Directives for more about #define statements and other macros. It helps to think of the textual substitution that macros do as occurring in a preprocessor step, before the main GUI parsing occurs.
With a #defined item in your "if" condition, the macro parsing will probably thank you if you surround it with spaces, e.g.:
if ( MM_BRIEFING_VIDEO_LENGTH_1 > 0 && MM_BRIEFING_VIDEO_LENGTH_2 == 0 )
NOT:
if (MM_BRIEFING_VIDEO_LENGTH_1>0&&MM_BRIEFING_VIDEO_LENGTH_2==0)
Precedence & Arithmetic Operations?
No example of an if-condition in TDM using parenthetical grouping or simple arithmetic operators {+,-,*,/} was seen. It is presumed those are possible; such arithmetic is deployed elsewhere.
For example, arithmetic is used to initialize vector values of the "rect" property, to position a button. First, in tdm_gui01.pk4\guis\mainmenu_defs.gui, boolean MM_POS_HASMOD_SHIFT is #defined:
#define MM_POS_HASMOD_SHIFT ("gui::hasCurrentMod" || "gui::inGame")
This in turn affects where the "New Mission" button is positioned vertically, by calculation:
#define MM_POS_NEW_MISSION_BUTTON MM_POS_X, MM_POS_Y + MM_POS_Y_STEP*(0+2*MM_POS_HASMOD_SHIFT), MM_POS_W, MM_POS_H
Then in tdm_gui01\guis\mainmenu_main.gui:
rect MM_POS_NEW_MISSION_BUTTON