Jump to content

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

[function] ShowMenu 1.2


  • Please log in to reply
35 replies to this topic
majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
ShowMenu
Documentation in the code (to create HTML docs see this)


Download

Posted Image

Example:
ShowMenu("[MyMenu]`nitem1`nitem2`n-`nitem3) 
	ShowMenu("[MyMenu]`nitem1|item2|-|item3", "", "", "|")   ;alternative separator
return

MyMenu:
   msgbox 
return 

#include ShowMenu.ahk


Note:
- First time you call ShowMenu, it will create only those menus that are needed for particular menu to be shown (so you can keep all application menus in single defintion string, only those requested by the user will be builded). On next call, ShowMenu will check if menu by given name already exists, and use it if so. Otherwise new menu is builded. To force menu update, you must delete the menu or use another menu definition string, with different menu name.

Joy2DWorld Mod
Posted Image

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
version 1.1
- custom data after =
- mnu parameter can be blank to use first menu in the menu defintion string.
- test script for playing
Posted Image

trik
  • Members
  • 1317 posts
  • Last active: Jun 11 2010 11:48 PM
  • Joined: 15 Jul 2007
I love the fact that you can change the menu from the edit control :) Truly original :!:
Religion is false. >_>

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
v1.2
Added 2 new metachars
+ infront of item name will make item checked
* infront of item name will make item disabled

Added new helper function ShowMenu_Data to get the data associated with item (previously u had to manualy extract it from menu definition)
Posted Image

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Hi Maj,

Is this the new "MMenu"?
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Hej toralf, long time no see.

No, this is not new MMenu, although I am planning to finish MMenu project in next months.

This is just very convinient way to build menus from the text variable(s). You can use it with MMenu too, if you change it to use MMenu API instead AHK Menus.

This is perfect for any kind of application, from those having single menu to those having dozens of them. You can basicly store all your application menus in single file, and even let the user change that file for run-time menu customization.
Posted Image

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Jun 30 2014 07:48 PM
  • Joined: 04 Dec 2006
@majkinetor,


cool.


have some comments, maybe helpful ?


1) You sig/gif thing (whatever its' called) is FANTASTIC!! It's even 'personable'! Actually enjoy seeing it...


2) (I realize its a 'trend' now, but..) Do you really think its a great idea hiding your code in a rar/zip instead of actually posting the code in the forum where it can be (A) SEARCHED!!! AND FOUND based on code, and (B) QUICKLY SEEN and SHARE IDEAS AND SOLUTIONS --> without requiring download, save, open, and look to see code... ?


3) While its cool to see stuff like
if (c:=(*&s = 43)) or ((*&s=42) and c:=2)
			StringTrimLeft, s, s, 1
, am likely missing something but in my tests is 1/3 SLOWER than just using a regex, and TWICE AS SLOW as just using substr(s,1,1).

again, likely have missed something in my benchmarks,
basically:
benchmark("on")
loop 100000 {
/*

		s = *1333333333333
		if regexmatch(s,"s)\*(.*)",s)
			c = 2
/*
		s = *1
		if substr(s,1,1) = *
		{
			c = 2
			;StringTrimLeft, s, s, 1
			s := substr(s,2) ; FASTER!!!
		}
		/*
		*/
		s = *1
		if (c:=(*&s = 43)) or ((*&s=42) and c:=2)
			StringTrimLeft, s, s, 1
			 */
}
msgbox % benchmark()
exitapp

Joyce Jamce

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
hello

have some comments, maybe helpful ?

Always.

1) You sig/gif thing (whatever its' called) is FANTASTIC!! It's even 'personable'! Actually enjoy seeing it...

Thx. I really love to see it.

2) (I realize its a 'trend' now, but..) Do you really think its a great idea hiding your code in a rar/zip instead of actually posting the code in the forum where it can be (A) SEARCHED!!! AND FOUND based on code, and (B) QUICKLY SEEN and SHARE IDEAS AND SOLUTIONS --> without requiring download, save, open, and look to see code... ?

Well, its not a big deal. First, its faster to run it then to copy paste the code (Copy link doesn't work here (using Opera), its copied but scripts never execute, they show some weird errors, so I have to manualy select entire text and create new file, save it, bla bla...). This way, u can just download it and usualy say something like "extract here" and dbl click.

The bigest reason is that I like to have standard forms of postings. I usualy don't have just one script, but _Test.ahk file, module and documentation. Now, imagine I have to copy/paste all that sht around, save documentation too, etc... Its just multiple times faster to unpack the damn thing, ye ?

While its cool to see stuff like ...am likely missing something but in my tests is 1/3 SLOWER than just using a regex, and TWICE AS SLOW as just using substr(s,1,1).

I compard *&s with Substring(s,1,1) and they show here exactly the same run time, using Titan's benchmark function and A_Tickcount, since I don't beleive Titan that much :p. Both tests provided the same results - they are equaly fast.

Anyway, I had long time explaning to Titan why some things can be slower, perhaps I should repeat it here.

- What comes first is interface. If interface is bad, i don't want to use it, so it can be lighting fast and it means nothing to me.
- What comes second is speed. Interface may be great, but if module sucsk in speed, I wont use it. There are places where speed is important and not, and there is phenomena known as overoptimisation which actually makes your code unreadable by anybody else, even by you after some time. This function displays and handles menus. So, it will not display 1000 menus per second or menus that have zillions of items. Menus are generaly small windows, containing no more then 10 items (well, lets say it can be pretty large if u use submenus and dinamicaly created menus but again, not that big). Do you really think that even if you are right about speed, and you aren't, it will make any difference to the user ? He will click the button and get the menu imediately in optimal code and after 50ms in code that is not optimal (50ms = almost less then noticable by any leaving person). Hm.... Exacly the place where I would choose more readable & maintable code versus blazingly fast code (up to some normal measures, ofc)

BTW, u missed one thing in your benchmark - There is no c:=1 / c:=0 part :p. So there is one assigment less, probably makes all the difference :)


