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

GUI.API | Simple & Powerful Buttons,Dialogue Boxes, Text-boxes, and More!

Started by account.username, 31 May 2014 - 04:45 PM
account.username #1
Posted 31 May 2014 - 06:45 PM
…GUI API…

(Working on new version as this one is outdated and in bad shape)

/A easy to use tool for making GUI's and more/

Pastebin : qz7KGw3R



Changes:

[indent=13]v 2.4 Fixed a bug with [button]:remove()[/indent]
[indent=13]v 2.3 Added functions [button]:remove() and [bar]:setup()[/indent]
[indent=13]v 2.0 Added Dialogue Boxes[/indent]
[indent=13]v 1.2 Added Text-boxes[/indent]


Screenshots

Spoiler






Example Code/Documentation


Spoiler

Buttons:

button = gui.createButton(  "button", function() print("Hello World!") end ) --#This creates a new button with a function that when triggered will print "Hello World!"

button:draw( 1,1 5, colors.green, colors.white ) -- #This draws the button at (1,1) with a width of 5. The first color is what the buttons color will be and the second color is the text color

button:toggle( colors.red,4) --#This toggles the buttons color to red for 4 seconds (If you leave the second argument as nil it will just toggle the button)

button:trigger() --#This triggers the buttons function

gui.detect( 1,3,true ) --#this checks an array of all the buttons you've defined and returns the name of the button. If you also set the third argument to true it will trigger that buttons action


Progress Bars:


bar = gui.createBar( "bar" ) --#This creates a new bar with the name

bar:draw( 5,8,30,colors.white,colors.green,true,colors.black,colors.white ) --#This draws the bar at (5,8) with a length of 30. The first color is the bar color, the second color is the progress color, the next argument is whether or not to display the percent, the third color is the text background color (for displaying the percent) and the fifth is the percent text color.

bar:update( percent ) --#Re-draws the bar with the new percent

Text-boxes:

input = gui.newTextBox(2,5,25,colors.green,colors.white) --#Makes a text box at (2,5) with a length of 25, a green background color and a white text color.


Dialogue Boxes:


box = gui.createDialogueBox("GUI API",{"This is a dialogue box!","Do you like it?"},"yn") --#Creates a dialogue box with the title "GUI API" ,two body lines: "This is a dialogue box!" and "Do you like it?" and the box type is "yn"

box:draw( 20,5,5,colors.gray,colors.lightBlue,colors.white ) --#Makes a text box at (20,5) with a width of 5, a gray background color, white text color, and lightBlue title bar color.





















Functions (Better Documentation)

SpoilerButtons:

createButton( Name , Action [function] )
Creates a button with a name and trigger function.

detect( x, y, trigger? [true/false] )
​Uses x and y args from a mouse click event and checks if any buttons were clicked. It can also trigger the buttons function if you set the "trigger?" arg to true.

[button]:draw( x , y, width, color, textColor )
Draws a button at the determined coordinates with the determined colors and length and then adds it to an array of all created and draw buttons.

[button]:toggle( toggleColor, Delay )
Changes the buttons color for a determined delay. If the "delay" argument is left as nil it will toggle and stay the toggle color.

[button]:trigger()
Activates the buttons "action" which is set when the button is created.

[button]:remove()
Removes button for the button array and deletes all info tied to that button.

Progress Bars:

createBar( name )
Creates a bar with a name.

[bar]:setup( x,y,length,barColor,progressColor,displayPercent? [true/false] ,percentBackgroundColor,textColor )
Sets up a bar to be drawn

[bar]:draw( x,y,length,barColor,progressColor,displayPercent? [true/false] ,percentBackgroundColor,textColor )
Draws a bar at the determined coordinates with the determined colors and with the determined length and then display a percent if you put true for the "displayPercent?" arg.
If you leave all the args as nil and have used :setup() it will draw it using what you put for :setup()

[bar]:update( percent )
Draws and updates the bar with the new percent.

Text-boxes:

newTextBox(x,y,length,backgroundColor,textColor)
Draws and selects a textbox at the determined coordinates with the determined colors and with the determined length.

