This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
EmTeaKay's profile picture

[Programming][Help] Making a menu

Started by EmTeaKay, 14 September 2012 - 02:25 AM
EmTeaKay #1
Posted 14 September 2012 - 04:25 AM
So, I need help making a menu. Nothing complex, just a three option menu. I know there's a tutorial here: http://www.computercraft.info/forums2/index.php?/topic/744-a-quick-guide-through-menu-making/ but I can't get that to work.
All I need is the code and an explanation and I'll be able to figure it out.
Bubba #2
Posted 14 September 2012 - 04:50 AM
Menus can be be simple or complex depending on how you want the menu to behave. Here is a simple one though.


local menuOptions = {"Option1", "Option2", "Option3"} --Put your menu items into this table
local termX, termY = term.getSize() --Get the terminal size
local selected = 1 --The selected menu item
function centerText(text, termY) --Write text to the center of the x axis
term.setCursorPos(termX/2-#text/2,termY)
term.write(text)
return true
end
function start()
while true do
  term.clear() --Rewrite the menu each time the user does something
  for i,v in ipairs(menuOptions) do --For each of the items in menuOptions do this
   if i == selected then --If the index of the menuItem is selected, draw it with "[ ]" around it
	centerText("[ "..v.." ]", i)
   else
	centerText(v,i)
   end
  end
  x,y = os.pullEvent() --Get user input
  if y == keys.down and selected < #menuOptions then --If selected is smaller than the number in menuOptions
   selected = selected + 1
  elseif y == keys.up and selected > 1 then
   selected = selected - 1
  elseif y == keys.enter then --Returns the selected item
   return selected
  end
end
end
x = start()
print()
print("You selected option ".. x)

Hope this helped :)/>/>
Grim Reaper #3
Posted 14 September 2012 - 05:19 AM
Here's a nice little script I wrote for you:
http://pastebin.com/wVkzaqF7

I'll post the code here too, but I strongly suggest that you download it onto a computer in your world to read. Or better yet, read it in NotePad++.
Spoiler


-- Basic Menu written by PaymentOption --
--[[
We'll be using tables to achieve this menu. Tables are much better for
creating a menu because the methods we'll be writing to manipulate the menu
won't require any modifications if we add more options to the menu. This might
not make sense immediately, but hopefully you'll understand a little better
once we write the code.
]]--

-- First, we'll start with declaring the variables we know we'll need.
-- We'll be creating a 1 level menu with 3 selections.

local bRunning = true
-- The above is the boolean value (true/false) that tells the loop at the bottom of
-- this program to keep running the code inside it. If this variable was false, the
-- loop would stop executing and our program would end.

local nScreenWidth, nScreenHeight = term.getSize()
-- The above is the x and y dimensions of the screen we're currently working with.
-- These numbers will help us with drawing our text to the right positions of the screen.

local nSelection = 1
-- The above is our current selection. This variable will help us keep track of
-- what option we've got currently selected in the menu.

local tMainMenu = {
[1] = { sTitle = "First Option", fAssociatedFunction = FirstOption },
[2] = { sTitle = "Second Option", fAssociatedFunction = SecondOption },
[3] = { sTitle = "Exit", fAssociatedFunction = function() bRunning = false end }
}
-- The above is our main menu table.
-- It consists of 3 sub tables keyed at 1, 2, and 3.
-- Each contains two variables: sTitle and fAssociatedFunction.
-- sTitle is the name of the option, or the name we'll print out when the program is run.
-- fAssociatedFunction is the identifier, or name, of the function this option will be linked to.
-- In other words, if we select this option it will run the function assigned to fAssociatedFunction.

-- Now that we've got our variables declared, we'll right our methods, or functions.
-- I like to keep my methods orgainzed by purpose. If I have methods that draw stuff,
-- they'll be under draw methods. If I have methods that do networking stuff, they'll
-- be under networking methods.

-- Drawing Methods --

-- PrintCentered is a function that prints the text we give it to the exact center of the terminal.
-- Its two parameters are the height, or y coordinate, on the screen we want to print to, and the
-- text that we want to print there.
function PrintCentered( nHeight, sString )
term.setCursorPos( nScreenWidth/2 - string.len( sString )/2, nHeight )
-- The above line positions the cursor on the center of the screen on the given height.
-- It accomplishes this, if you're interested, by taking the width of the screen and dividing it in 2,
-- so we're now at the center of the screen, then dividing the length of the text we want to print by 2
-- then subtracting that number from the screen width divided by 2 to get the exact center of the screen.
term.write( sString )
-- The above statement simply prints the string on the current position of the cursor.
end