Thanks for your comments. :)

EDIT:
U missed 5 things in your benchmark. One is already said. Then, I check if first char of the string is + OR *. You check just *. Also, I take different actions if c=0 or 1 or 2 afterwads. In your case, this is totaly absent. Then, as my code checks first + then *, your actualy made my code execute one false comparison always, while in your code that doesn't happen. If you switch places to 42 and 43 that will be much faster.
Posted Image

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I rechecked your tests with this code:

t := A_TickCount
loop 100000 { 
	  s = *1333333333333 
      if c:=(*&s = 42)
			s := SubStr(s,2)
} 
t1 := A_TickCount - t


t := A_TickCount
loop 100000 { 
      s = *1333333333333

	  l := substr(s,1,1)
      if l=*
		c := 1, s := SubStr(s,2)
  	  else if l=+
		c := 2, s := SubStr(s,2)
	  else c := 0

}
t3 := A_TickCount - t
msgbox %t1% | %t3%
exitapp


Result is: 781 | 657
Some things to notice:

1. If you add (l="+") instead (l=+) and the same for * bottom code shows result 750 | 969 which shouldn't happen. This should be the same binary code, IMO at should execute equaly fast.

2. When you add "or ((*&s=43)" without c:=2 part (in first loop, as in my original code), result is 938 | 594
3. When you add to that c:=2 you get 1050 | 600

In other words, AHK is not handling correctly shortcut circuits, although I remember in doc that it should. c:=2 part should never get executed above since first value returns TRUE and that is enoguh to know outcome of complete exrepssion.


Even with that the code is much nice. It does make a difference in 100 000 loop sizes (which is probably AHK problem), but can not be noticed in Real Life (RL) with this case.
Posted Image

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
...

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Jun 30 2014 07:48 PM
  • Joined: 04 Dec 2006

The bigest reason is that I like to have standard forms of postings. I usualy don't have just one script, but _Test.ahk file, module and documentation. Now, imagine I have to copy/paste all that sht around, save documentation too, etc...


with you on that. Not suggesting to eliminate the Zip/Rar, but *also* to give a code block of the FUNCTION itself...

Many scripts you, Sean, Titan, etc. have posted, even not interesting in subject really, but a little trick in script or something AHK does that didn't realize, fun and helpful. All this is lost for everyone by hiding scripts in zip/rar.

I compard *&s with Substring(s,1,1) and they show here exactly the same run time

wait.. i check too...

ok, Titan is right, is actually about 10% faster in comparing x := ( (*&s) = 1)

it's the rest of the logic that slows it down. (for example, calling *&s twice instead of setting var to it's value and testing directly, perhaps [untested]

BTW, u missed one thing in your benchmark - There is no c:=1 / c:=0 part :P. So there is one assigment less, probably makes all the difference :)



likely...



for anyone if helpful,

did not spend much time with it,
just threw in quick mods to give:

(1) color menus ^]COLOR as menu item other than first (put *AFTER* submenus if you want subs also in that color)
(2) x,y positioning opional
(3) can use ++THIS ITEM++ and **NICE** as menu entries: +] as check -] as disables

new:
(4) default delim set at "|" (change back to `n if you want)
(5) fix alt delim logic bugs for _data function: ShowMenu_data(mdef,"","|")
(6) "?" option to get result in single call : ShowMenu(mdef,"?")
(7) sets last shown menu as default for _data function call : ShowMenu_data()
( 8) Keeps color on recall of existing menu
(9) "?!" option uses null default label (ie. you don't even need a label for menu & result", "-" for label option gets same result). ---> KIND OF COOL THING -- you can use showmenu("[x]|Opton 1=result 1|Option 2=result 2|etc = [sub1]|[sub1]|option x =z","?!") as a VALUE --> menu is shown and result value returned....
10) *] prefix for setting default item +] checked -] disabled
11] reworked optional function call only 2 params, menu structure, options ( needs options entered in order, but can include only those relevant:

? or = = return '=' lookup value
! = use null label

menuname = use this menu

<labelID> = use this label

(x,y) = put menu here x,y

{delim} = use this delim (ex. {`n}) | is default



