Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[function] CreateMenu


  • Please log in to reply
12 replies to this topic
Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

WARNING: This is old thread. It is continued here.

NOTES: This function was initially released in September 2010, and was totally rewritten and upgraded in May 2013. To see old, obsolete version, go to the end of this post, and expand spoiler. Here is the new version;
 

[function] CreateMenu
Creates menu from a string in which each item is placed in new line and hierarchy is defined by Tab character on the left (indentation). Item icon parameters (FileName | IconNumber | IconWidth) can be added after one or more Tab characters after item name. To create separator, specify at least 3 minuses (---) but follow indentation.
 
License: Free for commercial and non-commercial use if you provide appropriate credits to the author (Learning one) and put a link to this thread. Author is not responsible for any damages arising from the use of this function. Use it at your own risk.
 
Function:


CreateMenu(MenuDefinitionVar, MenuName="MyMenu", MenuSub="MenuSub") {	; by Learning one. Creates menu from a string in which each item is placed in new line and hierarchy is defined by Tab character on the left (indentation). Item icon parameters (FileName | IconNumber | IconWidth) can be added after one or more Tab characters after item name. To create separator, specify at least 3 minuses (---) but follow indentation.
	MenuNames := {}, ItemNames := {}, Items := {}, BaseMenuName := MenuName, Depth := 0, LastLevel := 1
	Loop, parse, MenuDefinitionVar, `n, `r
	{
		if A_LoopField is space							; ignore
			continue
		ItemInfo := RTrim(A_LoopField, A_Space A_Tab), ItemInfo := LTrim(ItemInfo, A_Space), Level := 1		; refine, reset
		While (SubStr(ItemInfo,1,1) = A_Tab)			; get current Level and ItemInfo
			Level += 1,	ItemInfo := SubStr(ItemInfo, 2)
		RegExMatch(ItemInfo,  "([^`t]*)([`t]*)([^`t]*)", match)		; match1 = ItemName, match3 = ItemIconOptions
		ItemName := Trim(match1), ItemIconOptions := Trim(match3)	; ItemIconOptions exa:  %A_AhkPath%|1|48
		if (IsObject(Items["L" Level]) = 0)				; if object which will contain items for this level doesn't exist yet
			Items["L" Level] := [], Depth += 1			; create it, and increase Depth of this menu
		if (Level=1)
			MenuName := BaseMenuName					; Exa Level1: "MyMenu"
		else
			MenuName := MenuNames["L" Level-1] "_" ItemNames["L" Level-1]	; Exa Level2: "MyMenu_Edit", Exa Level3: "MyMenu_View_Zoom"
		MenuNames["L" Level] := MenuName, ItemNames["L" Level] := ItemName	; store last
		Items["L" Level].Insert([MenuName, ItemName, MenuSub, ItemIconOptions])	; Insert new item object in this level. Item structure: [MenuName,ItemName,LabelOrSubmenu,ItemIconOptions]
		if (Level > LastLevel)	; this is the 1. item in submenu - set this menu as last higher item's submenu
			Max := Items["L" Level-1].MaxIndex(), Items["L" Level-1][Max].3 := ":" MenuName	; set this menu as last higher item's submenu. Exa: ":MyMenu_Edit"
		LastLevel := Level	; store last level
	}
	Loop, % Depth
	{
		Level := (A_index = 1) ? Depth : Depth - A_Index + 1		; from reverse - from deepest to highest level
		For k,v in Items["L" Level]	; v = item. Item structure: [MenuName,ItemName,LabelOrSubmenu,ItemIconOptions] ; MsgBox % v.1 "`n" v.2 "`n" v.3 "`n" v.4
		{
			if (SubStr(v.2, 1, 3) = "---")	; this is a separator
				Menu, % v.1, Add			; Menu, MenuName, add
			else {							; normal item
				if (v.4 = "")				; no ItemIconOptions
					Menu, % v.1, Add, % v.2, % v.3	; Menu, MenuName, add, ItemName, LabelOrSubmenu
				else {						; with ItemIconOptions. v.4 structure: FileName|IconNumber|IconWidth
					ItemIconOptions := v.4	; Exa: %A_AhkPath%|1|48
					Transform, ItemIconOptions, Deref, % ItemIconOptions	; deref things like %A_AhkPath%, %A_ScriptDir%, etc.
					StringSplit, param, ItemIconOptions, |
					Menu, % v.1, Add, % v.2, % v.3													; Menu, MenuName, Add, ItemName, LabelOrSubmenu
					Att := FileExist(param1), Att := (Att = "") ? "" : (InStr(Att, "D") > 0) ? "folder" : "file"	; check is param1 a file
					if (Att="file")
						Menu, % v.1, Icon, % v.2, % Trim(param1), % Trim(param2), % Trim(param3)	; Menu, MenuName, Icon, ItemName, FileName [, IconNumber, IconWidth]
					param1 := "", param2 := "", param3 := ""	; clear for next loop
				}
			}
		}
	}
}	; http://www.autohotkey.com/board/topic/57647-function-createmenu/