Dialogue Boxes:

createDialogueBox( title,body,boxType )
Creates a dialogue box with a title, body text, and a box type. If you make the body a table you can have multiple lines.
The current box types are "yn" (yes or no returns true or false) or "ok" (returns true).

[box]:draw( x,y,width,backgroundColor,titleBackgroundColor,textColor )
Draws a dialogue box at the determined coordinates with the determined colors.

[box]:clear( color )
Clears a dialogue box of the screen with the determined color.

…Note…

Text-boxes are VERY work in progress.

Please leave suggestions and bugs in the replies.

Thanks!
Edited on 06 June 2015 - 01:13 AM
Televiewed #2
Posted 31 May 2014 - 06:52 PM
This is awesome, I gotta check it out.
skwerlman #3
Posted 01 June 2014 - 05:44 AM
Looks really useful!
Maybe this is only on my end, but I couldn't get the images to load.

Since text boxes are WIP anyway, maybe add a height option to them to they can be several lines high.

Also, is it buffered, or do all of the drawing operations happen immediately?

I assume
Creates a bar with a namevvvvvvvv.
is a typo. (Or maybe a Direwolf20 reference?)
Edited on 01 June 2014 - 03:48 AM
account.username #4
Posted 01 June 2014 - 06:54 AM
@skwerlman Hahah that was a typo (push to talk) and the drawing methods are instant.
newcat #5
Posted 05 June 2014 - 08:35 PM
This is pretty awesome, easy to use and cool :D/> Hopefully you continue developing this api.

Edit: Can you add a function to unregister buttons?
Edited on 05 June 2014 - 07:13 PM
account.username #6
Posted 06 June 2014 - 02:27 AM
@newcat Yeah Ill add that. Thanks for the awesome suggestion.
Rougeminner #7
Posted 06 June 2014 - 05:55 AM
as of right now i only have 2 questions, Can we have a video to explain in a bit further depth and was this made to resemble apple script?
newcat #8
Posted 06 June 2014 - 04:17 PM
It seems like there is a bug with the new remove function. Everytime I call this function my program just ends. I did a bit research and noticed that the if statement is never true:

if self.name == buttonList[i][1] then
  table.remove(buttonList,buttonList[i])
  print("Deleting Button")
end
Atleast I never see the text "Deleting Button"…
account.username #9
Posted 07 June 2014 - 12:09 AM
@rougeminner Yes I will make a video and frankly I don't know what that is unless you mean the literal Apple Script

@newcat Thank You! I'm fixing that now
account.username #10
Posted 07 June 2014 - 12:21 AM
@newcat All fixed in version 2.4
newcat #11
Posted 08 June 2014 - 06:39 PM
Thanks for fixing it, it does show the text now so that works. But my program still gets stopped after I use btn:remove(). And if I put the print command after the "end" of the for-loop it doesn't get printed out, so I am assuming that self = nil also doesn't get called. Maybe it is the way I am using the functions:

Spoiler

function mainMenu()

  btnAdd = gui.createButton("  Add Item ", addItem)
  btnAdd:draw(10,3,3, colors.green, colors.white)

  btnDel = gui.createButton("Remove Item", delItem)
  btnDel:draw(28,3,3, colors.red, colors.white)

  click = waitMouse(true)

end

function addItem()

  btnAdd:remove()
  btnDel:remove()

  print("Deleted")
 
  btnInp = gui.createButton("   Input data by hand   ", function() end)
  btnInp:draw(18,3,3,colors.gray,colors.white)
 
  btnGet = gui.createButton("Retrieve data from chest", function() end)
  btnGet:draw(18,9,3,colors.gray,colors.white)

  click = waitMouse(false)

  if (click == "   Input data by hand   ") then
    print("IDBH")
  elseif (click == "Retrieve data from chest") then
    print("RDFC")
  end

end

mainMenu()

