Drop Down Menus for Simple Window Manager (SWM)

Share your scripts and packages with other Mudlet users.
Jor'Mox
Posts: 1101
Joined: Wed Apr 03, 2013 2:19 am

Drop Down Menus for Simple Window Manager (SWM)

Post by Jor'Mox »

Here is a little script I put together that makes and manages drop down menus using the Simple Window Manager Script (found here). Each menu has the necessary info provided in the menus table (any number of menus may be created, each with any number of menu items). In the provided example, clicking a button echos what menu item of which menu was clicked. To make the buttons do something, insert the appropriate code in the clickMenu() function (all buttons call this function, and pass the index of the menu the button is in, and the index of the button, uniquely identifying each button).

The menu items can be displayed on any side of the button for that menu by specifying position (top, bottom, left, or right), and can be arranged vertically (going up or down), or horizontally (going left or right) by specifying orient ("vertical down", "vertical up", horizontal right", "horizontal left").

Once everything is set, call initMenus() to create all the menu items and they will be ready to use.

As always, comments and suggestions are greatly appreciated.
Code: [show] | [select all] lua
local menus = {
	{
		title = {name = "menu1title", text = "Menu 1"},
		x = 0, y = 0, width = 100, height = 25, origin = "topleft",
		font = "Monaco", font_size = 12, fgColor = "white",
		bgColor = "black", border = "2px solid white", border_radius = "2px",
		button_names = {"menu1button1","menu1button2"},
		button_text = {"Button 1", "Button 2"},
		bwidth = 75, bheight = 20, position = "bottom", orient = "horizontal right"
	},
	{
		title = {name = "menu2title", text = "Menu 2"},
		x = 200, y = 0, width = 100, height = 25, origin = "topleft",
		font = "Monaco", font_size = 12, fgColor = "black",
		bgColor = "white", border = "2px solid green", border_radius = "5px",
		button_names = {"menu2button1","menu2button2","menu2button3"},
		button_text = {"Button 1", "Button 2", "Button 3"},
		bwidth = 75, bheight = 20, position = "right", orient = "vertical down"
	},
}

local menu_labels = {}

function initMenus()
	-- Create Main Menu Labels
	for k,v in ipairs(menus) do
		-- Store name in array
		menu_labels[k] = {title = v.title.name, shown = false}
		-- Create label
		createLabel(v.title.name,0,0,0,0,1)
		-- Add to window manager
		windowManager.add(v.title.name, "label", v.x, v.y, v.width, v.height, v.origin)
		-- Set style sheet info
		setLabelStyleSheet(v.title.name,[[
			background-color: ]] .. v.bgColor .. [[;
			border: ]] .. v.border .. [[;
			border-radius: ]] .. v.border_radius .. [[;]])
		-- Echo text
		echo(v.title.name,[[<span style="font-size: ]] .. v.font_size .. [[px" style="font-family: ']] .. v.font .. [['" style="color: ]] .. v.fgColor .. [["><b><center>]].. v.title.text ..[[</b></center></style>]])
		-- Set callback to open menu
		setLabelClickCallback(v.title.name,"showMenu",k)
		windowManager.show(v.title.name)
	end	
	
	for k,v in ipairs(menus) do
		local x, y = v.x, v.y
		-- Set starting position for menu items
		if v.position == "bottom" then y = y + v.height
		elseif v.position == "top" then y = y - v.bheight
		elseif v.position == "right" then x = x + v.width
		elseif v.position == "left" then x = x - v.bwidth
		end
		
		for k2, v2 in ipairs(v.button_names) do
			menu_labels[k][k2] = v2
			createLabel(v2,0,0,0,0,1)
			windowManager.add(v2, "label", x, y, v.bwidth, v.bheight, v.origin)
			setLabelStyleSheet(v2,[[
				background-color: ]] .. v.bgColor .. [[;
				border: ]] .. v.border .. [[;
				border-radius: ]] .. v.border_radius .. [[;]])
			echo(v2,[[<span style="font-size: ]] .. v.font_size .. [[px" style="font-family: ']] .. v.font .. [['" style="color: ]] .. v.fgColor .. [["><b><center>]].. v.button_text[k2] ..[[</b></center></style>]])
			setLabelClickCallback(v2, "clickMenu",k,k2)
			windowManager.hide(v2)
			if string.find(v.orient,"horizontal") then
				if string.find(v.orient,"left") then
					x = x - v.bwidth
				else
					x = x + v.bwidth
				end
			else
				if string.find(v.orient,"up") then
					y = y - v.bheight
				else
					y = y + v.bheight
				end
			end
		end
	end
end

function showMenu(menu)
	if not menu_labels[menu].shown then
		for k,v in ipairs(menu_labels[menu]) do
			windowManager.show(v)
		end
		menu_labels[menu].shown = true
	else
		hideMenu(menu)
	end
end

function hideMenu(menu)
	for k,v in ipairs(menu_labels[menu]) do
		windowManager.hide(v)
	end
	menu_labels[menu].shown = false
end

function clickMenu(menu, button)
	echo("You clicked button number: " .. button .. " in menu number: " .. menu .. ".\n")
	hideMenu(menu)
end
Edit: Tweaked so clicking on a menu button for an already open menu closes the menu.
Last edited by Jor'Mox on Tue Aug 27, 2013 12:30 am, edited 2 times in total.

User avatar
Vadi
Posts: 4901
Joined: Sat Mar 14, 2009 3:13 pm

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Vadi »

A few screenshots please? :)

