Popupmenu enhancements for very many entries
Problem
The popup menus in Igor are currently not usable for more than > 50 entries.
NewPanel
make/T/N=256/O data = num2char(97 + mod(p, 26)) + "_" + num2str(p)
string/G str = Textwavetolist(data, ";")
PopupMenu pop, value=str
Reasons
- User has to do too much scrolling for selecting an entry.
- When selecting an entry from very far off from the currently selected entry there is also a lot of scrolling involved too.
- Unresolved issues in multi monitor setups where no entries are shown at all
Proposed solution
Provide a mechanism for creating subgroups for the popup menus like in 1.
API
PopupMenu subMenu(SubMenuName)="a_.*", subMenu(...)=...
Inner working
All popupmenu list items matching the regular expression would be collected and moved into a submenu named SubMenuName
. The order of giving the keywords determines the order of collection for multiple submenus. No entry can be in more than one submenu. The position of the submenu can be set by adding an entry named SubMenuName
into the item list. There is no resorting done so the order of the entries is completely controllable by the user.
GUI control procedure
The submenus are transparent for the GUI procedure this means the list indizes passed into it are the linear indizes without using submenus. There is no event send when selecting a submenu entry.
Advanced usage
Recursion can be achieved by adding entries like SubMenuA#SubMenuB
. There SubMenuB
would be a submenu in SubMenuA
. The items for SubMenuB
would be collected just from the subset of items of SubMenuA
.
Alternative ways of specifying the list
There are cases where a regular expression is not the right choice. For these cases the user can specify a semicolon separated list of items for each submenu or one could invent new Special Characters in Menu Item Strings
specifiers for denoting a submenu name and to which submenu an item belongs.
--Jim Prouty
Software Engineer, WaveMetrics, Inc.
April 21, 2018 at 11:49 am - Permalink
Looks promising
PopupContextualMenu/ASYN=Callback "first;second;third;"
// (YourFunction continues...)
End
// Routine called when/if popup menu item is selected. selectedItem=1 is the first item.
Function Callback(String list, String selectedText, Variable selectedItem)
Print "Callback: ", list, selectedText, selectedItem
End
Function Setup()
string/G list = ""
variable i
make/T/N=26/O data = num2char(97 + mod(p, 26)) + "_" + num2str(p)
for(i = 0; i < 26; i += 1)
if(i == 25)
list = AddListItem("submenuA ➨", list, ";", Inf)
else
list = AddListItem(data[i], list, ";", Inf)
endif
endfor
Killwindow/Z panel0
newPanel/N=$"panel0"
Popupmenu pop, value=list, proc=PopMenuProc, bodyWidth=105
End
Function PopMenuProc(pa) : PopupMenuControl
STRUCT WMPopupAction &pa
switch(pa.eventCode)
case 2: // mouse up
Variable popNum = pa.popNum
String popStr = pa.popStr
if(!cmpstr(popStr, "submenuA ➨"))
YourFunction()
endif
break
default:
print pa.eventCode
endswitch
return 0
End
but is quite unintuitive as the user has to click the submenu and then also the main popup menu is hidden. This is contrary to the common way a submenu works (try out File->Recent Experiments).
April 27, 2018 at 01:14 pm - Permalink
Has this (or any other scheme for adding sub-menus to PopupMenus) been implemented in Igor 8?
Thanks.
January 8, 2019 at 09:34 am - Permalink
In reply to Has this (or any other… by BRUCE
It has not, no. Any changes like this would have to be in Igor 9, but no plans as of yet.
January 8, 2019 at 10:45 am - Permalink
This is how I would implement this:
SubMenu "Submenu A"
FakePopupMenuSubmenu("A")
End
Submenu "Submenu B"
FakePopupMenuSubmenu("B")
End
Submenu "Submenu C"
FakePopupMenuSubmenu("C")
End
End
Function/S FakePopupMenuSubmenu(String startsWith)
WAVE/T listTextWave
Variable n
Variable numInWave = numpnts(listTextWave)
String thisItem
String matchingItemsList = ""
startsWith = LowerStr(startsWith)
For (n=0; n < numInWave; n++)
thisItem = listTextWave[n]
if (CmpStr(LowerStr(thisItem[0]), startsWith) == 0)
// Add to the list
matchingItemsList = AddListItem(thisItem, matchingItemsList, ";", inf)
endif
EndFor
matchingItemsList = SortList(matchingItemsList, ";", 16)
return matchingItemsList
End
Function YourFunction()
PopupContextualMenu/N "FakePopupMenu"
print V_flag, S_selection
End
Function Setup()
Make/O/N=300/T listTextWave = num2char(97 + mod(p, 26)) + "_" + num2str(p)
Killwindow/Z panel0
newPanel/N=$"panel0"/K=1
Button button0,pos={3.00,4.00},size={200.00,20.00},proc=ButtonProc,title="\\JL▼ Fake Popup Menu"
End
Function ButtonProc(ba) : ButtonControl
STRUCT WMButtonAction &ba
switch( ba.eventCode )
case 2: // mouse up
YourFunction()
break
case -1: // control being killed
break
endswitch
return 0
End
This creates a button that simulates the popup menu, though as I have written it the text of the button does not change, unlike a PopupMenu. However you should be able to get that as well if you used dynamic text for the button's title, or if you force the title to change when the user selects an item. This also isn't quite the same user interface as a regular PopupMenu, but it's pretty close. Writing the menu definition is tedious, but shouldn't be difficult.
June 19, 2019 at 08:25 am - Permalink
Now that is nice! Thanks for the writeup! I'll use that as a basis.
June 26, 2019 at 12:07 pm - Permalink
And now I thought I'd share what we have done.
Attached is a screenshot. The menu used to be larger than my rotated 16:9 monitor and now look how nicely cascaded it is. The switch is a one line change as we have generic code to do handle that. You even don't have to change the popup menu procedure.
The code is available at https://github.com/AllenInstitute/MIES/blob/master/Packages/MIES/MIES_G….
May 2, 2020 at 11:05 am - Permalink