12) [menuname] does NOT need a `n before or after... "[menu]|item 1 = a |item2 = b|[menu2]... etc." works fine. `n is OPTIONAL if you want it!
13) _] for dummy entery (helpful to add to submenu, eg: "[x]|_] anything=[sub1]|[sub1]|new item = new result"
14) > prefix for mDef to APPEND to existing menu.

enjoy.

note: @ majkinetor thanks for this script, finding it now *very* *very* handy!


have not benchmarked...


here is updated (Jan 1 08) HAPPY NEW YEAR! version,

adding "*" ability to auto delete menu after it is shown,
"%]" ability to use on the fly *any* menu command basically,
other bugs fixed.

very much have come to depend on this little function. Majkinetor, Thanks so much for this brilliant little function... it is *SUPER* useful
;-;------------------------------------------------------------------------------------------------
; Function:		ShowMenu
;				Show menu from the text.
;  
; J2DW mods:
; ©2008;  if you publish your source here,  use this freely;  if you use for personal use,  enjoy;  if you want to use this to distribute without sharing source code,  you must get written permission.
;
; prefix:
; +] to check item
; -] to disable item
; *] to make item the bold/default
; ^]COLORCODE (any place after first menu item added, I use it at end)
; ex; ^]red
; _] to make dummy (useful when adding to sub,  add 1 dummy = [sub] to main to invoke adding to sub) eg ."">[x]|_]dummy =[sub1]|[sub1]|Option new = new""
; %] to make command!  (ex: %]click,1|)
;
; prefix mDef with "*" to DELETE menu after shown
; prefex mDef with ">" to APPEND  new menus
; prefix mDef with "<" to build but not show menu
;
; showmenus(mdef,options)
;
;options: <LIST THEM IN ORDER>  
;'
;		? or = = return '=' lookup value  ||  ! = use null label  || menuname = use this menu  || <labelID> = use this label  || (x,y) = put menu here x,y  || {delim} = use this delim (ex. {`n}) | is default 



; prefix menu  to show with ?  to return selected value,  ?! to  use null label and return value
;  (also can use _sub of "-" to return null label)
;
; ShowMenu(mdef,"?") returns the = value for the selected menu item
; 	ShowMenu(mdef,"?!")  same w/ no need for label for menu
; 	ShowMenu(mdef,"!munx") same.. ie " ---> _sub is null label
;	ShowMenu(mdef,"?menx")  returns the selected value & shows menx
; 	
;
; ShowMenu_data now works with alt delim, eg. "ShowMenu_data(mdef,"{|}")"
; ShowMenu_data() now allowed,  returns result of last shown menu
;
; nice!
; Parameters: 
;				mDef	- Textual menu definition.
;				_mnu		- Menu to show. Label with the same name as menu will be launched on item selection.
;						  "" means first menu will be shown (default)
;				_sub		- Optional _subroutine that will override default label (named by menu)
;				_sep		- Optional _separator char used for menu items in menu definition, by default new line
;
; J2dW adds:
;
;				x - X position to show menu
;				y - Y position to show menu
;				
; Returns:      
;				Message describing error if it ocured or new line _separated list of created menus.
;				If return value is blank, ShowMenu just displayed menu already created in one of previous calls.
;
;	
; Remarks:
;				You must have in the code label with the same name as that given to the menu, otherwise
;				ShowMenu returns "No Label" error (unless you used "_sub" parameter in which case the same 
;				applies to that _subroutine). There must be no white space between menu name and start of the line.
;				Set each menu item on new line, use "-" to define _separator.
;
; Metachars:
;				To create *_submenu*, use "item = [_submenu]" notation where _submenu must exist in the textual 
;				menu definition. Referencing any particular menu as _submenu multiple times will work 
;				correctly, but circular references must be avoided.
;				To make item *checked*, use "+" as first character of its name, to make it *disabled* use "*".
;				To associated *user data* use "=data" after the item. If text after = doesn't contain valid
;				_submenu reference, it will be seen as user data. This also means that _submenu items can contain data.
;				To make menu definition more compact use something else then new line as item _separator
;				for instance "|" :
;>
;>					[_mnu1]
;>					item1|item2|item3|-|item4=[_mnu2]|item5
;>					[_mnu2]
;>					menu21 = menu21|menu22|menu23|menu24									  
;>				
;				You can then use this command to show the menu
;>					ShowMenu(mDef, "", "", "|")				;use first menu found and | as item _separator
;
; About:
;				v1.2 by majkinetor
;				See:  http://www.autohotkey.com/forum/topic23138.html
;

;example menu handler  A_ThisMenuItem has menu item chosen, ShowMenu_Data(eMenu) to get menu included key info
;OnMenu:
;	data := ShowMenu_Data(eMenu)
;	Tooltip %A_ThisMenu%  -  %A_ThisMenuItem%`nUser data: "%data%", 0, 0
;return
;



ShowMenu(mDef, options = "", r=0)   {  ; , r =0)

	; options in order "? or =  = return = result | ! = use null label for sub | name = menu to show | <label> = sub to call to process | (x,y)  = x,y coordinates to show menu | {delim}  ex: {`n}  to set alt delim to | (default)
	
	; eg:  menu, "?!"
	; menu,"?menu2<processmenu1>{`n}"
	
	
	;if r
	;	regexmatch(options,"(?<mnu>[\w#_@$?\[\]]*)>(?<sub>[\w#_@$?\[\]]*)>(?<mx>\d*)>(?<my>\d*)>(?<sep>.+)",_)
	
	;else if 
	if regexmatch(options,"S)^\s*"
					. "(?<Return_Result>[=?])?"
					. "\s*(?<Null_sub>\!)?"
					. "\s*(?<mnu>[\w#_@$?\[\]]+)?"
					. "\s*(?:<\s*(?<sub>[\w#_@$?\[\]]+)\s*>)?"
					. "\s*(?:\(\s*(<mx>\d*)\s*,\s*(<my>\d*)\s*\))?"
					. "\s*(?:\{(?<sep>.+?)\})?",_) {
					
					
		if _Null_sub and !_sub
			_sub = ShowMenu_Null
		if _return_result
			_return_result = =
		_mnu = %_Return_Result%%_mnu%

			
	}  
	
	if _sep =
		_sep = |


return ShowMenus(mDef, _mnu, _sub, _sep, _mx, _my , 0) 

}



ShowMenus( mDef, _mnu="", _sub="", _sep="|", _mx = "", _my = "", r=0 ) {
	global ShowMenu_activemenu,ShowMenu_activedelim, ShowMenu_Null ; ShowMenu_activemenuid,

	;Xebug( mDef "`n-->mnu= " _mnu " sub= " _sub )
	if (substr(_mnu,1,1) = "=") {
		;if (substr(_mnu,2,1) = "!") {
		;	_mnu := substr(_mnu,3)
		;	_sub = -
		;} else
			_mnu := substr(_mnu,2)
		_Return_Result = 1
	}

	;if _sub = -
	;	_sub = ShowMenu_Null
	
	if (substr(mDef,1,1) = "*") {
		mDef := substr(mDef,2)
		Kill_Menu = 1
	}
	if (substr(mDef,1,1) = ">") {
		mDef := substr(mDef,2)
		Add_Menu = 1
	}
	
	if (substr(mDef,1,1) = "<" ) {
		mDef := substr(mDef,2)
		Hide_Menu = 1
	}
	/*
	options := _mnu

	if _sub !=
		options .= "<" _sub ">"
		
	if (_mx . _my != "") 
		options .= "(" _mx "," _my ")"
		
	if _sep !=
		options .= "{" . _sep . "}"
*/


	;Xebug("options " options "`nReturn_result " _Return_Result "`nNull_sub" _Null_sub "`nmnu " _mnu "`nsub" _sub "`nx,y" _mx "," _my "`n sep=" _sep)				
	
	
	/*
	;global ShowMenu_X, ShowMenu_Y
	if (substr(_mnu,1,1) = "?") {
		if (substr(_mnu,2,1) = "!") {
			_mnu := substr(_mnu,3)
			_sub = -
		} else
			_mnu := substr(_mnu,2)
		_Return_Result = 1
	}

	if _sub = -
		_sub = ShowMenu_Null
		
	*/
	
	static p, menus
	if (!r)  {
		if (_mnu = "") and (substr(mDef, 1, 1) = "[")				;use first menu if _mnu = ""
			_mnu := substr(mDef, 2, InStr(mDef, "]")- 2)
		p := _sub="" ? _mnu : _sub, menus:=""							;set on function call (not on recursion step)
	}
	
	Menu, %_mnu%, UseErrorLevel, on
	Menu, %_mnu%, nostandard				;check if menu already exists
	;Xebug("error level after default test is " errorlevel " and _mnu is " _mnu)
	if !ErrorLevel and  !Add_menu  ; ie uuhhoo EXISTS 
		if !r and !Hide_menu {							;if this is first call, show the menu
			; restore color !
			;if regexmatch( _sep . mDef, "\Q" . _sep . "[" _mnu "]\E(?!\Q" _sep "[\E).*?\Q" _sep "^]\E([^\Q" _sep "\E]+)", color)
			;	Menu, %_mnu%, Color, %color%, single
			;Xebug("did not error on default,")
			ShowMenu_Null =
			if  _mx . _my =
				Menu, %_mnu%, Show
			else
				Menu, %_mnu%, Show, %_mx% , %_my%
				;if not in recursion, show
				;Menu, %_mnu%, Show, ShowMenu_X, ShowMenu_Y
			Showmenu_activemenu := mDef
			; Showmenu_activemenuid := _mnu
			ShowMenu_activedelim := _sep
			if Kill_menu
				menu, %_mnu%, Delete
			return ((_Return_Result and ShowMenu_Null) ? ShowMenu_data()  : "")
		
		} else return			; otherwise this is recursion step so just return

/*	
	if !r {														;if this is first call, show the menu
			
			if  _mx . _my =
				Menu, %_mnu%, Show
			else
				Menu, %_mnu%, Show, %_mx% , %_my%
				;if not in recursion, show
				;Menu, %_mnu%, Show, ShowMenu_X, ShowMenu_Y
			
			Showmenu_activemenu := mDef
			ShowMenu_activedelim := _sep
			return (_Return_Result ? ShowMenu_data()  : "")
		
	} 	
*/
; otherwise this is recursion step so just return	
	;Menu, %_mnu%, Color,											    ;check if menu already exists
	;if !ErrorLevel

	;Xebug("got here to p = " p)
	if !(r || IsLabel(p))
		return "No Label"

	if !(s := substr(mDef, 1, StrLen(_mnu)+2) = "[" _mnu "]" )		;start index
		s := InStr(mDef, _sep "[" _mnu "]")
	IfEqual, s, 0, Return "Menu not found"
	
	if !(e := InStr(mDef, _sep "[",false, s+1))						;end index
		e := StrLen(mDef)		

 	;if *(&mDef+s-1) = 10											;skip `n if on start
	;	s++
	s += Strlen(_mnu)+3, this := substr(mDef, s, e-s+1)				;extract menu def

	menus .= _mnu _sep  ; may need just `n ?? (insteapd of _sep)
	
	;debug("this is " this)
	Loop, parse, this, %_sep%, % (_sep = "`n" ? : "`n`r"  : "")
	{
		s := A_LoopField
		;Xebug(s)
		IfEqual, s, ,continue
		IfEqual, s,-,SetEnv,s,										;_separator
		;Xebug(RegExMatch(s, "(?<S>\=)?\s*\[(?<T>[^\]]+)\]\s*(?<M>\w+)?",ou) "`n" ous "`n" out "`n" ouM "is ouM",1,1)
		if  RegExMatch(s, "(?<S>\=)?\s*\[(?<T>[^\]]+)\]\s*(?<M>\w)?", ou) {	; has _submenu somewhere
			if  (j := ouS)  and  !ouM  ;check for _submenu def
				;Xebug( "got here"  out "-" _sub "-" 
				ShowMenus( (Add_menu ? ">" : "") . (Hide_menu ? ">" : "" ) . mDef, out , _sub, _sep, 0, 0 , 1 )
			; s := substr(s, 1, InStr(s,"=")-1),   
		} else
			j = 
			
		if k := InStr(s,"=")									;if it has = after it remove it
				s := substr(s, 1, k-1)
	
		if (substr(s,2,1) = "]") and  instr("+-*^_%=",(c := substr(s,1,1)) )
			s := substr(s,3)
		else
			c =
		if (c = "^") 
				menu, %_mnu%, color, %s% ; , single
		else if (c = "%") {
			if regexmatch(s,"([^,])*,([^,])*",part)
				Menu, %_mnu%, %part1%,%part2%
			else
				Menu, %_mnu%, %s%
		} else if c != _
		{
			;Xebug("adding " _mnu "," s ", j= " j "->" out "|" p)
			;Xebug(_mnu " is menu"  s "is item-->" (j ? ":" out : p))
			Menu, %_mnu%, Add, %s%, % j ? ":" out : p
			if c {
			IfEqual, c, +, Menu, %_mnu%, Check, %s%
			 IfEqual, c, -, Menu, %_mnu%, Disable, %s%
			 IfEqual, c, *, Menu, %_mnu%, Default, %s%
			 }
		}
		/*
		if (c:=(*&s = 43)) or ((*&s=42) and c:=2)
			StringTrimLeft, s, s, 1
		Menu, %_mnu%, Add, %s%, % j ? ":" out : p
		IfEqual, c, 1, Menu, %_mnu%, Check, %s%
		IfEqual, c, 2, Menu, %_mnu%, Disable, %s%
		*/

	}



;	if ShowMenu_Colors and regexmatch(showmenu_colors,"\b\Q" _mnu "\E\b\s*(?<r>\w+)",Colo)
;;	if regexmatch(_mnu,"_(?<r>\w+)$",Colo)
;;		menu, %_mnu%, color , %Color%,single
		;Xebug("THIS: " ShowMenu_Colors "- " Color  "--" _mnu,1,1)
	if  !( r or Hide_Menu) {
		;debug("showing normal way")
		;debug( _mnu)
		ShowMenu_Null = 
		if  _mx . _my =
			Menu, %_mnu%, Show
		else
			Menu, %_mnu%, Show, %_mx% , %_my%
		ShowMenu_activemenu := mDef
		; Showmenu_activemenuid := _mnu
		ShowMenu_activedelim := _sep
		if _Return_Result  
			menus :=   ShowMenu_Null ? showMenu_Data() : ""
		if Kill_menu
			menu, %_mnu%, Delete
	} 
	
	return menus
	
	ShowMenu_Null:
		ShowMenu_Null = 1
	return
}
;------------------------------------------------------------------------------------------------
; Function:		ShowMenu_Data
;				Get data associated with menu item
;  
; Parameters: 
;				mDef	- Textual menu definition.
;				item	- Menu item which associated data will be returned, if omited defaults to A_ThisMenuItem
;				
; Returns:      
;				Associated data or empty string if no data is associated with item.
ShowMenu_Data(mDef = "", item="", _sep = "|") {
	global Showmenu_activeMenu,Showmenu_activeDelim ;,Showmenu_activemenuid
	if (mDef = "") {
		mDef := ShowMenu_activemenu
		_sep := ShowMenu_activedelim
		; _mnu := Showmenu_activemenuid 
	}

	if item =
		if ! (item := A_ThisMenuItem)  ; and  ( _mnu = A_Thismenu)) 
			return

	;Xebug(mDef "-->" item "---->" _sep)
	mDef .= _sep
	j := InStr(mDef, item "=")
	IfEqual, j, 0, return 
	j += StrLen(item)+1
	return substr(mDef, j, InStr(mDef, _sep, false, j)-j)
}


if useful:

;------------------------------------------------------------------------------------------------
; Function:		ShowMenu
;				Show menu from the text.
;  
; J2DW mods:
;
; prefix:
; +] to check item
; -] to disable item
; *] to make item the bold/default
; ^]COLORCODE (any place after first menu item added, I use it at end)
; ex; ^]red
; _] to make dummy (useful when adding to sub,  add 1 dummy = [sub] to main to invoke adding to sub) eg ."">[x]|_]dummy =[sub1]|[sub1]|Option new = new""
;
; prefex mDef with ">" to APPEND  new menus
;
; showmenus(mdef,options)
;
;options: <LIST THEM IN ORDER>  
;'
;		? or = = return '=' lookup value  ||  ! = use null label  || menuname = use this menu  || <labelID> = use this label  || (x,y) = put menu here x,y  || {delim} = use this delim (ex. {`n}) | is default 



; prefix menu  to show with ?  to return selected value,  ?! to  use null label and return value
;  (also can use _sub of "-" to return null label)
;
; ShowMenu(mdef,"?") returns the = value for the selected menu item
; 	ShowMenu(mdef,"?!")  same w/ no need for label for menu
; 	ShowMenu(mdef,"!munx") same.. ie " ---> _sub is null label
;	ShowMenu(mdef,"?menx")  returns the selected value & shows menx
;
; ShowMenu_data now works with alt delim, eg. "ShowMenu_data(mdef,"{|}")"
; ShowMenu_data() now allowed,  returns result of last shown menu
;
; nice!
; Parameters: 
;				mDef	- Textual menu definition.
;				_mnu		- Menu to show. Label with the same name as menu will be launched on item selection.
;						  "" means first menu will be shown (default)
;				_sub		- Optional _subroutine that will override default label (named by menu)
;				_sep		- Optional _separator char used for menu items in menu definition, by default new line
;
; J2dW adds:
;
;				x - X position to show menu
;				y - Y position to show menu
;				
; Returns:      
;				Message describing error if it ocured or new line _separated list of created menus.
;				If return value is blank, ShowMenu just displayed menu already created in one of previous calls.
;
;	
; Remarks:
;				You must have in the code label with the same name as that given to the menu, otherwise
;				ShowMenu returns "No Label" error (unless you used "_sub" parameter in which case the same 
;				applies to that _subroutine). There must be no white space between menu name and start of the line.
;				Set each menu item on new line, use "-" to define _separator.
;
; Metachars:
;				To create *_submenu*, use "item = [_submenu]" notation where _submenu must exist in the textual 
;				menu definition. Referencing any particular menu as _submenu multiple times will work 
;				correctly, but circular references must be avoided.
;				To make item *checked*, use "+" as first character of its name, to make it *disabled* use "*".
;				To associated *user data* use "=data" after the item. If text after = doesn't contain valid
;				_submenu reference, it will be seen as user data. This also means that _submenu items can contain data.
;				To make menu definition more compact use something else then new line as item _separator
;				for instance "|" :
;>
;>					[_mnu1]
;>					item1|item2|item3|-|item4=[_mnu2]|item5
;>					[_mnu2]
;>					menu21 = menu21|menu22|menu23|menu24									  
;>				
;				You can then use this command to show the menu
;>					ShowMenu(mDef, "", "", "|")				;use first menu found and | as item _separator
;
; About:
;				v1.2 by majkinetor
;				See:  http://www.autohotkey.com/forum/topic23138.html
;

;example menu handler  A_ThisMenuItem has menu item chosen, ShowMenu_Data(eMenu) to get menu included key info
;OnMenu:
;	data := ShowMenu_Data(eMenu)
;	Tooltip %A_ThisMenu%  -  %A_ThisMenuItem%`nUser data: "%data%", 0, 0
;return
;



ShowMenu(mDef, options = "", r=0)   {  ; , r =0)

	; options in order "? or =  = return = result | ! = use null label for sub | name = menu to show | <label> = sub to call to process | (x,y)  = x,y coordinates to show menu | {delim}  ex: {`n}  to set alt delim to | (default)
	
	; eg:  menu, "?!"
	; menu,"?menu2<processmenu1>{`n}"
	
	
	;if r
	;	regexmatch(options,"(?<mnu>[\w#_@$?\[\]]*)>(?<sub>[\w#_@$?\[\]]*)>(?<mx>\d*)>(?<my>\d*)>(?<sep>.+)",_)
	
	;else if 
	if regexmatch(options,"S)^\s*"
					. "(?<Return_Result>[=?])?"
					. "(?<Null_sub>\!)?"
					. "\s*(?<mnu>[\w#_@$?\[\]]+)?"
					. "\s*(?:<\s*(?<sub>[\w#_@$?\[\]]+)\s*>)?"
					. "\s*(?:\(\s*(<mx>\d*)\s*,\s*(<my>\d*)\s*\))?"
					. "\s*(?:\{(?<sep>.+?)\})?",_) {
					
					
		if _Null_sub and !_sub
			_sub = ShowMenu_Null
		if _return_result
			_return_result = =
		_mnu = %_Return_Result%%_Null_sub%%_mnu%
			
	}  
	
	if _sep =
		_sep = |


return ShowMenus(mDef, _mnu, _sub, _sep, _mx, _my , 0) 

}



ShowMenus( mDef, _mnu="", _sub="", _sep="|", _mx = "", _my = "", r=0 ) {
	global ShowMenu_activemenu,ShowMenu_activedelim

	;Xebug( mDef "`n-->" _mnu)
	if (substr(_mnu,1,1) = "=") {
		if (substr(_mnu,2,1) = "!") {
			_mnu := substr(_mnu,3)
			_sub = -
		} else
			_mnu := substr(_mnu,2)
		_Return_Result = 1
	}

	if _sub = -
		_sub = ShowMenu_Null
	
	if (substr(mDef,1,1) = ">") {
		mDef := substr(mDef,2)
		Add_Menu = 1
	}
	
	/*
	options := _mnu

	if _sub !=
		options .= "<" _sub ">"
		
	if (_mx . _my != "") 
		options .= "(" _mx "," _my ")"
		
	if _sep !=
		options .= "{" . _sep . "}"
*/


	;Xebug("options " options "`nReturn_result " _Return_Result "`nNull_sub" _Null_sub "`nmnu " _mnu "`nsub" _sub "`nx,y" _mx "," _my "`n sep=" _sep)				
	
	
	/*
	;global ShowMenu_X, ShowMenu_Y
	if (substr(_mnu,1,1) = "?") {
		if (substr(_mnu,2,1) = "!") {
			_mnu := substr(_mnu,3)
			_sub = -
		} else
			_mnu := substr(_mnu,2)
		_Return_Result = 1
	}

	if _sub = -
		_sub = ShowMenu_Null
		
	*/
	
	static p, menus
	if (!r)  {
		if (_mnu = "") and (substr(mDef, 1, 1) = "[")				;use first menu if _mnu = ""
			_mnu := substr(mDef, 2, InStr(mDef, "]")- 2)
		p := _sub="" ? _mnu : _sub, menus:=""							;set on function call (not on recursion step)
	}
	
	Menu, %_mnu%, UseErrorLevel, on
	Menu, %_mnu%, nostandard				;check if menu already exists
	;Xebug("error level after default test is " errorlevel " and _mnu is " _mnu)
	if !ErrorLevel and  !Add_menu  ; ie uuhhoo EXISTS 
		if !r {							;if this is first call, show the menu
			; restore color !
			;if regexmatch( _sep . mDef, "\Q" . _sep . "[" _mnu "]\E(?!\Q" _sep "[\E).*?\Q" _sep "^]\E([^\Q" _sep "\E]+)", color)
			;	Menu, %_mnu%, Color, %color%, single
			;Xebug("did not error on default,")
			if  _mx . _my =
				Menu, %_mnu%, Show
			else
				Menu, %_mnu%, Show, %_mx% , %_my%
				;if not in recursion, show
				;Menu, %_mnu%, Show, ShowMenu_X, ShowMenu_Y
			Showmenu_activemenu := mDef
			ShowMenu_activedelim := _sep
			return (_Return_Result ? ShowMenu_data()  : "")
		
		} else return			; otherwise this is recursion step so just return

/*	
	if !r {														;if this is first call, show the menu
			
			if  _mx . _my =
				Menu, %_mnu%, Show
			else
				Menu, %_mnu%, Show, %_mx% , %_my%
				;if not in recursion, show
				;Menu, %_mnu%, Show, ShowMenu_X, ShowMenu_Y
			
			Showmenu_activemenu := mDef
			ShowMenu_activedelim := _sep
			return (_Return_Result ? ShowMenu_data()  : "")
		
	} 	
*/
; otherwise this is recursion step so just return	
	;Menu, %_mnu%, Color,											    ;check if menu already exists
	;if !ErrorLevel

	;Xebug("got here to p = " p)
	if !(r || IsLabel(p))
		return "No Label"

	if !(s := substr(mDef, 1, StrLen(_mnu)+2) = "[" _mnu "]" )		;start index
		s := InStr(mDef, _sep "[" _mnu "]")
	IfEqual, s, 0, Return "Menu not found"
	
	if !(e := InStr(mDef, _sep "[",false, s+1))						;end index
		e := StrLen(mDef)		

 	;if *(&mDef+s-1) = 10											;skip `n if on start
	;	s++
	s += Strlen(_mnu)+3, this := substr(mDef, s, e-s+1)				;extract menu def

	menus .= _mnu _sep  ; may need just `n ?? (insteapd of _sep)
	
	Loop, parse, this, %_sep%, % (_sep = "`n" ? : "`n`r"  : "")
	{
		s := A_LoopField
		;Xebug(s)
		IfEqual, s, ,continue
		IfEqual, s,-,SetEnv,s,										;_separator
		;Xebug(RegExMatch(s, "(?<S>\=)?\s*\[(?<T>[^\]]+)\]\s*(?<M>\w+)?",ou) "`n" ous "`n" out "`n" ouM "is ouM",1,1)
		if  RegExMatch(s, "(?<S>\=)?\s*\[(?<T>[^\]]+)\]\s*(?<M>\w)?", ou) 	; has _submenu somewhere
			if  (j := ouS)  and  !ouM  ;check for _submenu def
				;Xebug( "got here"  out "-" _sub "-" 
				ShowMenus( (Add_menu ? ">" : "") . mDef, out , _sub, _sep, 0, 0 , 1 )
			; s := substr(s, 1, InStr(s,"=")-1),   
		 if k := InStr(s,"=")									;if it has = after it remove it
				s := substr(s, 1, k-1)
	
		if ( (c := substr(s,1,2)) = "+]" ) or (c = "-]") or (c = "*]") or  (c = "^]")  
			s := substr(s,3)
			
		if (c = "^]") 
			;if substr(s,1,1) = "]"
			;	menu, %_mnu%, color, % substr(s,2)
			;else
				menu, %_mnu%, color, %s% ; , single
		else if c != _]
		{
			;Xebug(_mnu " is menu"  s "is item-->" (j ? ":" out : p))
			Menu, %_mnu%, Add, %s%, % j ? ":" out : p
			if c {
			IfEqual, c, +], Menu, %_mnu%, Check, %s%
			 IfEqual, c, -], Menu, %_mnu%, Disable, %s%
			 IfEqual, c, *], Menu, %_mnu%, Default, %s%
			 }
		}
		/*
		if (c:=(*&s = 43)) or ((*&s=42) and c:=2)
			StringTrimLeft, s, s, 1
		Menu, %_mnu%, Add, %s%, % j ? ":" out : p
		IfEqual, c, 1, Menu, %_mnu%, Check, %s%
		IfEqual, c, 2, Menu, %_mnu%, Disable, %s%
		*/

	}