Jor'Mox
Posts: 1101
Joined: Wed Apr 03, 2013 2:19 am

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Jor'Mox »

Image
Image
Image

While doing these, I just realized I need to make it so that if you click the main button for a menu that is open, it closes, right now it does nothing (technically it opens it again).

Jor'Mox
Posts: 1101
Joined: Wed Apr 03, 2013 2:19 am

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Jor'Mox »

If I have a little time later I'll turn this into a proper set of functions to create, style, and otherwise manage menus. Like I did with gauges. May be a bit though, I've been busy lately. But, if there are any suggestions on what to use for function names, or argument order, or whatever, I'm always glad to hear them (it means less fixes when Vadi tells me I have something mixed up later!)

fetaera
Posts: 191
Joined: Sat Aug 03, 2013 7:00 pm

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by fetaera »

These are crying out for some QLinearGradient love ;)

Jor'Mox
Posts: 1101
Joined: Wed Apr 03, 2013 2:19 am

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Jor'Mox »

Once they are created, you can apply whatever style sheets you want. As promised, I'm working on a more generic version to allow for easily created menus for anyone. I'll hopefully post my work in progress here later today, once I have the critical functions all finished.

Jor'Mox
Posts: 1101
Joined: Wed Apr 03, 2013 2:19 am

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Jor'Mox »

Here is a set of functions to create and manipulate drop down menus in a generic way. I tried to make the function names as similar to built in functions.

