ID:2203046
 
#define islist(L) (istype(L,/list))

var/list/menulist = list()
/datum/menu
var/name
var/list/children
var/ischeckgroup = FALSE //set to true to make this have checkboxes. First entry defaults to checked

/datum/menu/New()
var/ourentry = menulist[type]
if (islist(ourentry)) //some of our childern already loaded
children = ourentry
else
children = list()

menulist[type] = src

var/datum/menu/parent = menulist[parent_type]
if (!parent)
menulist[parent_type] = list(src)
else if (islist(parent))
menulist[parent_type] += src
else
parent.children += src

/datum/menu/proc/Generate_list(client/C)
. = list()
if (length(children))
for (var/thing in children)
var/datum/menu/child = thing
var/list/childlist = child.Generate_list(C)
if (childlist)
var/childname = "[child]"
if (childname == "[child.type]")
var/list/tree = splittext(childname, "/")
childname = tree[tree.len]
.[child.type] = "parent=[url_encode(type)];name=[url_encode(childname)]"
. += child.Generate_list(C)

var/checkdefault = FALSE
for (var/thing in typesof("[type]/verb"))
var/atom/verb/verbpath = thing
var/list/entry = list()
entry["parent"] = "[type]"
entry["name"] = verbpath.desc
if (copytext(verbpath.name,1,2) == "@")
entry["command"] = copytext(verbpath.name,2)
else
entry["command"] = verbpath.name

if (ischeckgroup)
if (!checkdefault)
entry["is-checked"] = TRUE
checkdefault = TRUE
else
entry["is-checked"] = FALSE

entry["can-check"] = TRUE
entry["group"] = "[type]"
entry["saved-params"] = "is-checked"
.[verbpath] = list2params(entry)

//the verb will be created and exist on the client.
/datum/menu/Example/verb/Example()
//set name = "" //if this starts with @ the verb is not created and name becomes the command to invoke.
set desc = "" //desc is the text given to this entry in the menu

/datum/menu/Icon/Size
ischeckgroup = TRUE

/datum/menu/Icon/Size/verb/iconstretchtofit()
set name = "@.winset \"mapwindow.map.icon-size=0\""
set desc = "&Auto (stretch-to-fit)"

/datum/menu/Icon/Size/verb/icon96()
set name = "@.winset \"mapwindow.map.icon-size=96\""
set desc = "&96x96 (3x)"

/datum/menu/Icon/Size/verb/icon64()
set name = "@.winset \"mapwindow.map.icon-size=64\""
set desc = "&64x64 (2x)"

/datum/menu/Icon/Size/verb/icon48()
set name = "@.winset \"mapwindow.map.icon-size=48\""
set desc = "&48x48 (1.5x)"

/datum/menu/Icon/Size/verb/icon32()
set name = "@.winset \"mapwindow.map.icon-size=32\""
set desc = "&32x32 (1x)"


/datum/menu/Admin/Generate_list(client/C)
if (C.holder)
. = ..()

/datum/menu/Admin/verb/Player_Panal()
set desc = "Open Player Panel"
...stuffhere...

/client/New()
. = ..()
var/list/topmenus = menulist[/datum/menu]
for (var/thing in topmenus)
var/datum/menu/topmenu = thing
var/topmenuname = "[topmenu]"
if (topmenuname == "[topmenu.type]")
var/list/tree = splittext(topmenuname, "/")
topmenuname = tree[tree.len]
winset(src, "[topmenu.type]", "parent=menu;name=[url_encode(topmenuname)]")
var/list/entries = topmenu.Generate_list(src)
for (var/child in entries)
winset(src, "[url_encode(child)]", "[entries[child]]")
if (!ispath(child, /datum/menu))
var/atom/verb/verbpath = child
if (copytext(verbpath.name,1,2) != "@")
new child(src)


This creates menu entries based on the path of the verbs, by just doing some subtype work.

No need to edit 2 files to add a menu item

Also much easier to manage in general.
I do something similar to this in most of my projects that use the top menu. +1