The text "Deleted" also is not printed.
account.username #12
Posted 10 June 2014 - 11:23 PM
@newcat Based on the code you sent you are never calling the function "addItem" which that code is in. That might be your problem.
newcat #13
Posted 11 June 2014 - 10:49 AM
@newcat Based on the code you sent you are never calling the function "addItem" which that code is in. That might be your problem.
Actually I do but you can't know that since it is done in the function waitMouse() which I didn't paste here :unsure:/>
But i figured out that I can just call

btnAdd = nil
and it deletes the reference to the object. I know that it doesn't delete the object itself, so it is not good in terms of memory using, but button:remove() still crashes my program.
I already removed the self = nil for testing purposes, but it still crashes so I assume that the table.remove() is the problem…

My workaround works so far but i think i might get issues when I create another button with the same name…

Edit: This is my waitMouse() function:

function waitMouse(trigger)
  while true do
	local ev,mb,x,y = os.pullEvent("mouse_click")
	if (mb == 1 and gui.detect(x,y, false) ~= nil) then
	  return gui.detect(x,y, trigger)
	end
  end
end
So it's checking if somebody clicked with the left MB on one of the buttons (so basically it's a bit of an extension of your gui.detect() )

Edit2:
While trying your dialogue box I noticed that the button "hitbox" isn't correct. In my try (with table as body) it was 3 pixel too far left, so there was just one pixel which was on the button. The rest of the button was… um… let's say for beauty reasons ;)/>

Also I now ran into problems with my workaround. But with these problems I noticed a improvement you could do with your button-click-detection. Since it is possible to create overlapping buttons (which is not useful, but I did since I can't remove the old buttons from the detection yet) you may want to check the newest buttons first, because the newest buttons will always be on top of the older.

function detect( x,y,trigger )
  for i = #buttonList,1,-1 do
	-all the stuff here
  end
end

And another thing, just for your documentation, in the function btn:draw() the width is actually the height

I know thats quite much all in all, but please don't take that as criticism, these are all just tips how you can improve your API even further, but still this API is really powerful, easy to use and AWESOME, so please continue this great work!

Edit3:

Just noticed some bugs with your textboxes:
  • When typing a number, it always adds 1 to it. So if i press '1' it writes 2. If I press '9' it writes 10, if i press '0' it writes 11
  • It's not possible to write capital letters yet
Edited on 11 June 2014 - 01:44 PM
timia2109 #14
Posted 11 June 2014 - 11:49 AM
Nice design!
Think I will use it in my next project!
account.username #15
Posted 12 June 2014 - 12:29 AM
@newcat Which dialog box type and I think I may have fixed the remove() bug
hbomb79 #16
Posted 12 June 2014 - 02:09 AM
Ummm, sorry to be dumb, I've never done this before, but do I just use he pastebin link and put GUI. Infront and that it, or is there a special installation for these, is there a video explaining how to use this, I've never used buttons before, so the stuff in closed brackets are cuss right so I can create a button with name one and then draw button and type one in the square brackets?
account.username #17
Posted 12 June 2014 - 04:36 AM
@Hbomb_79 I don't quite understand what you're asking. Please clarify (improve grammar and such).
skwerlman #18
Posted 12 June 2014 - 05:36 AM
Ummm, sorry to be dumb, I've never done this before, but do I just use he pastebin link and put GUI. Infront and that it, or is there a special installation for these, is there a video explaining how to use this, I've never used buttons before, so the stuff in closed brackets are cuss right so I can create a button with name one and then draw button and type one in the square brackets?
Firstly, inexperienced/new ~= dumb. Everyone was new to lua/CC once.

Run
pastebin get qz7KGw3R gui
to install it.

In your code, include
os.loadAPI('/gui')
to load it.

Finally, you can just call the API's functions from anywhere after the above line. Make sure to preface them with gui.
e.g.: gui.createButton(<your parameters here>)

More info on os.loadAPI can be found here.

If you still need help, create a topic here. Ask A Pro is generally a better place for an in-depth explanation with Q&amp;A.
Edited on 12 June 2014 - 03:36 AM
newcat #19
Posted 12 June 2014 - 06:12 PM
I used the "ok" dialogue box type.

Btw: There is one d too much at the end of the for-loop in the remove() function ;)/>

Edit:

