AutoHotkey Toggles and the Ternary Operator (Beginning Hotkeys Part 18)

Toggling AutoHotkey Switches, Plus How the Ternary Operator Adds If-Then-Else Conditional Shortcuts to a Single Line of Code

Last time, I demonstrated how to add power and flexibility to most AutoHotkey commands through the use of forced expressions. The Hotkey example which initiated the discussion came from an earlier version of the MousePrecise.ahk script:

SC052::Send % (toggle := !toggle) 
    ? "{LButton Down}" 
    : "{LButton Up}"

Note: The MousePrecise.ahk script is available at the ComputorEdge AutoHotkey download site.

ComputorEdge AutoHotkey E-Books*         *          *

This beginning Hotkey blog builds upon the discussions in the previous parts. If you find any of the information too confusing, then reviewing earlier blogs may be worthwhile.

New to AutoHotkey? See “Introduction to AutoHotkey: A Review and Guide for Beginners.”

*          *          *

This Hotkey activates with the zero key on the numeric keypad (keyboard Scan Code SD052) using the Send command to either hold down the left mouse button (LButton Down) or release the left mouse button (LButton Up)—depending upon the value of the variable toggle (true or false). The evaluation of the expression is forced (see the heading “Force an expression”) through the use of the single percent sign % followed by a space (or tab). The remainder of the line contains a three-part ternary expression.

Note: AutoHotkey reads the above statement as one line of code. Line continuation techniques have been implemented for display purposes only. When a question mark (?) or colon (:) precedes a line within the ternary operator, AutoHotkey merges it with the previous line.

This time we dig into the toggle technique for switching states on and off, plus take a closer look at the ternary conditional (If-Then-Else) shortcut.

AutoHotkey Toggles

A toggle is simply a switch for changing the state (e.g. on or off) or a condition (e.g. true or false) of variables or other AutoHotkey features. Its usefulness results from the easy resetting of a variable to its contrary value with one operation:

toggle := !toggle

When AutoHotkey evaluates this expression, the contents of the variable toggle resets to its opposite value (!toggle for not toggle). The first time the expression runs, the variable toggle does not exist, thus false. The Logical Not! operator returns its complementary value. In the case of a false or 0 variable value, true or 1 becomes the new value—and vice versa. This toggle technique creates a quick switch for any on/off condition.

Tip: Due to the different methods in AutoHotkey for assigning values to variables, when used in expressions, novices commonly confuse the set equal operator (:=) with the is equal comparison operator (=). In the creation of variables, either operator (:= or =) works fine for assigning values—as long as you use the proper syntax. For example, (from the online documentation) when assigning values with the equal sign (=) only:

MyNumber = 123
MyString = This is a literal string.
CopyOfVar = %Var%

When assigning with the colon equal sign (:=):

MyNumber := 123
MyString := "This is a literal string."
CopyOfVar := Var

However, when used in an expression, the two yield very different results. For example, for a toggle to work it must use the colon equal sign (:=) to set or reset a switch:

NumpadEnter::MsgBox % (Switch := !Switch) 
   ? "Switch is on!" 
   : "Switch is off!"

In this case, the value of Switch is change each time the expression is evaluated. If the sole equal sign mistakenly appears in the expression, the values are merely compared rather than set:

NumpadEnter::MsgBox % (Switch = !Switch) 
   ? "Switch is on!" 
   : "Switch is off!"

Since Switch is only compared to !Switch (not Switch), the expression always evaluates to false, regardless of the initial value of Switch. Be sure to use the colon equal sign (:=) when setting values (versus comparing values) inside an expression.

The Ternary Operator Creates Conditional Shortcuts

In the examples above, the ternary operator (?:) inserts a complete If-Then-Else conditional expression into one line of code. The term ternary operator translates to “composed of three parts.” Those three parts are separated by first a question mark ? following the If expression, then a colon : between the two possible results (If true and If false, respectively). From the online documentation:

var := x>y ? 2 : 3

AutoHotkey evaluates the expression before the question mark (x>y) ?. If true, the ternary assigns the first option (the 2 before the colon) to the variable var. If false, the second the ternary assigns second option (the 3 after the colon) to the variable var.

