Ascii Menu


This is a simple console-based menu system. It demonstrates some interesting syntactic possibilities in Lua for defining the menu.

Source

-- [Download]

------------------------------------------------------------------
-- menu.lua
------------------------------------------------------------------
do
 local curriedMethod, method, meta = {}, {}, {}
 -- __index either executes a method from method or curries a method from
 -- curriedMethod with its self argument. This allows all calls to be with
 -- "." rather than ":" and also allows you to write obj.foo instead of
 -- obj.foo() for methods which don't require arguments. It might
 -- not be great design, but it is interesting. :)
 
 function meta:__index(key)
 local func = method[key]
 if func then
 return func(self)
 else
 func = curriedMethod[key]
 if func then
 local rv = function(a, b) return func(self, a, b) end
 self[key] = rv
 return rv
 end
 end
 end
 -- quick and dirty display routine.
 local DASHES = string.rep('-', 80)
 local DOUBLES = string.rep('=', 80)
 local function drawmenu(self)
 local maxsize = string.len(self.name) + 2
 local item = 0
 for i = 1, self.n do
 local sz = 6 + string.len(self[1][i])
 if maxsize < sz then maxsize = sz end
 end
 if maxsize > 75 then maxsize = 75 end
 local sepformat = " +%-"..maxsize.."."..maxsize.."s+\n"
 local nameformat = " | %-"..(maxsize - 2).."."..(maxsize-2).."s |\n"
 local itemformat = " | %2i. %-"..(maxsize - 6).."."..(maxsize-6).."s |\n"
 local sepline = string.format(sepformat, DASHES)
 io.write("\n", string.format(sepformat, DOUBLES))
 io.write(string.format(nameformat, self.name))
 io.write(string.format(sepformat, DOUBLES))
 for i = 1, self.n do
 if self[2][i] then
 item = item + 1
 io.write(string.format(itemformat, item, self[1][i]))
 else
 io.write(sepline)
 end
 end
 io.write(sepline)
 end
 -- Equally quick and dirty menu execution. Tail calls the function
 -- associated with the selected menu item.
 local function domenu(self)
 drawmenu(self)
 io.write("\n\nSelect a menu item: ")
 while true do
 local choice = io.read("*l")
 if choice == nil then return false end
 local _, _, item = string.find(choice, "^%s*(%d+)%s*$")
 if item then
 item = item + 0 -- force numeric conversion
 for i = 1, self.n do
 if self[2][i] then
 if item == 1 then return self[2][i]() end
 item = item - 1
 end
 end
 end
 io.write("\nSelection not valid. Try again: ")
 end
 end
 -- Create a new menu with given name and back reference.
 local function newmenu(name, back)
 return setmetatable({
 {}, {}, -- [1] is the menu label, [2] is the associated function
 name = name,
 back = back,
 n = 0
 },
 meta)
 end
 -- insert a label and a function at the end of a menu
 local function put(self, name, action)
 local n = self.n + 1
 self.n = n
 self[1][n] = name
 self[2][n] = action
 return self
 end
 -- Now the actual menu methods.
 -- add(label, id)
 function curriedMethod:add(name, id)
 return put(self, name, function() return id end)
 end
 
 -- I personally would use functions instead of ids
 curriedMethod.addf = put
 -- create and open a submenu with the given name
 function curriedMethod:sub(name)
 local submenu = newmenu(self.name .. " / " .. name, self)
 put(self, name.." -->", function() return domenu(submenu) end)
 return submenu
 end
 -- create a new, unrelated menu. You cannot use super afterwards
 function curriedMethod:new(name)
 return newmenu(name)
 end
 
 -- go back to the previous level, after introducing the automatic Back label
 -- unless this is a top-level menu
 function method:super()
 local mom = self.back
 if mom then
 put(self, "-")
 put(self, "<-- Back", function() return domenu(self.back) end)
 return self.back
 else return self
 end
 end
 -- insert a separator line
 function method:sep()
 return put(self, "-")
 end
 -- and a function to actually execute the thing
 curriedMethod.run = domenu
 -- Finally, we define the Menu "object"
 -- This is a bit of a kludge, because all menus respond to "new"
 -- in the same way. So you could actually just use Menu as your
 -- top-level menu.
 Menu = newmenu("")
end
-- OK, let's give it a whirl
local function about_dialog()
 io.write [[
 
 Menu system written by RiciLake in order to demonstrate
 some interesting syntactic possibilities in Lua
 This program is released into the public domain. But if you find
 it useful, you could certainly buy me a coffee sometime
]]
 return "About menu"
end
local ID_CAMPAIGN, ID_RANDOMMAP, ID_LOADGAME, ID_EXIT =
 "ID_CAMPAIGN", "ID_RANDOMMAP", "ID_LOADGAME", false
 
mainMenu = Menu.new "Main"
 .sub "New"
 .add("New Campaign", ID_CAMPAIGN)
 .add("New Random Map", ID_RANDOMMAP)
 .super
 .add("Load Game", ID_LOADGAME)
 .sep
 .addf("About", about_dialog)
 .sep
 .add("Exit", ID_EXIT)
while true do 
 local selection = mainMenu.run()
 if not selection then break end
 print("Selected: ", selection)
end

--RiciLake


RecentChanges · preferences
edit · history
Last edited December 8, 2007 12:01 pm GMT (diff)

AltStyle によって変換されたページ (->オリジナル) /