Well, there may not be a "simple" solution in 3.5, but at least there does turn out to be a solution, that doesn't involve diving into C++ like the Engine does. Several people were right, it involves a MenuButton - and tons of code. I found a good example in this tool:
Material-Maker
Note that the current version it's updated to is Godot 4.2 (which has the MenuBar widget - I admit that simplifies things a lot, but for now I didn't want to switch from 3.5). If you wanted the sample code for 3.5, git-checkout the tag 1.3.
Like I said, it's a lot of code, that looks like this:
const MENU = [
{ menu="File/New material", command="new_material", shortcut="Control+N" },
{ menu="File/New paint project", command="new_paint_project", shortcut="Control+Shift+N", not_in_ports=["HTML5"] },
{ menu="File/Load", command="load_project", shortcut="Control+O" },
{ menu="File/Load material from website", command="load_material_from_website" },
{ menu="File/Load recent", submenu="load_recent", standalone_only=true, not_in_ports=["HTML5"] },
{ menu="File/-" },
{ menu="File/Save", command="save_project", shortcut="Control+S" },
...
func create_menu(menu_def : Array, object : Object, menu : PopupMenu, menu_name : String) -> PopupMenu:
var menu_item_name = menu_def[i].menu.right(menu_name_length)
var is_separator = false
if menu_item_name.find("/") != -1:
var submenu_name = menu_item_name.split("/")[0]
if ! submenus.has(submenu_name):
var submenu : PopupMenu
if menu.has_node(submenu_name):
submenu = menu.get_node(submenu_name)
else:
submenu = PopupMenu.new()
submenu.name = submenu_name
menu.add_child(submenu)
create_menu(menu_def, object, submenu, menu_name+submenu_name+"/")
menu.add_submenu_item(submenu_name, submenu.get_name())
submenus[submenu_name] = submenu
elif menu_def[i].has("submenu"):
But at least it works...