-- ClearScreen is a simple function that clears the screen of all text and reset
-- the position of the cursor to the upper left hand corner of the screen.
function ClearScreen()
term.clear()
-- The above statement clears the screen of all text.
term.setCursorPos( 1, 1 )
-- The above statement repositions the cursor to the coordinates 1,1 (the upper left hand corner).
end

-- PrintMenu simply prints out all of the options in the menu passed
-- in the center of the screen starting at the given y value.
-- !WARNING!
-- This method assumes that the menu table passed has the format we used above:
-- tMenu[n] = { sTitle, fAssociatedFunction }
function PrintMenu( tMenu, nStartHeight )
-- We'll loop through the table to make sure we print every option.
-- This is where tables come in handy: if we just use a bunch of if
-- statements then we'd have to add a new if every time we added an
-- option. This loop and table method will handle all of that for you!
for index = 1, #tMenu do
-- Start the iterator at 1 and we'll use it as our index.
-- Run this loop as many times as there are indexes in the tMenu table.

-- If the iterator has reached the option that is currently selected in the table
-- we want to signal to the user somehow that it is selected. We'll do this here
-- by drawing brackets around the option, while leaving the other options without
-- brackets.
if index == nSelection then
PrintCentered( nStartHeight + ( index - 1 ), "[" .. tMenu[index].sTitle .. "]" )
else
PrintCentered( nStartHeight + ( index - 1 ), " " .. tMenu[index].sTitle .. " " )
end
end
end

-- End Drawing Methods --

-- Key Handling Methods --

-- HandleKeyPress takes a key code, or value, and
-- carries out the appropriate action. For example,
-- if we pressed enter then this method would execute the
-- associated function of the selected option.
function HandleKeyPress( nKey, tMenu )
-- I like to use the key codes instead of the keys API, so bare with me.
-- This is another place that a table as a menu comes in handy!
-- Instead of having to check for each individual enter press on which option,
-- we can just run the associated function for that selection in the table!

-- If the enter key is pressed then run the associated function of the selected option.
if nKey == 28 then
tMenu[nSelection].fAssociatedFunction()
-- If the up key is pressed and the selection is greater than 1 then move the selection up once.
-- We check the current selection to make sure we don't move off of the menu when pressing keys.
elseif nKey == 200 and nSelection > 1 then
nSelection = nSelection - 1
-- If the down key is pressed and the selection is less than the amount of selections in the given table.
elseif nKey == 208 and nSelection < 3 then
nSelection = nSelection + 1
end
end

-- End Key Handling Methods --

-- Associated Functions --

-- FirstOption is the method that is run when
-- the first option is selected and enter is pressed
-- on the menu.
function FirstOption()
ClearScreen() -- Clear the screen.
PrintCentered( 5, "First option selected." ) -- Notify the user that they just pressed the first option.
sleep( 2 ) -- Sleep for a couple seconds so the text isn't printed then instantly cleared.
end

-- Second option works just like FirstOption.
function SecondOption()
ClearScreen() -- Clear the screen.
PrintCentered( 5, "Second option selected." ) -- Notify the user that they just pressed the second option.
sleep( 2 ) -- Sleep for a couple seconds so the text isn't printed then instantly cleared.
end

-- End Associated Functions --

-- Main Program Loop --

-- Loop this code until bRunning is not true (the program has been exited).

while bRunning do
ClearScreen() -- Wipe the screen so we can update it with the newest frame.
PrintMenu( tMainMenu, 5, nSelect ) -- Print the main menu starting on line 5.

local sEvent, nKey = os.pullEvent( "key" ) -- Wait for any key presses so we can update the menu.
HandleKeyPress( nKey, tMainMenu ) -- Perform the appropriate actions for the key pressed.
end

-- End Main Program Loop --

