Hotstring Menus for Multiple Options

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jackdunning
Posts: 126
Joined: 01 Aug 2016, 18:17
Contact:

Hotstring Menus for Multiple Options

Post by jackdunning » 12 Nov 2019, 17:36

I have no idea who else has written this type of Hotstring menu script but I originally wrote this one as a Hotstring demo script. It allows the addition of single-key menu shortcuts and descriptive tags. I've tried to keep it simple for adapting to various uses.

HotstringMenu.ahk includes the Hotstring menu function HotstringMenu(TextList) and subroutine HotstringMenuAction. Originally discussed in the book, Beginning AutoHotkey Hotstrings: A Practical Guide for Creative AutoCorrection, Text Expansion and Text Replacement, you can include virtually any replacement text or characters in the pop-up menu Hotstrings.

The function and subroutine create an action menu of replacement options when activating the Hotstring. Call the function using the Hotstring Execute Option (X) in the form:

Code: Select all

:x*?:b``::HotstringMenu("🦄 | Unicorn &1
     ,🐀 | Rat &2,🐁 | Mouse &3
     ,🐂 | Ox &4,🐃 | Water Buffalo &5
     ,🐄 | Cow &6,❓ | Red &7
     ,❔ | White &8")
Image

Code: Select all

HotstringMenu(TextList)
{
  MenuItems := StrSplit(TextList, "`,")
  Loop % MenuItems.MaxIndex()
  {
    Menu, MyMenu, add, % MenuItems[A_Index], HotstringMenuAction
  }
  Menu, MyMenu, Show
  Menu, MyMenu, DeleteAll
}
 

HotstringMenuAction:
  InsertText := StrSplit(A_ThisMenuItem, "|")
  TextOut := StrReplace(RTrim(InsertText[1]), "&")
  SendInput {raw}%TextOut%%A_EndChar%
Return


; Sample Hotstring Menus

:x*?:$``::HotstringMenu("¢,£,¥,€")
:x*?:f``::HotstringMenu("⅒,⅑,⅛,⅐,⅙,⅕,¼,⅓,⅜,⅖,½,⅗,⅝,⅔,¾,⅘,⅚,⅞")
:x*?:s``::HotstringMenu("©|Copyright,®|Registered,™|Trademark,°,•,·,…,¶")
:x*?:m``::HotstringMenu("±,×,÷,≈,≅,∑,ƒ,¹,²,³")
:x*?:a``::HotstringMenu("⇐,⇔,⇒,⇑,⇓")
:x*?:b``::HotstringMenu("🦄 | Unicorn &1,🐀 | Rat &2
          ,🐁 | Mouse &3,🐂 | Ox &4,🐃 | Water Buffalo &5
          ,🐄 | Cow &6,❓ | Red &7,❔ | White &8")
:x:flux::HotstringMenu("Flux|&0,Flux #&1,Flux #&2")
In the included examples, most of the Hotstrings use the backtick ` to activate a menu. (You must escape the backtick ` with another backtick ``.)

The delimiting vertical bar makes possible the addition of descriptive tags and single-key shortcuts (e.g. &1, &2, &3, …) which won't appear in the replaced text.

For more information, see the following blogs:

AutoHotkey Hotstring Menus for Text Replacement Options November 4, 2019
Hotstring Menu Techniques for Inserting Symbols and Emojis November 11, 2019

December 9, 2019 Update

I've posted a rewritten version of the function which takes advantage of arrays, the variadic function parameter, and the boundfunc object.
Last edited by jackdunning on 09 Dec 2019, 15:44, edited 1 time in total.

User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Re: Hotstring Menus for Multiple Options

Post by Delta Pythagorean » 14 Nov 2019, 07:40

You can simplify this down by making the parameter variadic and letting the user chose which label to set the menu to work off of.

Code: Select all

::brb::HotStringMenu("MenuLabel", "Hello",, "Goodbye", "See you later!")

MenuLabel:
	MsgBox, % "You said: " A_ThisMenuItem
	Return

HotstringMenu(Handle, TextList*) {
	For Each, Item in TextList
		Menu, MyMenu, Add, % Item, % (Item != "") ? Handle : ""
	Menu, MyMenu, Show
	Menu, MyMenu, DeleteAll
}
Last edited by Delta Pythagorean on 18 Nov 2019, 16:16, edited 1 time in total.

[AHK]......: v2.0.12 | 64-bit
[OS].......: Windows 11 | 23H2 (OS Build: 22621.3296)
[GITHUB]...: github.com/DelPyth
[PAYPAL]...: paypal.me/DelPyth
[DISCORD]..: tophatcat


User avatar
jackdunning
Posts: 126
Joined: 01 Aug 2016, 18:17
Contact:

Re: Hotstring Menus for Multiple Options

Post by jackdunning » 17 Nov 2019, 16:42

Delta Pythagorean wrote:
14 Nov 2019, 07:40
You can simplify this down by making the parameter variadic and letting the user chose which label to set the menu to work off of.

Code: Select all

:brb::HotStringMenu("MenuLabel", "Hello",, "Goodbye", "See you later!")

MenuLabel:
	MsgBox, % "You said: " A_ThisMenuItem
	Return

HotstringMenu(Handle, TextList*) {
	For Each, Item in TextList
		Menu, MyMenu, Add, % Item, % (Item != "") ? Handle : ""
	Menu, MyMenu, Show
	Menu, MyMenu, DeleteAll
}
Certainly, something worth considering. It ends the need to parse the string and adds subroutine flexibility. Based upon this prompting, I'm currently working on a blog about variadic parameters.

A couple of notes:

The Hotstring line should be:

Code: Select all

:x:brb::HotStringMenu("MenuLabel", "Hello",, "Goodbye", "See you later!")
You don't need the ternary operator in the Menu command line since the For Each, Item in TextList command skips empty array values:

Code: Select all

For Each, Item in TextList
		Menu, MyMenu, Add, % Item, % Handle

User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Re: Hotstring Menus for Multiple Options

Post by Delta Pythagorean » 18 Nov 2019, 16:23

jackdunning wrote:
17 Nov 2019, 16:42
Delta Pythagorean wrote:
14 Nov 2019, 07:40
You can simplify this down by making the parameter variadic and letting the user chose which label to set the menu to work off of.
[...]
Certainly, something worth considering. It ends the need to parse the string and adds subroutine flexibility. Based upon this prompting, I'm currently working on a blog about variadic parameters.

A couple of notes:
The Hotstring line should be[...]
You don't need the ternary operator[...]
  • A. You're right on the hotstring option, I don't really use AHK for automation, I use it for multi-purpose coding (oddly enough).
  • B. I didn't test the code beforehand, so things would/wouldn't work differently than I thought, I just posted the code thinking it'd work, never knew the For operation skipped blanks.
  • C. The ternary line was for the blank parameter as to add a blank line, but I guess you could do some custom string like whitespace or something for a blank line.

[AHK]......: v2.0.12 | 64-bit
[OS].......: Windows 11 | 23H2 (OS Build: 22621.3296)
[GITHUB]...: github.com/DelPyth
[PAYPAL]...: paypal.me/DelPyth
[DISCORD]..: tophatcat


User avatar
jackdunning
Posts: 126
Joined: 01 Aug 2016, 18:17
Contact:

Associative Arrays and Menu Columns

Post by jackdunning » 22 Nov 2019, 16:28

Taking suggestions to heart, I found that I also needed to tailor the original Menu commands in the function so I added a third parameter.

Code: Select all

HotstringMenuV(MenuType,Handle,MenuArray*)
{
  For Each, Item in MenuArray
    If (MenuType = "A")         ; Add alphabetic single-key menu shortcuts
      Menu, MyMenu, Add, % "&" Chr(Each+96) " " Item, % Handle
    Else If (MenuType = "N")   ; Add numeric single-key menu shortcuts
      Menu, MyMenu, Add, % "&" Each "  " Item, % Handle
    Else If (MenuType = "T")    ; For use with associative arrays
      If (InStr(Item,"Brk"))      ; Add column breaks to long menus
        Menu, MyMenu, Add, % Each " | " StrReplace(Item,"Brk"), % Handle, +BarBreak
      Else
        Menu, MyMenu, Add, % Each " | " Item, % Handle
    Else    ; Default menu item. Use "" in calling function
      Menu, MyMenu, add, % Item , % Handle
  Menu, MyMenu, Show
  Menu, MyMenu, DeleteAll
}

; Additional menu Label subroutine for variadic function:

MenuShortcut:
  TextOut := SubStr(A_ThisMenuItem, 4)
  SendInput {raw}%TextOut%%A_EndChar%
Return

HotstringMenuAction:
  InsertText := StrSplit(A_ThisMenuItem, "|")
  TextOut := StrReplace(RTrim(InsertText[1]), "&")
  SendInput {raw}%TextOut%%A_EndChar%
Return
I plan a new blog on this topic next Monday (November 25, 2019).

Using the following associative array and the HotstringMenuV() function call, the function creates a three-column menu.

Image

Code: Select all

FractionsA := {⅒: "one-tenth",⅑: "one-ninth",⅛: "one-eight",⅐: "one-seventh",⅙: "one-sixth Brk",⅕: "one-fifth"
                              ,¼: "one-fourth",⅓: "one-third Brk",⅜: "three-eights",⅖: "two-fifths",½: "one-half",⅗: "three-fifths"
                              ,⅝: "five-eights",⅔: "two-thirds",¾: "three-fourths",⅘: "four-fifths",⅚: "five-sixths",⅞: "seven-eights"}

:x*:frct::HotstringMenuV("T","HotstringMenuAction",FractionsA*)
The term "Brk" tells the script to start a new column. The term appears in what seems to be odd locations due to the For Each, Item in MenuArray array sort.
Last edited by jackdunning on 01 Dec 2020, 16:01, edited 2 times in total.

User avatar
jackdunning
Posts: 126
Joined: 01 Aug 2016, 18:17
Contact:

Arrays, the Variadic Function Parameter, and the BoundFunc

Post by jackdunning » 09 Dec 2019, 15:38

As a major rewrite of Hotstring menus, this new HotstringMenuF() function combines arrays, the variadic function parameter, and the boundfunc object to create a cleaner, more functional structure for generating Hotstring replacement menus.

Image

The initial AutoExecute section defines sample arrays and launches the menu building functions:

Code: Select all

HotstringMenuAutoExecute:

Greetings := ["Hi!","Hello!","What's up?","How's it going?"]
FractionsA := {⅒: "one-tenth",⅑: "one-ninth"
              ,⅛: "one-eight",⅐: "one-seventh"
              ,⅙: "one-sixth Brk",⅕: "one-fifth"
              ,¼: "one-fourth",⅓: "one-third Brk"
              ,⅜: "three-eights",⅖: "two-fifths"
              ,½: "one-half",⅗: "three-fifths"
              ,⅝: "five-eights",⅔: "two-thirds"
              ,¾: "three-fourths",⅘: "four-fifths"
              ,⅚: "five-sixths",⅞: "seven-eights"}
ArrowsA := {⇐: "Left arrow",⇔: "Double arrow"
           ,⇒: "Right arrow",⇑: "Up arrow"
           ,⇓: "Down arrow"}
Bovines := {🐂: "Ox",🐃: "Water Buffalo",🐄: "Cow"
           ,🐄🐂🐃: "All three!"}

TopMenu := {FractionsA: "Fractions`tf``"
           , ArrowsA: "Arrows`ta``"
           , Bovines: "Bovines 🐂🐃🐄`tb``"
           , Greetings: "Greetings`tg``"}

TopMenu("MainMenu",TopMenu*)
Return

:x*:tp``::Menu, MainMenu, Show
:x*:a``::Menu, ArrowsA, Show
:x*:f``::Menu, FractionsA, Show
:x*:b``::Menu, Bovines, Show
:x*:g``::Menu, Greetings, Show

; Function for creating top menu and all submenus HotstringMenuF()
TopMenu(MenuName,MenuItems*)
{
  For Each, Item in MenuItems {
    HotstringMenuF(Each,%Each%*)
    Menu, %MenuName%, Add, % "&" A_Index " " Item, % ":" Each
  }
}

HotstringMenuF(MenuName,MenuArray*)
{
  ArrayLength := MenuArray.SetCapacity(0) ; Get array size
  For Each, Item in MenuArray {
    If (ArrayLength < 10)
    Shortcut := "&" . A_Index
  Else 
    Shortcut := "&" . Chr(A_Index+96)
  If (InStr(Item,"Brk")) ; Add column breaks to long menus
  {
    Item := StrReplace(Item,"Brk")
    Options := "+BarBreak"
  }
  Else 
    Options := ""

; Bind output data to the InsertFunction()
  Handler := Func("InsertFunction").Bind(Each,Item)

  If (Each = A_index) ; Simple array
    Menu, % MenuName, Add, % Shortcut " — " Item, % Handler
        , % Options
  Else
    Menu, % MenuName, Add, % Shortcut " " Each " — " Item
                , % Handler, % Options
  }
}

InsertFunction(Key,Item)
{
  If (Key ~= "^\d*$")
    SendInput {raw}%Item%%A_EndChar%
  Else
    SendInput {raw}%Key%%A_EndChar%
}
The new script, HotstringSubMenu.ahk:
  1. Implements a new function, TopMenu(), for creating a top-level menu containing the Hotstring submenus.
    (The script also sets up individual Hotstrings for bypassing the top-level menu and directly activating each submenu.)
  2. Uses the variadic function parameter for both the function definitions and function calls directly employing arrays by name.
  3. For persistence and ready access, the script sets up each menu at launch rather than recreating and destroying each on demand.
  4. The script automatically adds single-key shortcuts to each item in every menu.
  5. Long menus (over nine items) use letters rather than numbers as single-key shortcuts.
  6. Includes the option to use simple arrays for standard text or associative arrays for special characters or emojis (no spaces allowed).
  7. Employs two different tricks for identifying simple versus associative arrays.
  8. Takes advantage of a trick for obtaining the length of both simple and associative arrays.
See discussion at "Increase the Flexibility of Menus by Passing Data with the BoundFunc Object."

You can find this and earlier HotstringMenu.ahk scripts at the ComputorEdge Free AutoHotkey Scripts page.

User avatar
jackdunning
Posts: 126
Joined: 01 Aug 2016, 18:17
Contact:

Dynamic Submenus

Post by jackdunning » 13 Jan 2020, 15:12

I recently responded to a question in another thread about adding a dynamic date text insertion menu into Hotstring menus. I wrote and posted an example of how to use the DeleteAll parameter to remove and rebuild a submenu on-the-fly. As long as the script uses "Menu, SubMenu, DeleteAll" and NOT "Menu, SubMenu, Delete", the newly constructed submenu can replace the old submenu without relinking it to the main menu.

The problem with creating dynamic menus is the Menu structure is static. Once created, it cannot be changed. To create a dynamic display each time you use the menu you must first delete the old menu and create a new one.

As a test, I added my DateMenus.ahk functions to my HotstringSubMenus.ahk script.

Then, I altered the Hotstring for activating the menu to delete and regenerate the date/time menu:

Code: Select all

:x*:tp``::
	Menu, DateMenu, DeleteAll
	List := DateFormats(Date)
	TextMenuDate(List)
	Menu, MainMenu, Show
Return
I've posted a sample script called HotstringSubMenusTimeDate.ahk.

Image

Each time I activate the top-level menu with the Hotstring tp` (tp and the backtick key), the script deletes the old DateMenu and creates a new one—already linked to the top menu.

S_N
Posts: 10
Joined: 19 May 2019, 13:38

Re: Arrays, the Variadic Function Parameter, and the BoundFunc

Post by S_N » 30 Nov 2021, 13:47

jackdunning wrote:
09 Dec 2019, 15:38
script, HotstringSubMenu.ahk:
i tried to use this but when doing tp`` it just pastes this
Menu, MainMenu, Show

also, is it possible to define the menu in a separate file ?

thanks!!

Gio710
Posts: 31
Joined: 01 Mar 2022, 06:34

Re: Hotstring Menus for Multiple Options

Post by Gio710 » 31 Jul 2023, 05:56

Hi Jack,
I also tried to reach out on your blog, with the same request : Thanks for these great examples I was able to use and adapt. I’m looking for an additional feature. Would you mind telling me if you think it’s possible ? :

When using this script, is there a way to store in a variable the "path" chosen when using submenus, whatever the depth of the menu/submenus is ?

for example, if I choose :
- Bovine\Ox or
- Arrows\Left

Let's say each menu item be associated with an abbreviated name for each menu item, is there a way to store it in an output variable an abbreviated path like this :
- "Bv\Ox"
- "Ar\Lf"
Thanks in advance

Serge

Post Reply

Return to “Scripts and Functions (v1)”