;	if ShowMenu_Colors and regexmatch(showmenu_colors,"\b\Q" _mnu "\E\b\s*(?<r>\w+)",Colo)
;;	if regexmatch(_mnu,"_(?<r>\w+)$",Colo)
;;		menu, %_mnu%, color , %Color%,single
		;Xebug("THIS: " ShowMenu_Colors "- " Color  "--" _mnu,1,1)
	if ( r = 0 ) {
		if  _mx . _my =
			Menu, %_mnu%, Show
		else
			Menu, %_mnu%, Show, %_mx% , %_my%
		ShowMenu_activemenu := mDef
		ShowMenu_activedelim := _sep
		if _Return_Result 
			menus :=  showMenu_Data()
	} 
	return menus
	
	ShowMenu_Null:
	
	return
}
;------------------------------------------------------------------------------------------------
; Function:		ShowMenu_Data
;				Get data associated with menu item
;  
; Parameters: 
;				mDef	- Textual menu definition.
;				item	- Menu item which associated data will be returned, if omited defaults to A_ThisMenuItem
;				
; Returns:      
;				Associated data or empty string if no data is associated with item.
ShowMenu_Data(mDef = "", item="", _sep = "|") {
	global Showmenu_activeMenu,Showmenu_activeDelim
	if item =
		item := A_ThisMenuItem
	if (mDef = "") {
		mDef := ShowMenu_activemenu
		_sep := ShowMenu_activedelim
	}
	;Xebug(mDef "-->" item "---->" _sep)
	mDef .= _sep
	j := InStr(mDef, item "=")
	IfEqual, j, 0, return 
	j += StrLen(item)+1
	return substr(mDef, j, InStr(mDef, _sep, false, j)-j)
}



