Eric
I had tried that before, expecting it to work, but it appears ContextualMenus don’t work on the Mac. Extract from the manual: “ContextualMenus do not work with PopupMenu on macOS due to a limitation of the underlying macOS control that is used.”
So currently I am using a desktopMenuItem.popup in the MouseUp event - and having to backfill the result into the text of a blank first row so that it looks as if it has been selected. Not as pretty.
On my Mac (Ventura 13.2, Xojo 2022r4) - ConstructContextualMenu does not fire when clicking the control.
In your code, base is the root menuItem for the popup control, but you have assigned to it the result of your popMenu (base = popMenu.popUp). I’m not sure that would work as you indicate.
There’s probably a feedback somewhere for doing this, as the underlying macOS control will happily allow it, it’s just Xojo’s framework that sits atop won’t.
What you can do is to build a hierarchical menu of menu items, Then you grab [NSMenuitem submenu] from the base item, then use [NSPopupButton setMenu:] to set that menu onto the PopupMenu.
When I used it, I dynamically built the menu at open and used addHandler to capture the action event of each item. However I was using the PopupMenu in a “PullDown” form [NSPopupButton setPullsDown:], whereby selecting an item from the menu didn’t change the displayed label of the PopupMenu.
The declares for doing this is part of my App Kit, but are NOT compatible with DesktopControls. I’m sure the MVPs can provide you with a version or plugin that works with DesktopControls.
As Greg points out DesktopPopupMenu has method:
.addRow(item as desktopMenuItem)
and Event
.selectionChanged item as desktopMenuItem
However if I want to extract the associated menuItems from any row (e.g. to see if it has underlying hierarchy) I can’t. There appears to be no .menuAt method. All the methods return only a text value or a tag variant.
Why? So that I can do .selectRowWithTag for hierarchical menus.
Is there an obvious workaround or do I have to subclass to manage an array of menuItems to duplicate what is already there?
I keep a map of the hierarchical structure in an SQLite database and build the corresponding menu structure from that. If I want the “associated menuItems from any row”, then I get that direct from the database.
The same map also drives a listbox with the same structure. The user can drag rows around to re-order the listbox, which gets saved to the database so that, when the menu is opened again, it reflects the listbox structure.
Here is how I did it.
First you’ll need 2 functions
One to initially load the top level values of your hierarchy (LoadMenu)
One to update when the popupmenu value is changed (UpdateMenu)
Setup
One table (menu_table) with the menu structure: menu_id, menu_name, parent_id.
PopupMenu field is called popup_menu
parent_ids is a window property
menu_ids is a window property
db is the application database object
Function LoadMenu
dim parent_ids as new dictionnary
dim rs as rowset = db.SelectSQL("select * from menu_table")
while not rs.afterlastrow
parent_ids(rs.column("parent_id").stringvalue) = "0)
menu_ids(rs.column("menu_id").StringValue) = rs.column("menu_name").StringValue
rs.movetonextrow
wend
rs.movetofirstrow
dim i as integer
while not rs.AfterLastRow
if menu_id.HasKey(rs.Column("parent_id").StringValue)=false then
popup_menu.AddRow(rs.Column("menu_name").StringValue+"-->")
popup_menu.RowTagAt(i) = rs.Column("menu_id").IntegerValue
i=i+1
end if
rs.MoveToNextRow
wend
rs.close
Function UpdateMenu
// This function is called by an event handler on the popup_menu object on selection change. The input is "id as string" The call in popup_menu is:
// if Me.SelectedRowIndex <> -1 then
// UpdateMenu(Me.RowTagAt(Me.SelectedRowIndex))
// end if
dim selected_id as string = id
dim reset_cat as Boolean = false
'Check if requested a level up
if left(id,2) = "-2" then
dim previous_id as string = mid(id,3)
dim s_rs as RowSet = db.SelectSQL("select * from menu_table where menu_id=?", previous_id)
if menu_ids.HasKey(s_rs.Column("parent_id").StringValue) = true then
selected_id = s_rs.Column("parent_id").StringValue
else
'Requested top level
reset_cat = true
end if
end if
if reset_cat then
LoadMenu
else
'Check if new selection has children. If so, rebuild menu
if parent_ids.HasKey(selected_id)= true then
' Add the level up selection
popup_menu.RemoveAllRows
popup_menu.AddRow("--Return one level up")
popup_menu.RowTagAt(0) = "-2" + selected_id
' Add the current selection
popup_menu.AddRow(menu_ids.Value(selected_id).StringValue)
popup_menu.RowTagAt(1) = selected_id
' Add the children
dim rs as RowSet = App.empDB.SelectSQL("select * from menu_table where parent_id=?",selected_id)
dim j as Integer = 2
while not rs.AfterLastRow
if parent_ids.HasKey(rs.Column("menu_id").StringValue)=true then
popup_menu.AddRow("---"+rs.Column("category_name").StringValue+"-->")
else
popup_menu.AddRow("---"+rs.Column("category_name").StringValue)
end if
popup_menu.RowTagAt(j) = rs.Column("ebay_id").IntegerValue
j=j+1
rs.MoveToNextRow
wend
rs.close
end if
popup_menu.SelectRowWithTag(selected_id)
end if