It’s important to note that the ternary operator does not include the first part of the line of code (var :=) which sets the value of the expression (the ternary operator) to var. For example, placing parentheses around the ternary shows how it can become part of a larger expression:

var := (x>y ? 2 : 3) + 2

Now when AutoHotkey evaluates the ternary, it adds 2 (or any other expression) to the result.

The ternary may be used multiple times within the same expression as long as parentheses are used appropriately:

var := (x>y ? (z>y ? 5 : 6) : 3) + 2

If the first comparison (x>y) evaluates true, then AutoHotkey evaluates the second ternary (in red) before adding the final 2. Otherwise, the expression assigns the value 5 (or 3 +2) to var. This technique replicates the same effect as nested If-Then-Else statements. However, the ternary does not provide better performance than the traditional If-Then-Else statement format. [See comment below from Groggy Ottor below.] The ternary primarily acts as a shortcut for inserting “either/or” options into a single line of code—such as those used in conjunction with forced expressions inside AutoHotkey commands (discussed last time).

How to Create A Toggling Hotkey Without the Ternary Operator

If you need more than two options or additional commands executed within a conditional, then use the simpler to understand and more readable traditional If-Then-Else set of statements.  Replace the ternary operator with traditional If-Then-Else conditional to make adding more features and operations easier while maintaining the toggle:

SC052::
  If (toggle := !toggle)
  {
    Send {LButton Down}
  }
  else
  { 
    Send {LButton Up}
  }
Return

Removing the forced expression within the Send command frees up the conditional for adding multiple commands. Ultimately, I replaced the initial Hotkey found at the beginning of this blog with:

SC052::
  if (toggle := !toggle)
  {
    Hotkey, NumpadEnter, on
    Send {LButton Down}
    Tooltip, Left Button Down`rNumpadEnter to cancel, 20,20
  }
  else
  { 
    Hotkey, NumpadEnter, off
    Send {LButton Up}
    Tooltip
  }
Return

Since there are multiple commands issued for both toggle and !toggle, the initial embedding of the forced ternary expression within the Send command must be abandoned in favor of traditional conditional statements which allow the addition of any number of AutoHotkey commands. (The rationale for these changes to the MousePrecise.ahk script are the subject of the next blog.)

Using Functions with the Ternary Operator

Suppose you want to use these sets of commands in more than one place in your script? You can turn the snippets into on and off functions available for calling wherever you need them. Then you can set up the Hotkey with a forced ternary expression calling the new functions:

SC052::% (toggle := !toggle) ? toggleon() : toggleoff()

toggleon()
  {
    Hotkey, NumpadEnter, on
    Send {LButton Down}
    Tooltip, Left Button Down`rNumpadEnter to cancel, 20,20
  }

toggleoff()
  { 
    Hotkey, NumpadEnter, off
    Send {LButton Up}
    Tooltip
  }

I can’t say that this function approach performs any better that the previous conditional format, but it does create functions reusable elsewhere in the script without repeating the code.

Next time, I plan to address the last of the additional features for the MousePrecise.ahk script:

  • Technique for releasing LButton block in other apps
  • Use a Tooltip as a reminder that the left mouse button is down.
  • Set up a quick release Hotkey to terminate LButton Down.

3 thoughts on “AutoHotkey Toggles and the Ternary Operator (Beginning Hotkeys Part 18)

  1. Jack, you can’t post false statements like this:
    “However, the ternary does not provide better performance than the traditional If-Then-Else statement format.”
    The ternary operator is inarguably faster than if/else and I encourage you to try this yourself.
    I recently switched the core logic of my JSON parser from If/Else to ternary and saw an incredible speed increase. To the point where I only use If/If Else/Else statements when absolutely necessary (Ex: If I must have a loop in the middle of a check).
    You can code entire blocks using nothing but ternary as long as you use proper parentheses, commas, and function calls.
    There is a very well done AHK forum post that covers script optimizations and they report that ternary performs FORTY PERCENT FASTER than if/else statements.
    Not sure if hyperlinks are allowed in these comments, so instead I’ll advise googling “AHK How to optimize the speed of a script as much as possible.” The first result should be the article in question. Pages 1 and 4 have tons of script speed gold in them.
    I hope you’ll consider correcting/updating this article. 🙂

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s