I just figured out the problem with button:remove()
After removing the row the table has one row less, but the for-loop will iterate so many times like the table had rows before (sorry for my bad english).

I fixed it like this:

function Buttons:remove()
  for i = 1,#buttonList do
    if self.name == buttonList[i][1] then
	  table.remove(buttonList,i)
	  return true
    end
  end
end

In this case the table stops iterating after it found the right button. You can also do

function Buttons:remove()
  for i = 1,#buttonList-1 do
    if self.name == buttonList[i][1] then
	  table.remove(buttonList,i)
    end
  end
end
But the problem with this version is that it'll delete ALL buttons with the name, with the other version it'll just delete the oldest.
Edited on 12 June 2014 - 08:05 PM
hbomb79 #20
Posted 12 June 2014 - 11:12 PM
Thanks for you answer skwerlman, one more question, how do I use the buttons to call a function once clicked, could someone please give a quick example code…. I have never used an api, also how would I use a dialogue box and check what they clicked on e.g. Like with an if statement…
account.username #21
Posted 13 June 2014 - 02:05 AM
@Hbomb_79 you just need to have the buttons action set as a function.


button = gui.createButotn( "Button", function() doSomething() end )
button:trigger()
newcat #22
Posted 13 June 2014 - 06:20 PM
Just found another bug: If you use multiple dialogboxes in one program, after clicking "ok" in one dBox, in each following box you can click whereever you want and it will still continue.

Looking at the code I noticed that ret is not local, so everytime you call box:draw(), ret will have the value of the last dBox.

I didn't test it with y/n-boxes, but there will be the same problem. I solved it with declaring ret as local at the start of the draw-function, so it will be deleted after the function finished:

function Boxs:draw( x,y,width,color,bcolor,tcolor )
  local ret = nil
  ... --all the other stuff here
end
account.username #23
Posted 13 June 2014 - 08:52 PM
@newcat Thanks thats fixed!
hbomb79 #24
Posted 16 June 2014 - 04:44 AM
Can you explain that button:trigger() i want it so i can have a menu that displays several buttons, when i click on one it clears the screen and loads a function ive already made… the function is called bulkHead() how would i do this?
BytePointer #25
Posted 19 June 2014 - 07:12 AM
WOW, this is VERY useful! Thanks, I suck at GUIs!
hbomb79 #26
Posted 19 June 2014 - 10:11 AM
Can someone help me out, this is what im having issues with, i have NO idea how to use this api…

this is the code…. It crashes and turns red without even clicking the button as well….???


os.loadAPI("/api")
function hi()
  print "Hi"
end
button = api.createButton ("button", fucntion() hi() end)
button:draw(1,1 5, colors.green, colors.white)
button:toggle(colors.red,4)
button:trigger()

It then gives me this error "api:70: attempt to index ? (a nil value)"
account.username #27
Posted 19 June 2014 - 08:26 PM
@Hbomb_79

Change:
os.loadAPI("/api")

To:
os.loadAPI("api")
Lignum #28
Posted 19 June 2014 - 08:33 PM
@Hbomb_79

Change:
os.loadAPI("/api")

To:
os.loadAPI("api")

That wouldn't make a difference. The error occurs in the API, which means it's been loaded.
newcat #29
Posted 20 June 2014 - 11:51 PM
There is a spell mistake. It needs to be

function() hi() end

You have written fucntion().
hbomb79 #30
Posted 30 June 2014 - 03:21 AM
Oh wow, what a silly mistake… Thanks
gknova61 #31
Posted 30 June 2014 - 10:17 AM
I just tried buttons (first &amp; last thing I tried), and I got this error:
Just downloaded the API so latest version.
[string "gui"]:70: attempt to index a nil value after running the following code

os.loadAPI("gui")
button = gui.createButton(  "button", function() print("Hello World!") end ) --#This creates a new button with a function that when triggered will print "Hello World!"

button:draw( 1,1, 5, colors.green, colors.white ) -- #This draws the button at (1,1) with a width of 5. The first color is what the buttons color will be and the second color is the text color

button:toggle( colors.red,4) --#This toggles the buttons color to red for 4 seconds (If you leave the second argument as nil it will just toggle the button)