Available functions:
-- All arguments after height are optional. They default to "", 1, "bottom", "down".
createMenu(menuName, x, y, width, height, menuText, numButtons, buttonPosition, buttonDirection)
-- This function sets the stylesheet for the menu buttons. To set all buttons at once, omit the buttonNumber variable. To set the stylesheet for the main button, and a different stylesheet for the other buttons, omit the buttonNumber variable, then pass the stylesheet info for the main button, followed by the stylesheet info for the rest of the buttons.
setMenuStyleSheet(menuName, buttonNumber, buttonCSS)
-- This function sets the click callback for the menu buttons
setMenuClickCallback(menuName, buttonNumber, ...)
-- This function is used to set the text for the various menu buttons. It accepts color names, or rgb values for color. To set the main button text, omit the buttonNumber. Default color is "white".
setMenuText(menuName, text, buttonNumber, r, g, b)
-- This function changes the number of buttons in the menu
setMenuNumberButtons(menuName, numButtons)
-- This function changes the size of the buttons (if this function isn't called, the menu buttons will be the same size as the main button that opens and closes the menu)
setMenuButtonSize(menuName, width, height)
-- These functions change where the menu buttons appear relative to the main button, and in which direction they extend
setMenuPosition(menuName, position)
setMenuDirection(menuName, direction)
-- These functions open and close the menu (show or hide the menu items). If toggle is true for openMenu then it will open if it is closed and close if it is open (this is used to make clicking the main button work as expected)
openMenu(menuName, toggle)
closeMenu(menuName)
-- These functions act exactly like similar functions for windows
resizeMenu(menuName, width, height)
moveMenu(menuName, x, y)
hideMenu(menuName)
showMenu(menuName)
Code: [show] | [select all] lua
menusTable = menusTable or {}

function createMenu(menuName, x, y, width, height, menuText, numButtons, buttonPosition, buttonDirection)
	menuText = menuText or ""
	numButtons = numButtons or 1
	buttonPosition = buttonPosition or "bottom"
	buttonDirection = buttonDirecton or "down"
	
	assert(table.contains({"bottom","top","left","right"},buttonPosition), "createMenu: buttonPosition must be bottom, top, left, or right")
	assert(table.contains({"up","down","left","right"},buttonDirection), "createMenu: buttonDirection must be up, down, left, or right")
	
	local tbl = {open = false, width = width, height = height, x = x, y = y, text = menuText, numButtons = numButtons, buttonPos = buttonPosition, buttonDir = buttonDirection}
	-- save new values in table
	menusTable[menuName] = tbl
	
	createLabel(menuName,0,0,0,0,1)
	setBackgroundColor(menuName,0,0,0,255)
	setLabelClickCallback(menuName, "openMenu", menuName, "true")
	for k = 1,numButtons do
		createLabel(menuName.."_menubutton_"..k,0,0,0,0,1)
		setBackgroundColor(menuName.."_menubutton_"..k,0,0,0,255)
		setMenuText(menuName, "Button " .. k, k, "white")
		setLabelClickCallback(menuName.."_menubutton_"..k, "closeMenu", menuName)
		hideWindow(menuName.."_menubutton_"..k)
	end
	
	-- resize and move menu to desired position
	resizeMenu(menuName, tbl.width, tbl.height)
	moveMenu(menuName, tbl.x, tbl.y)
	-- write text on main menu button
	setMenuText(menuName, menuText, "white")
	showMenu(menuName)
end

function setMenuPosition(menuName, position)
	assert(menusTable[menuName], "setMenuPosition: no such menu exists.")
	assert(table.contains({"bottom","top","left","right"},position), "setMenuPosition: Position must be bottom, top, left, or right")
	menusTable[menuName].buttonPos = position
end

function setMenuDirection(menuName, direction)
	assert(menusTable[menuName], "setMenuDirection: no such menu exists.")
	assert(table.contains({"up","down","left","right"},direction), "setMenuDirection: Direction must be up, down, left, or right")
	menusTable[menuName].buttonDir = direction
end

function setMenuNumberButtons(menuName, numButtons)
	assert(menusTable[menuName], "setMenuNumberButtons: no such menu exists.")
	local curButtons = menusTable[menuName].numButtons
	menusTable[menuName].numButtons = numButtons
	if curButtons < numButtons then
		for k = curButtons + 1,numButtons do
			createLabel(menuName.."_menubutton_"..k,0,0,0,0,1)
			setBackgroundColor(menuName.."_menubutton_"..k,0,0,0,255)
			setMenuText(menuName, "Button " .. k, k, "white")
			setLabelClickCallback(menuName.."_menubutton_"..k, "closeMenu", menuName)
			hideWindow(menuName.."_menubutton_"..k)
		end
	elseif curButtons > numButtons then
		for k = numButtons + 1, curButtons do
			hideWindow(menuName.."_menubutton_"..k)
		end
	end
end

function setMenuStyleSheet(menuName, buttonNumber, buttonCSS)
	assert(menusTable[menuName], "setMenuStyleSheet: no such menu exists.")
	if tonumber(buttonNumber) then
		assert(buttonNumber <= menusTable[menuName].numButtons, "setMenuStyleSheet: buttonNumber higher than number of buttons in menu.")
		setLabelStyleSheet(menuName.."_menubutton_"..buttonNumber, buttonCSS)
	else
		buttonCSS = buttonCSS or buttonNumber
		setLabelStyleSheet(menuName, buttonNumber)
		for k = 1, menusTable[menuName].numButtons do
			setLabelStyleSheet(menuName.."_menubutton_"..k, buttonCSS)
		end
	end
end

function setMenuClickCallback(menuName, buttonNumber, ...)
	assert(menusTable[menuName], "setMenuClickCallback: no such menu exists.")
	assert(tonumber(buttonNumber) and buttonNumber <= menusTable[menuName].numButtons, "setMenuClickCallback: buttonNumber higher than number of buttons in menu.")
	setLabelClickCallback(menuName.."_menubutton_"..buttonNumber, ...)
end

function setMenuText(menuName, text, buttonNumber, r, g, b)
	assert(menusTable[menuName], "setMenuText: no such menu exists.")
	if r ~= nil then
		if g == nil then
			r,g,b = getRGB(r)
		elseif b == nil then
			r,g,b = buttonNumber,r,g
			buttonNumber = nil
		end
	elseif buttonNumber and not tonumber(buttonNumber) then
		r,g,b = getRGB(buttonNumber)
		buttonNumber = nil
	else
		r,g,b = 255,255,255
	end
	local echoString = [[<font color ="#]] .. RGB2Hex(r,g,b) .. [[">]] .. text .. [[</font>]]
	local labelName = menuName
	if buttonNumber then
		assert(menusTable[menuName].numButtons >= buttonNumber, "setMenuText: buttonNumber higher than number of buttons in menu.")
		labelName = labelName .. "_menubutton_" .. buttonNumber
		menusTable[menuName].buttonText = menusTable[menuName].buttonText or {}
		menusTable[menuName].buttonText[buttonNumber] = echoString
	else
		menusTable[menuName].text = echoString
	end
	echo(labelName, echoString)
end

function setMenuButtonSize(menuName, width, height)
	assert(menusTable[menuName], "setMenuButtonSize: no such menu exists.")
	assert(width and height, "setMenuButtonSize: need to have both width and height.")
	menusTable[menuName].bsize = {width = width, height = height}
	for k = 1, menusTable[menuName].numButtons do
		resizeWindow(menuName.."_menubutton_"..k, width, height)
	end
	if menusTable[menuName].open then
		openMenu()
	end
end

function resizeMenu(menuName, width, height)
	assert(menusTable[menuName], "resizeMenu: no such menu exists.")
	assert(width and height, "resizeMenu: need to have both width and height.")
	resizeWindow(menuName, width, height)
	if not menusTable[menuName].bsize then
		for k = 1,menusTable[menuName].numButtons do
			resizeWindow(menuName.."_menubutton_"..k, width, height)
		end
	end
	if menusTable[menuName].open then
		openMenu()
	end
	menusTable[menuName].width, menusTable[menuName].height = width, height
end

function moveMenu(menuName, x, y)
	assert(menusTable[menuName], "moveMenu: no such menu exists.")
	assert(x and y, "moveMenu: need to have both X and Y positions.")
	moveWindow(menuName, x, y)
	if menusTable[menuName].open then
		openMenu()
	end
	menusTable[menuName].x, menusTable[menuName].y = x, y
end

function openMenu(menuName, toggle)
	assert(menusTable[menuName], "openMenu: no such menu exists.")
	if toggle and menusTable[menuName].open then
		closeMenu(menuName)
	else
		menusTable[menuName].open = true
		local width, height = menusTable[menuName].width, menusTable[menuName].height
		local bwidth, bheight = width, height
		local x,y = menusTable[menuName].x, menusTable[menuName].y
		local pos, dir = menusTable[menuName].buttonPos, menusTable[menuName].buttonDir
		if menusTable[menuName].bsize then
			bwidth = menusTable[menuName].bsize.width
			bheight = menusTable[menuName].bsize.height
		end
		if pos == "left" then
			x = x - bwidth
		elseif pos == "right" then
			x = x + width
		elseif pos == "top" then
			y = y - bheight
		elseif pos == "bottom" then
			y = y + height
		end
		for k = 1, menusTable[menuName].numButtons do
			moveWindow(menuName.."_menubutton_"..k,x,y)
			resizeWindow(menuName.."_menubutton_"..k,bwidth,bheight)
			showWindow(menuName.."_menubutton_"..k)
			if dir == "left" then
				x = x - bwidth
			elseif dir == "right" then
				x = x + bwidth
			elseif dir == "up" then
				y = y - bheight
			elseif dir == "down" then
				y = y + bheight
			end
		end
	end
end

function closeMenu(menuName)
	assert(menusTable[menuName], "closeMenu: no such menu exists.")
	menusTable[menuName].open = false
	for k = 1, menusTable[menuName].numButtons do
		hideWindow(menuName.."_menubutton_"..k)
	end
end

function hideMenu(menuName)
	assert(menusTable[menuName], "hideMenu: no such menu exists.")
	hideWindow(menuName)
	for k = 1, menusTable[menuName].numButtons do
		hideWindow(menuName.."_menubutton_"..k)
	end
end

function showMenu(menuName)
	assert(menusTable[menuName], "showMenu: no such menu exists.")
	showWindow(menuName)
	if menusTable[menuName].open then
		for k = 1, menusTable[menuName].numButtons do
			showWindow(menuName.."_menubutton_"..k)
		end
	end
end

User avatar
Vadi
Posts: 4901
Joined: Sat Mar 14, 2009 3:13 pm

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Vadi »

If you'd like, we could set you up with a page on the Mudlet wiki - we've listed other scripts there previously. It'd be easier to maintain formatting there. Kudos on the project!

Jor'Mox
Posts: 1101
Joined: Wed Apr 03, 2013 2:19 am

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Jor'Mox »

Right the moment, I'm just trying to get the functions all set up so they will blend more or less seamlessly with native Mudlet functions. So far, everything seems to work, that I can tell anyway, though there may be a need for additional functions. Once it is finished, I will obviously incorporate it as an option in my simple window manager script, so that menu "objects" can be added and managed there as well.

However, while I'm sure a wiki space might be handy, I honestly don't know the first thing about working with wikis. Also, personally, I think my Simple Window Manager project is more interesting and potentially useful as an alternative to Geyser and Vyzor. Drop down menus, done in a neutral way such as this, is useful, but not especially complicated. I wrote everything in a day, and I would imagine that if there were a large demand for menus there would be scripts providing that functionality already.

If these menu functions end up being useful, I am trying to design them so that they could be incorporated into GUIUtils, rather than being a stand alone add on. They might need a bit more polishing, or having one or two additional functions added if I missed something, but as far as I can tell they are ready. Aside from needing a bit of documentation so that it is clearer how to use them.

User avatar
Vadi
Posts: 4901
Joined: Sat Mar 14, 2009 3:13 pm

Re: Drop Down Menus for Simple Window Manager (SWM)

Post by Vadi »

Okay, I agree with your way of thinking.

Post Reply