Note: There is a really good reason I don't just package these all up into a tidy API, because I want this code to be a learning experience for those who use it.
If you use my code and can't be bothered reading it (and you're probably not even reading this) that's fine!
If you do however use my code, modify it and make it better (that is not hard to do) then please post it back here for others to see and use. Share the code, we all learn stuff.
This is my thread from mccraftcpl copied across. Seeing as that forum doesn't get as much love as this one.
Here are some videos on using both general functions in LUA and my GUI functions too.
Spoiler
Functions[media]http://www.youtube.com/watch?v=KYwivKJoIrw[/media]
GUI Functions
[media]http://www.youtube.com/watch?v=FwYkAlStg0A[/media]
Current List of Functions:
- Loading Bar
- Horizontal Selection Bar
- Vertical Sliding bar
- Vertical Menu
- Key Pad
- Message Box -new, advanced pc only
- Check Boxes -new
- Menu Bar -new, advanced pc only
- Analogue Clock -new, please try to make it better
Loading Bar
Call the function with the parameters (start x, finish x, "<symbol>")
It will put a loading bar onto the next line. If the line is lower than the screen it will clear the screen and put the loading bar at the top.
function HorzLoadBar(sx, fx, sym)
x,y = term.getCursorPos()
if y >= 18 then
term.clear()
term.setCursorPos(sx,1)
x,y = sx,1
end
term.setCursorPos(sx,y)
write("[")
term.setCursorPos(sx,y)
term.setCursorPos(fx-1,y)
write("]")
mdp = math.floor((sx + fx)/2) - 3
for i = (sx+1),(fx-2) do
term.setCursorPos(i,y)
write(sym)
sleep(0.1) --CHANGE THIS TO EDIT TIME
term.setCursorPos(mdp,y+1)
write(string.format("%d",i/(fx-2) * 100))
write("%")
end
term.setCursorPos(1,y+2)
end
Horizontal Selection Bar
This function creates a horizontal selection menu below the current line and the function returns the selected item as a string.
Parameters are:
(items for the menu as an LUA table, Y location you want the menu to appear)
function HorzMenu(menutable1, Ylocation)
local selection1 = 1
while true do
term.setCursorPos(1, Ylocation)
for i = 1,#menutable1 do
if i == selection1 then write"[" end
write(menutable1[i])
if i == selection1 then write"]" end
write" | "
end
local r,s = os.pullEvent()
if r == "key" then
if s == 203 then selection1 = selection1 - 1 end
if selection1 == 0 then selection1 = #menutable1 end
if s == 205 then selection1 = selection1 + 1 end
if selection1 == #menutable1 +1 then selection1 = 1 end
if s == 28 then
term.setCursorPos(1,Ylocation + 1)
return menutable1[selection1]
end
end
end
end
Vertical Sliding Bar
Select a numerical value by moving a slider up and down.
The max range of values is 14 atm. With a simple modification you can make it step in values.
Parameters are (start value, end value, Menu Title as string)
function vertBar(nMin, nMax, sTitle)
local bSelected = false
local selection = nMin
local mdp = (nMin + math.floor((nMax - nMin)/2))
if (nMax - nMin) > 13 then error("ERROR: Bar will not fit on screen max span is 14 e.g. 2 -> 15") end
while (bSelected == false) do
term.clear()
term.setCursorPos(1,1)
print(sTitle)
print("[=]")
for i = nMin,nMax do
if i ~= selection then
write" x"
if i == mdp then
print(" ", tostring(selection))
else
print("")
end
else
write" -"
if i == mdp then
print(" ", tostring(selection))
else
print("")
end
end
end
print("[=]")
local ev1, ev2 = os.pullEvent()
if ev1 == "key" and ev2 ~= 28 then
if selection <nmax then="" selection="selection" +="" 1="" else="" end="" elseif="" ev1="=" "key"="" and="" ev2="=" 28="" bselected="true" return(selection)
function VertMenu(tableofItems)
local selection = "";
local sIndex = 1;
while (selection == "") do
term.clear();
local xMax, yMax = term.getSize();
local y = math.floor(yMax/2) + math.floor(#tableofItems/2);
y = y - #tableofItems;
if y<1 then y = 1 end;
if #tableofItems > yMax then
print "ERROR: List does not fit on screen, reduce number of items in table";
return "";
end
for i = 1, #tableofItems do
if string.len(tableofItems[i]) >= xMax then
term.clear();
term.setCursorPos(1,1);
print (("ERROR: List item # "..i).." is too long please reduce");
return "";
end
local x = math.floor(xMax/2) - math.floor(string.len(tableofItems[i])/2);
if i == sIndex then x = x-1 end;
term.setCursorPos(x,y);
if i == sIndex then term.write("[") end;
term.write(tableofItems[i]);
if i == sIndex then term.write("]") end;
y = y+1;
end
local r,s = os.pullEvent("key");
if s == 208 then sIndex = sIndex +1 end;
if s == 200 then sIndex = sIndex -1 end;
if sIndex > #tableofItems then sIndex = 1 end;
if sIndex < 1 then sIndex = #tableofItems end;
if s == 28 then
term.clear();
term.setCursorPos(1,1);
return tableofItems[sIndex]
end
end
end
Due to demand here is some sample code for how to implement sub menus:Spoiler
menu = {
{"a","b","c"},
{"d","e","f"},
{"g","h","i"},
{"j","k","l"},
{"m","n","o"},
{"p","q","r"}
}
results = {}
results[1] = VertMenu(menu[1])
if results[1] == "a" then
results[2] = VertMenu(menu[2])
elseif results[1] == "b" then
results[2] = VertMenu(menu[3])
elseif results[1] == "c" then
results[2] = "Stop level 1"
end
if results[2] == "d" then
results[3] = VertMenu(menu[4])
elseif results[2] == "e" then
results[3] = VertMenu(menu[5])
elseif results[2] == "f" then
results[3] = VertMenu(menu[6])
elseif results[2] == "g" then
results[3] = "Stop level 2-1"
elseif results[2] == "h" then
results[3] = "Stop level 2-2"
elseif results[2] == "i" then
results[3] = "Stop level 2-3"
end
--if results[3]... --etc. etc. etc.
for a=1,#results do
print(results[a])
end
Use this awesome keypad to get a 4 digit code from the user!
Gogo virtual hardware!
function: keyPad();
returns a table containing the 4 values in the order they were entered
function keyPad(void)
local mt = {
{1,2,3},
{4,5,6},
{7,8,9}
};
local mt2 = {0,0,0,0};
local xOffset = 23;
local nX = 1;
local nY = 1;
local n = 1;
while (n<=4) do
term.clear();
for i = 1,3 do
for j = 1,3 do
term.setCursorPos((xOffset + i),(j));
if (nY == j) and (nX == i) then
term.write("*");
else
term.write(mt[j][i]);
end
end
end
for i = 1,n-1 do
term.setCursorPos((xOffset + i),(4));
term.write("*");
end
r,s = os.pullEvent();
if(r == "key") then
if (s == 205) then
if (nX < 3) then
nX = nX +1;
end
end
if (s == 208) then
if (nY < 3) then
nY = nY +1;
end
end
if (s == 200) then
if (nY > 1) then
nY = nY - 1;
end
end
if (s == 203) then
if (nX > 1) then
nX = nX -1;
end
end
if (s == 28) then
mt2[n] = mt[nY][nX];
n = n+1;
end
end
end
term.clear();
term.setCursorPos((1),(1));
return mt2;
end
1.4.6 Wohoo!
Here is some nice code to celebrate… a lovely message box.
Click on OK or X to close and the function will tell you what the user chose!
For advanced PCs only!
function is
drawMessageBox(startX,startY,sizeX,sizeY,message,title)
startX is the X position of the top left of the box
startY is the Y position of the top left of the box
size X is the width of the box
size Y is the height of the box
message is the line of text in the box
title is the text that appears in the top border
function drawMessageBox(bX,bY,bDX,bDY,message,title)
term.setCursorPos(bX,bY);
--Clear
term.setBackgroundColor(colors.white);
term.clear();
term.setBackgroundColor(colors.black);
term.setTextColor(colors.yellow);
--Print Box
for i = 0,bDY do
term.setCursorPos(bX,bY+i);
term.write(" ");
term.setCursorPos(bX+bDX,bY+i);
term.write(" ");
end -- for
for i = 1,bDX do
if i == bDX then term.setBackgroundColor(colors.red) end
term.setCursorPos(bX+i,bY);
if i == bDX then term.write("X")else term.write(" "); end
if i == bDX then term.setBackgroundColor(colors.black) end
term.setCursorPos(bX+i,bY+bDY);
term.write(" ");
end -- for
--Print Title
term.setCursorPos(bX,bY);
term.setTextColor(colors.white);
term.write(" "..title);
--Print Message
term.setBackgroundColor(colors.white);
term.setCursorPos(math.floor((bX+(bX+bDX))/2) - math.floor(#message/2),math.floor((bY+(bY+bDY))/2));
term.setTextColor(colors.blue);
term.write(message);
--Print OK button
term.setBackgroundColor(colors.black);
term.setTextColor(colors.yellow);
term.setCursorPos(math.floor((bX+(bX+bDX))/2) - 2,bY+bDY);
term.write("[OK]");
term.setCursorPos(40,40);
--Listen for mouse and check where the user clicks
while(true)do
a,MB,X,Y = os.pullEvent("mouse_click");
XY = (X..",")..Y;--Convert click to cartesian Coords
bDXY = ((bX+bDX)..",")..(bY); --Convert [X] location to cartesian coords
if (XY == bDXY) then
--Clear the screen
term.setBackgroundColor(colors.black);
term.setTextColor(colors.white);
term.clear();
term.setCursorPos(1,1);
return "cancel"
end
okX = math.floor((bX+(bX+bDX))/2) - 2; --the [OK] location
if(Y == (bY+bDY)) then
if ((X >= okX) and (X<= okX +4)) then
--Clear the screen
term.setBackgroundColor(colors.black);
term.setTextColor(colors.white);
term.clear();
term.setCursorPos(1,1);
return "OK"
end --if
end -- if
end--while
end -- function
help = drawMessageBox(15,5,25,8,"Divide by 0","Error");
print (help);
Want some more 1.4.6 GUI goodness?
How about Check Boxes!?
function checkBoxes(items)
items - a table containing all the items in order that you want to be check box options
it returns a table containing true/false for each item
Scroll down for non-Advanced PC version
Advanced PC version: (uses mouse)
function checkBoxes(items)
--Initialise the boolean (true/false) array
state = {};
for i = 1,#items do
state[i] = false;
end
--Display the list and its boolean state
while(true) do
term.clear();
term.setCursorPos(1,1);
for i = 1,#items do
if (state[i]) then
term.write("[X] "); -- if active
else
term.write("[ ] "); -- if not
end--if
print(items[i]);
end -- do
print "[Done]"; -- Click this to finish
e,mb,x,y = os.pullEvent("mouse_click");
if(y==(#items + 1)) then return state end
if(y<=#items) then state[y] = not state[y] end
end -- while
end -- function
this = checkBoxes({"One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Eleven","Twelve"});
for i = 1,#this do
print(this[i]);
end
Standard PC Version (uses keyboard)
function checkBoxes(items)
--Initialise the boolean (true/false) array
state = {};
for i = 1,#items do
state[i] = false;
end
--Display the list and its boolean state
counter = 1;
while(true) do
term.clear();
term.setCursorPos(1,1);
for i = 1,#items do
if (state[i]) then
term.write("[X] "); -- if active
else
term.write("[ ] "); -- if not
end--if
if (i == counter) then
term.write(items[i]);
print "<";
else
print(items[i]);
end
end -- do
if (counter == #items + 1) then print "[Done]<" else print "[Done]" end -- Select this to finish
e,k = os.pullEvent("key");
if(k == 28 or k == 57 or k == 203 or k == 205) then
if (counter == #items + 1) then
return state
else
state[counter] = not state[counter];
end
end
if(k == 208) then counter = counter + 1 end
if(k == 200) then counter = counter - 1 end
if(k == 209) then counter = counter + 5 end
if(k == 201) then counter = counter - 5 end
if (counter > #items + 1) then counter = 1 end
if (counter < 1) then counter = #items + 1 end
end -- while
end -- function
this = checkBoxes({"One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Eleven","Twelve"});
for i = 1,#this do
print(this[i]);
end
Menu Bar
Like in MS Windows with the pull down menus
menuBar(items)
items is a table of tables (Yo dawg… I heard you like tables…), where the first item in each sub table is the title of the menu, and the subsequent entries are sub menu items.
Advanced PC only (uses mouse)
function menuBar(items)
itemIndex = 0;
while(true) do --forever
--clear and reset terminal
term.clear();
term.setCursorPos(1,1);
term.setCursorBlink(false);
--Print the menu titles
for i = 1, #items do
term.write((items[i])[1] .. " ");
end
--listen for mouse
e,mb,x,y = os.pullEvent("mouse_click");
--if they clicked on the menu
if (y==1) then
--stopValue is the graphical location at the end of the menu title the user will click on
stopValue = 0;
--loop through the items
for i = 1,#items do
--set the stop value to the end of the title string
stopValue = stopValue + string.len( (items[i])[1] );
--if it is between the stop value minus the word length and the stop value, then the user has clicked on a word
if (x<=stopValue and x> (stopValue - string.len( (items[i])[1] ))) then
--set the index to what the user clicked on
itemIndex = i;
--break out the loop
break;
end
--allow for the double spacing bewteen items
stopValue = stopValue +2;
end
--if the item index is greater than zero than the user has clicked on something
if (itemIndex > 0) then
--loop through the sub table
for i = 2,#(items[itemIndex]) do
--line up to top item
term.setCursorPos((stopValue - string.len(items[itemIndex][1] )+1),i+1);
--print the sub menu
term.write(items[itemIndex][i]);
end
--listen for mouse again
e2,mb2,x2,y2 = os.pullEvent("mouse_click");
--if they clicked on something (and not in empty space on Y axis)
if (y2 <= #(items[itemIndex]) + 2 and y2 ~= 1 and y2 ~= 2) then
--and not in empty space on X axis
if (x2 >= (stopValue-3) and x2 <= stopValue-3+(string.len(items[itemIndex][y2-1]))) then
--tidy up the screen and return the two items they clicked on.
term.clear();
term.setCursorPos(1,1);
return({items[itemIndex][1],items[itemIndex][y2-1]});
end
end
end
end--if
end--while
end--function
n = menuBar({
{"Menu1","This","is","awesome"},
{"Menu2","This2","is2","awesome2"},
{"Menu3","This3","is3","awesome3"}
});
for i = 1,#n do
print(n[i]);
end
Analogue Clock
This is basically a radial meter that displays a needle at a certain percentage around the edge.
it takes two arguments the radius of the clock, and the percentage to display at this moment.
The example program is a simple time of day clock.
0 is at the top and 50% is at the bottom.
Advanced PC version (Pixel)
function drawCircle(r,timeperc)
--if you set the background and colors before calling the function
--and remove the term.clear()
--the clock will fill like a pie chart
term.setBackgroundColor(colors.white);
term.clear();
term.setBackgroundColor(colors.black);
term.setCursorBlink(false);
linedrawn = false;
sx,sy = term.getSize();
sx = math.floor(sx/2);
sy = math.floor(sy/2);
--These numbers are to do with the number of radians the algorithim needs to cycle through to draw a complete circle
--the offset is to make it start at the top of the screen
for i = 4.67,7.18 + 4.67,0.01 do
--circle plotting alogrithim
plotx = sx+1 + r*math.cos(i) *1.6;
ploty = sy+1 + r*math.sin(i);
term.setCursorPos(plotx,ploty);
--I cant figure out why I need 1.88 instead of 2, be nice and help me with this...
perc = timeperc/1.88;
if(i > (4.67 + perc * (7.18 + 4.67)) and linedrawn == false) then
drawLine({sx,sy},{plotx,ploty});
linedrawn = true;
end
term.write(" ");
end
end
function drawLine(pt1,pt2)
p = pt2[1];
h = pt1[1];
q = pt2[2];
k = pt1[2];
for i = 0,1,0.01 do
x = (p-h)*i + h;
y = (q - k)*i + k;
term.setCursorPos(x,y);
term.write(" ");
end
end
term.redirect(peripheral.wrap("right"));
while(true)do
drawCircle(14,os.time()/24);
os.sleep(0.0001);--to stop jittering
end
term.setCursorPos(1,1);
Non-Advanced PC Version (ASCII)
function drawCircle(r,timeperc)
term.setCursorBlink(false);
linedrawn = false;
sx,sy = term.getSize();
sx = math.floor(sx/2);
sy = math.floor(sy/2);
for i = 4.67,7.18 + 4.67,0.01 do
plotx = sx+1 + r*math.cos(i) *1.6;
ploty = sy+1 + r*math.sin(i);
term.setCursorPos(plotx,ploty);
perc = timeperc/1.88;
if(i > (4.67 + perc * (7.18 + 4.67)) and linedrawn == false) then
drawLine({sx,sy},{plotx,ploty});
linedrawn = true;
end
term.write("#");
end
end
function drawLine(pt1,pt2)
p = pt2[1];
h = pt1[1];
q = pt2[2];
k = pt1[2];
for i = 0,1,0.01 do
x = (p-h)*i + h;
y = (q - k)*i + k;
term.setCursorPos(x,y);
term.write("*");
end
end
--term.redirect(peripheral.wrap("right"));
while(true)do
drawCircle(8,os.time()/24);
os.sleep(0.0001);--to stop jittering
--if you remove the term.clear()
--the clock will fill like a pie chart
term.clear();
end
term.setCursorPos(1,1);
</nmax></symbol>