button:trigger() --#This triggers the buttons function

gui.detect( 1,3,true ) --#this checks an array of all the buttons you've defined and returns the name of the button. If you also set the third argument to true it will trigger that buttons action
It errors on button:trigger()
Edited on 30 June 2014 - 08:17 AM
newcat #32
Posted 08 August 2014 - 02:52 AM
I started to work on my old GUI project again and fixed the problems with the textboxes I mentioned in one of my older posts. Here is the new version:

Spoiler

function newTextBox(x,y,length,bcolor,tcolor)
  typed = {}
  alphabet = "abcdefghijklmnopqrstuvwxyz"
  local shift = false
  ... --all the display stuff here
  typing = true
  term.setCursorPos(x,y)
  while typing do
    event,key = os.pullEvent()
    if event == "key" then
	  if key >= 2 and key <= 10 then
	    table.insert(typed,tonumber(key)-1)
	  elseif key == "10" then
	    table.insert(typed, "0")
	  elseif keys.getName(key) == "enter" then
	    typing = false
	    return string.gsub(table.concat(typed,""),"space"," ")
	  elseif keys.getName(key) == "space" then
	    table.insert(typed,keys.getName(key))
	  elseif keys.getName(key) == "period" then
	    table.insert(typed,".")
	  elseif keys.getName(key) == "comma" then
	    table.insert(typed,",")
	  elseif keys.getName(key) == "backspace" then
	    table.remove(typed,#typed)
	  elseif keys.getName(key) == "leftShift" or keys.getName(key) == "rightShift" then
	    if shift == true then shift = false else shift = true end
	  else
	    key = keys.getName(key)
	    if string.find(alphabet,key) ~= nil then
		  if shift == false then
		    table.insert(typed,key)
		  else
		    table.insert(typed, string.char(string.byte(key) - 32))
		  end
	  end
    end
   
    ... --all the other stuff here
  end
end

I found no way to pull an event like "key_up" therefore you can just toggle shift at the moment. Maybe that will be implemented in a later version of CC…
I hope you update your API so all the people who want to work with that awesome API have that fix without having to manually paste it into their version.
NikolaiM #33
Posted 13 August 2014 - 11:13 AM
Trying to make a power monitor, but every time i run the program the bar shows up all wierd looking(not working proberly)

http://pastebin.com/NiQJJdUU
TommieIV #34
Posted 19 August 2014 - 10:54 PM
Would anybody do a tutorial video? I kinna need one! Or answer this: how do I use the button trigger function?
newcat #35
Posted 20 August 2014 - 02:46 PM
Would anybody do a tutorial video? I kinna need one! Or answer this: how do I use the button trigger function?
I will probably do one in a few days.
newcat #36
Posted 24 August 2014 - 03:29 AM
Trying to make a power monitor, but every time i run the program the bar shows up all wierd looking(not working proberly)

http://pastebin.com/NiQJJdUU
What data do you get from the sensor? Some example values would be cool
newcat #37
Posted 26 August 2014 - 12:32 AM
Would anybody do a tutorial video? I kinna need one! Or answer this: how do I use the button trigger function?
Here is it: https://www.youtube.com/watch?v=-oXDm92KZRc
account.username #38
Posted 27 August 2014 - 02:22 AM
@newcat THANK YOU SO MUCH!!!!!!!!!!!!
Mackan90096 #39
Posted 29 August 2014 - 04:52 PM
Looks, really good, I'm going to use this with my OS. Am I allowed to upload this to github and redistrubute it with the download of my OS if I give full credit of it to you ?
Rougeminner #40
Posted 04 September 2014 - 08:16 PM
I am sorry for a "delayed" reply acounts.username. Your script kinda reminded of applescript that is why i asked but great api going to use it for a OS. Since i have been to busy to check recently is this api compatibld with PDA's
Corky_McDuff #41
Posted 04 October 2014 - 09:25 AM
Hmm, I love this but I am way out of my depth here.
Would anyone be kind enough to assist I incorporating buttons into my teleport program please?
I would like a list of deck descriptions and a corresponding numbers next to it that once click teleports to that location.
This will be used for my Starship Enterprise Turbo Lifts and there are several decks in place, so will need small buttons to fit them all in.
My program is gbB2eRG1 if you are interested.
Thanks in advance…sorry for being so useless at this!
Dragon53535 #42
Posted 04 October 2014 - 10:00 AM
Hmm, I love this but I am way out of my depth here.
Would anyone be kind enough to assist I incorporating buttons into my teleport program please?
I would like a list of deck descriptions and a corresponding numbers next to it that once click teleports to that location.
This will be used for my Starship Enterprise Turbo Lifts and there are several decks in place, so will need small buttons to fit them all in.
My program is gbB2eRG1 if you are interested.
Thanks in advance…sorry for being so useless at this!
You could ask in the Ask a Pro section, alot of us lurk in there awaiting fun topics :P/> Also you will probably get more views that way and thus more help.
TheCaptainSly #43
Posted 11 October 2014 - 01:12 AM
My question is going to be pretty hard for me to state but I'll give it a shot.

With the dialogue boxes I understand that Y = true and N = false. But I want to be able to use that in an if statement and decide whether or not Yes or No was clicked. I've tried everything I could think of. I'll put the most recent attempt in the spoiler below. If anything in the buttons and such look weird, It's probably because I typed it wrong. Everything in the actual program is typed correctly.

Spoiler

skipfileCheck = GUIAPI.createDialogueBox("ZoS", {"Would you like to", "skip file checking?"}, "yn")

function skipfileCheckingYN()
skipfileCheck:draw( 20, 5, 7, colors.gray, colors.lightBlue, colors.white )
if skipfileCheck == true then
os.shutdown()
elseif skipfileCheck == false then
fileChecking()
end
end

I want to know what variable says true when I click yes and what variable says false when I click no. This code should work as it is copied from my original code that has been heavily tested.

I fixed the code and everything so it should now convey what I'm trying to ask.
Edited on 13 October 2014 - 04:16 PM
newcat #44
Posted 11 October 2014 - 10:44 PM
–snip–

You wrote "y/n" as box type, but it actually needs to be "yn".
I didn't test it but the code you posted should not display anything and the return value will be nil.
Edited on 11 October 2014 - 08:45 PM
TheCaptainSly #45
Posted 13 October 2014 - 06:18 PM
–snip–

You wrote "y/n" as box type, but it actually needs to be "yn".
I didn't test it but the code you posted should not display anything and the return value will be nil.

I've fixed the code. If you could check the original post that would be super. Sorry for not checking and testing said code before asking. I know it's hard to give help if you can't understand what the asker is asking.
newcat #46
Posted 13 October 2014 - 09:39 PM
Oh ok now I got what you were asking for.
The draw-method is the thing that returns true or false so basically what you need to do is to place a variable in front of the method like this:

result = skipfileCheck:draw( 20, 5, 7, colors.gray, colors.lightBlue, colors.white )

Now the result variable holds either true or false, depending what the user clicked on.

Hope it helped you and sorry for the poor formatting, I am writing on my mobile phone.
TheCaptainSly #47
Posted 13 October 2014 - 11:13 PM
Ok I get now… I think If I have another question I'll surely tell you! Thank you
Kizz #48
Posted 23 April 2015 - 02:36 PM
This api is extremely unstable :(/>. I had to make major changes just to be able to add multiple buttons. Even still, the toggle and remove functions do not work at all. I had to rebuild the detect API to pull an event so that you can click a button. The button table almost always crashed on a nil value for some reason.

It looked promising but is clearly unfinished and no longer active. :(/>
chrdov #49
Posted 22 May 2015 - 09:51 PM
do the creation functions and detection function actually return anything, like the element that was clicked?
newcat #50
Posted 23 May 2015 - 02:05 AM
The creation function returns the button object (this API is object-oriented) and the detect function returns the label of the clicked button.

I made a tutorial a while ago so if you want to know more about this API just scroll up some posts and check out the video. I also show a little snippet in the tutorial which expands the detect function a bit.