Example 1 - basic


MenuDefinitionVar=
(
File
	New
	Open
	Save
	Save as...
	Exit
Edit
	Undo
	Cut
	Copy
	Paste
	Delete
View
	Full screen
	Zoom
		100
		200
Help
)

CreateMenu(MenuDefinitionVar)
Menu, MyMenu, show
return

MButton::Menu, MyMenu, show

MenuSub:
MsgBox,, You selected:, % "Item:`n" A_ThisMenuItem "`n`nFrom menu:`n" A_ThisMenu
return

Example 2 - icons, separators (---), custom menu name, custom menu label (subroutine)


MenuDefinitionVar=
(
AHK						%A_AhkPath%|1
	Suspend				%A_AhkPath%|-206
	Pause				%A_AhkPath%|-207
	Suspend and pause	%A_AhkPath%|-208
	---
	Big exe				%A_AhkPath%|1|48
	Big file			%A_AhkPath%|2|48
Favorites				C:\Your icon.ico
	File 1
	File 2
	---
	Folder 1
	Folder 2
Help					C:\Your icon.ico||32
)

CreateMenu(MenuDefinitionVar, "Test", "TestSub")
Menu, Test, show
return

MButton::Menu, Test, show

TestSub:
MsgBox,, You selected:, % "Item:`n" A_ThisMenuItem "`n`nFrom menu:`n" A_ThisMenu
return

Example 3 - attach a menu to the window


MenuDefinitionVar=
(
AHK						%A_AhkPath%|1
	Suspend				%A_AhkPath%|-206
	Pause				%A_AhkPath%|-207
	Suspend and pause	%A_AhkPath%|-208
	---
	Big exe				%A_AhkPath%|1|48
	Big file			%A_AhkPath%|2|48
Favorites
	File 1
	File 2
	---
	Folder 1
	Folder 2
View
	Full screen
	Zoom
		100
		200
Help
)

CreateMenu(MenuDefinitionVar, "GuiMenu", "GuiMenuSub")
Gui 1:Menu, GuiMenu	; attach a menu to the window
Gui 1:Add, Text, x5 y5 w300 h100 hwndhgText
Gui 1:Show,, Select me...
return

GuiMenuSub:
GuiControl, 1:, % hgText, % "Item:`n" A_ThisMenuItem "`n`nFrom menu:`n" A_ThisMenu
return

GuiClose:
ExitApp

See also: [function] CreateTreeView


Old, obsolete version:

Spoiler


Delusion
  • Members
  • 272 posts
  • Last active: Jul 13 2014 09:04 PM
  • Joined: 16 Jul 2008
nice! im surpised nobody has replied to this
good job!
QuickSubs | Popcorn Movie Catalog
All my scripts are just in AutoHotkey v1.0.48.05

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Related: ShowMenu
Posted Image

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
@Delusion: Thanks. :)

Raccoon
  • Members
  • 178 posts
  • Last active: Oct 06 2014 05:58 PM
  • Joined: 02 Jan 2008
Does this function take into consideration the ability to create a menu item containing %A_Tab% in the text? eg; Exit%A_Tab%Alt+F4

Edit:
It's also worth nothing that different text editors behave differently. This script will behave strangely in editors that have auto-intending, use spaces instead of chr(0x09) when Tab is pressed, or that convert tabs into spaces.

Perhaps a + sign would be a better symbol to parse?
Posted Image

Need help right away? Get live support on IRC.
Already have an IRC client installed? /join #ahk

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

Does this function take into consideration the ability to create a menu item containing %A_Tab% in the text? eg; Exit%A_Tab%Alt+F4

Yes.

It's also worth nothing that different text editors behave differently. This script will behave strangely in editors that have auto-intending...

I use SciTE4AutoHotkey by fincs. No problems there...

Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

Function is totally rewritten and upgraded - see first post.


My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


klownboy
  • Members
  • 87 posts
  • Last active: May 24 2014 11:21 AM
  • Joined: 20 Jul 2012

Hi Learning one,