Hope I helped! :)/>/>
EmTeaKay #4
Posted 14 September 2012 - 05:24 AM
Current code:
local menuOptions = {"1", "2", "3"}
local termX, termY = term.getSize()
local selected = 1
function centerText(text, termY)
term.setCursorPos(termX/2-#text/2,termY)
term.write(text)
return true
end
function start()
while true do
 term.clear()
 for i,v in ipairs(menuOptions) do
  if i == selected then
	   centerText("[ "..v.."] ", i)
 else
    centerText(v,i)
 end
   end
   x,y = os.pullEvent()
   if y == keys.down and selected < #menuOptions then
    selected = selected + 1
   elseif y == keys.up and selected > 1 then
    selected = selected - 1
   elseif y == keys.enter then
    return selected
   end
end
end
x = start()
print()
print("You have selected option: "..x)  
Also, it shows up like
[1]
[2]
[3]
Bubba #5
Posted 14 September 2012 - 02:25 PM
Hmm. I'm on my phone right now so I can't test it but the code looks right to me. I don't know why it would be showing up wth the brackets around each number. It was working fine earlier. Ill try to test this out when I get home.
sjele #6
Posted 14 September 2012 - 04:14 PM
   if y == keys.down and selected < #menuOptions then 
it errors with atemting to call nill
That line error, i belive you can fix it by changing keys.down to key code for down arrow.

Scroll down here to find the number for it:
http://computercraft.info/wiki/index.php?title=Raw_key_events
Bubba #7
Posted 14 September 2012 - 08:02 PM
   if y == keys.down and selected < #menuOptions then 
it errors with atemting to call nill
That line error, i belive you can fix it by changing keys.down to key code for down arrow.

Scroll down here to find the number for it:
http://computercraft.info/wiki/index.php?title=Raw_key_events

That should only be the case if you are not using the latest version of CC (perhaps tekkit?) or if for some reason you deleted the keys api.If you aren't using the latest version of CC though, then yes that would fix the problem.
EmTeaKay #8
Posted 14 September 2012 - 08:42 PM
Great, I got it working, but how do I make it so it goes sideways instead of up and down?
Grim Reaper #9
Posted 14 September 2012 - 09:19 PM
Did you take a look at my tutorial? It's a little better documented, but I can show you how to do what you're looking for. Instead of using the up and down arrow key codes, we'll use the left and right.

203 = left
205 = right

In the print options method you can change how to options are drawn from being vertically stacked, to horizontal.
EmTeaKay #10
Posted 15 September 2012 - 05:50 AM
Yes, I do agree it is a lot better explained, but searching through that much code without knowing what you're looking for is daunting. So, do you think I could give you my current code and you change it? Because I am that that great at Lua tables and all that jazz.
http://pastebin.com/3zEBxREP
Also, thank you in advance.
EmTeaKay #11
Posted 16 September 2012 - 03:26 AM
Grim Reaper, I used your code and I stripped away all the '–''s. Now it says:

startup:28:attempt to call nil
And here is my current code, in the form of Pastebin:
http://pastebin.com/8RCmSMpE
Matrixmage #12
Posted 16 September 2012 - 04:35 AM
Grim Reaper, I used your code and I stripped away all the '–''s. Now it says:

startup:28:attempt to call nil
And here is my current code, in the form of Pastebin:
http://pastebin.com/8RCmSMpE

I didn't look through all of it as it would take forever to compare both pieces of code, but you probably deleted a important line of code, or didn't completely get rid of a comment. Just try running the code as he posted it first, before getting rid of all of his comments.
EmTeaKay #13
Posted 16 September 2012 - 04:56 AM
It seems that the problem is that I can't have it as a startup.
EmTeaKay #14
Posted 16 September 2012 - 05:59 AM
http://pastebin.com/AsXYxYYi All right, I got a different menu code. But this one prints vertical when I want it to print horizontal. Could someone please help?
And, sorry for the double post.
sapient.fool #15
Posted 17 September 2012 - 06:28 AM
Here, I fixed up your code for you.
I personally don't care for doing menus this way, but hey, whatever gets the job done.

Spoilerhttp://pastebin.com/Z4D1ry6k

function one()
print("Hello")
end
function two()
print("Hey")
end
function three()
print("Hi")
end
local menuOptions = {"1", "2", "3"}
local termX, termY = term.getSize()
local selected = 1
local strSeperator = string.rep(" ",5)
function centerText(text, termY)
term.setCursorPos(termX/2-#text/2, termY)
term.write(text)
end
function start()
while true do
  term.clear()
  local strMenuLine = "|"
  for i,v in ipairs(menuOptions) do
   if i == selected then
	strMenuLine = strMenuLine..strSeperator.."["..v.."]"
   else
	strMenuLine = strMenuLine..strSeperator.." "..v.." "
   end
   strMenuLine = strMenuLine..strSeperator.."|"
  end
  centerText("+"..string.rep("-",(#strMenuLine-2)).."+",1)
  centerText(strMenuLine,2)
  centerText("+"..string.rep("-",(#strMenuLine-2)).."+",3)
  local id, key = os.pullEvent()
  if id == "key" then
   if key == 205 then
	selected = selected + 1
	if selected > #menuOptions then
	 selected = 1
	end
   elseif key == 203 then
	selected = selected - 1
	if selected < 1 then
	 selected = #menuOptions
	end
   elseif key == 28 then
	return selected
   end
  end
end
end
x = start()
print()
if x == 1 then
one()
end
if x == 2 then
two()
end
if x == 3 then
three()
end