Joyce Jamce

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Jun 30 2014 07:48 PM
  • Joined: 04 Dec 2006

What is the point of setting "s" always to "*1333333333333" in the loops? You could use constants in the expressions...

was at the end of different play. initially tested just raw *& vs substr vs. regex, expected difference, didn't see. then tried one regex for ALL var tests (is fastest), then finally tried to simulate quickly directly from code.

not best test (maybe not even good test), but wanted general idea of code speed vs. options.

was expecting that *&x would be a super-fast alternative to substr(), which it wasn't...


unless, as is very possible, have missed something.


ps: @Laszlo, did not mention you in previous post, so mention: frequently learn much new and cool from your code as well....
Joyce Jamce

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Check out the above post.
I will check out your version soon. Thx for your code and ideas :)
Posted Image

Joy2DWorld
  • Members
  • 562 posts
  • Last active: Jun 30 2014 07:48 PM
  • Joined: 04 Dec 2006

Thx for your code and ideas :)


@majkinetor,


Thx for *your* code and script! Am finding your little script *VERY* handy!!
Joyce Jamce

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
I will probably add some or all of your ideas in "official code" when I find time (although I plan to stick with 1 char descriptors + -).

BTW, as you added more parameters to recursive function, I guess you can't expect your code to work faster then original as number of parameters influnce speed a lot.
Posted Image