Your script is very good.  I've been testing your examples.  I'm most interested in the style menu of Example 2.  It's unclear to me since I'm still relatively new at AHK, what the best way to input the exe file to run a menu item in example 2.  For example, say your "File 2" (2nd tier menu item) under "Favorites" is the utility, CCleaner and I want the "CCleaner.exe" to run upon hitting that menu item.  Does it have to be accomplished in a sub menu(s) using the variables "A_ThisMenu" and "A_ThisMenuItem" with "If" statements.  I suppose I have the same question for a level 1 tier menu item (e.g., you want another program to run when you press the "Help" on the 1st level of menus).  It would really help if you could expand Example 2 a little bit to clarify the best way accomplish this. 

Thanks again,

Ken



DataLife
  • Members
  • 1022 posts
  • Last active: Nov 27 2015 01:09 AM
  • Joined: 27 Apr 2008

I have a project that has been on hold for quite some time. I plan on rewriting the whole script, which will include creating Menus.

 

I just tried [function] CreateMenu

 

Extremely easy to understand. I was able to modify your example with my Menu items and icons.

 

thanks for sharing this function.


Check out my scripts.  (MyIpChanger) (XPSnap) (SavePictureAs) All my scripts are tested on Windows 7, AutoHotkey 32 bit Ansi unless otherwise stated.

DataLife
  • Members
  • 1022 posts
  • Last active: Nov 27 2015 01:09 AM
  • Joined: 27 Apr 2008

Hi Learning one,

Your script is very good.  I've been testing your examples.  I'm most interested in the style menu of Example 2.  It's unclear to me since I'm still relatively new at AHK, what the best way to input the exe file to run a menu item in example 2.  For example, say your "File 2" (2nd tier menu item) under "Favorites" is the utility, CCleaner and I want the "CCleaner.exe" to run upon hitting that menu item.  Does it have to be accomplished in a sub menu(s) using the variables "A_ThisMenu" and "A_ThisMenuItem" with "If" statements.  I suppose I have the same question for a level 1 tier menu item (e.g., you want another program to run when you press the "Help" on the 1st level of menus).  It would really help if you could expand Example 2 a little bit to clarify the best way accomplish this. 

Thanks again,

Ken

Yes, you are correct. You would use If Statements under the TestSub label in example 2.

See if this code helps you understand a little.

TestSub:
if A_ThisMenuItem = Suspend
 {
	MsgBox run Suspend code
	MsgBox and more Suspend code
 }

if A_ThisMenuItem = Big exe
 {
	MsgBox run Big exe code
	MsgBox and More Big exe code
 }

if A_ThisMenuItem = Help
 {
	MsgBox run Help code
	MsgBox and More Help code
 }
return

You could also use Gosub, or Goto under your if statements to go to a different label.


Check out my scripts.  (MyIpChanger) (XPSnap) (SavePictureAs) All my scripts are tested on Windows 7, AutoHotkey 32 bit Ansi unless otherwise stated.

klownboy
  • Members
  • 87 posts
  • Last active: May 24 2014 11:21 AM
  • Joined: 20 Jul 2012

Hi DataLife and Learning one,

  Thank you for the suggestions DataLife and the menu code Learning one.  I started making up my menu and it's running and looking great.  For a little clarity in the layout, under the main menu sub, I direct the script using gosubs and the variable "A_ThisMenu" with "if" statements to separate catagories, ToolsSub for "Tools", VideoSub for "Video", and GraphicsSub for "Graphics", and then under each category use if statements and the variable "A_ThisMenuItem" to run the exe files.  The layout this way is easier to follow since it's follows the layout of the actual menu. but could it be a little slower since I'm directing the code through two variables instead of one.  What do you think?

Thanks,

Ken



Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
Hi DataLife and klownboy, I'm glad you like it.
 

but could it be a little slower since I'm directing the code through two variables instead of one


Negligible slower or not slower at all, and in some cases it may be even faster. happy.png Don't bother with this.
But instead of if blocks, as DataLife demonstrated above, I would use if + else if.
Tip - sometimes you'll want to shorten ThisMenu (A_ThisMenu) variable. Here's how you can do it;
ThisMenu := "MyMenu_Videos_Movies"	; long
ThisMenu := RegExReplace(ThisMenu,"(.*)_(.*)$", "$2")
MsgBox % ThisMenu			; short (result "Movies")

My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


klownboy
  • Members
  • 87 posts
  • Last active: May 24 2014 11:21 AM
  • Joined: 20 Jul 2012
Hi Learning one, I changed my scripts to use "if" and "else if" as you said, and I'll try the your technique for shortening the "A_ThisMenu" for a long menu item. The script is working very well and was quite easy to set up.
Thanks for the tips,
Ken