[Mod] Smart Formspecs - OO builder + binding [1.1][smartfs]

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

[Mod] Smart Formspecs - OO builder + binding [1.1][smartfs]

by rubenwardy » Post

Image

This mod provides a 2nd generation way of creating forms - this means that the modder does not need to worry about complex formspec strings*
  • Expandable: you can register your own elements to use on the form.
  • Easy event handling: use binding to handle events.
  • New elements: includes a toggle button
  • Inventories: add pages to smart inventories, without worrying about which mod is installed.
No longer do you have to use fiddly formspec strings and receive field functions, instead you use code like this:
Spoiler

Code: Select all

myform = smartfs.create("smartfs:form",function(state)
    state:size(10,7)
    state:label(2,0,"lbl","SmartFS example formspec!")
    state:field(7,1,3,1,"txt","Textbox")
    state:image(0,0,2,2,"img","default_stone.png")
    state:toggle(0,2,3,1,"tg",{"plenty..","of..","custom..","elements"})
    state:checkbox(2,1,"c","Easy code",true)

    state:button(0,3,2,1,"btn","Click Me"):click(function(self,state)
        print("Button clicked!")
        self.setText("Hello")
        state:close()
    end)
    return true
end)
myform:show("playername")
License: CC0

To install this library, install it as a mod and depend on it,
or if you do not like doing that then place the smartfs.lua file in your mod and then include it (dofile).
There is an example.lua file in the download that shows you how to use this library.

Download / GitHub Page

* it is possible to insert custom formspec strings
Last edited by rubenwardy on Fri Oct 21, 2016 16:00, edited 5 times in total.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
fairiestoy
Member
Posts: 191
Joined: Sun Jun 09, 2013 19:25
Location: Germany

by fairiestoy » Post

Hey Ruben,
first of all, nice idea to add kind of a wrapper for that formspec strings. But one tip for handling this:
Better use it as standalone mod. This way, several modders will not have the same file and can access your
wrapper if they put it into their dependency file. Would make life easier of modders and keeps the data size
smaller due to only one folder with this files.


Greetings
Interesting about new things is, to figure out how it works ...

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

But nobody likes depending on mods, so they will just not use it.

I have added that.
Last edited by rubenwardy on Mon Oct 28, 2013 19:08, edited 1 time in total.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
fairiestoy
Member
Posts: 191
Joined: Sun Jun 09, 2013 19:25
Location: Germany

by fairiestoy » Post

Tell me if im wrong, but there are a few libraries that are used as dependency because they are helpful like plantlife. Also the amount of mods with dependencies and without are imo very averaged. But it was just a suggestion, maybe keep it optional :P
Interesting about new things is, to figure out how it works ...

User avatar
LionsDen
Member
Posts: 530
Joined: Thu Jun 06, 2013 03:19

by LionsDen » Post

Or maybe shoot to get it included as part of the minetest download. Maybe part of the default files/mod or something.

User avatar
PilzAdam
Member
Posts: 4026
Joined: Fri Jul 20, 2012 16:19
GitHub: PilzAdam
IRC: PilzAdam
Location: Germany

by PilzAdam » Post

The main problem with formspec strings is not the format, but rather the positioning. It was designed for inventory lists, so creating whole GUIs with it is a PITA.

User avatar
jojoa1997
Member
Posts: 2890
Joined: Thu Dec 13, 2012 05:11
Location: Earth

by jojoa1997 » Post

I agree. I have made my own inventory before and the hardest is the positioning. I remember reloading the game at least 50 times. Now if there was a way to see formspecs changed real time then that would be helpful.
Coding;
1X coding
3X debugging
12X tweaking to be just right

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

I could make it so you can give positions in pixels from the top left corner, but that could be hacky, and people WILL get confused.

I started work on a Formspec GUI Editor, but I got distracted. There is only a main class in that project so far, no real code at all.

You could make a mod the shows formspecs in game, but it would not have an editor.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

Unfortunately state:close() does not currently work as there is no way to do this in the api.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

u34

by u34 » Post

rubenwardy wrote:Unfortunately state:close() does not currently work as there is no way to do this in the api.
how can i close a GUI by button anyway?
how can i activate a function by enabling a button in a formspec?

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

This element has not been added yet.

https://github.com/minetest/minetest/bl ... i.txt#L990

Code: Select all

btn:onClick(function(self,state)
    print("Button clicked!")
    self.setText("Hello")
    state:close()
end)
Last edited by rubenwardy on Thu Feb 20, 2014 20:42, edited 1 time in total.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

Added the element

Code: Select all

local btn = state:button(0,3,2,1,"btn","Click Me")
btn:setClose(true)
btn:onClick(function(self,state)
    --run on close
end)
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

u34

by u34 » Post

rubenwardy wrote:This element has not been added yet.

https://github.com/minetest/minetest/bl ... i.txt#L990

Code: Select all

btn:onClick(function(self,state)
        print("Button clicked!")
        self.setText("Hello")
        state:close()
    end)
you can add an exit button by default to close the formspec.
does this help?

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

Please read all my responses.
rubenwardy wrote: Added the element

Code: Select all

local btn = state:button(0,3,2,1,"btn","Click Me")
btn:setClose(true)
btn:onClick(function(self,state)
    --run on close
end)
EDIT: Do you mean the proceed button?
Last edited by rubenwardy on Tue Nov 05, 2013 15:18, edited 1 time in total.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

Bump :P
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

Update!

I have added a wrapper for advanced inventories.

This means that you can add your formspec easily to smart inventories, such as inventory_plus and unified_inventory, without caring which one is installed. You don't have to modify your code for each one.

Adding to the inventory is as simple as:

Code: Select all

smartfs.add_to_inventory(myform, "icon.png", "MyFormLabel")
It works, but is quite buggy at this stage.

Any suggestions?

Have you got any suggestions, such as new elements?

I may be adding a page element (ie: start, back, 1, 2, 3, next, end)
to display stuff.
Last edited by rubenwardy on Thu Feb 20, 2014 20:51, edited 1 time in total.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
domtron vox
Member
Posts: 111
Joined: Thu Feb 20, 2014 21:07
GitHub: DomtronVox
IRC: Domtron
In-game: Domtron

by domtron vox » Post

Great Mod! I am going to try and use it for my mod.

I do have a question though. It seems like the height value has no effect on the button. Is this a problem with smartfs or is it an engine limitation(I was trying to make a square button)?

EDIT:
I'm using the smartfs.lua as a library. I got the following error. Is using "throw" a mistake or did I do something to cause the error? I replaced all "throw"s with "print" and it worked.

Code: Select all

18:31:27: ERROR[main]: ServerError: ...on/games/minetest/bin/../mods/mod_manuel/smartfs.lua:19: attempt to call global 'throw' (a nil value)
18:31:27: ERROR[main]: stack traceback:
18:31:27: ERROR[main]:     ...on/games/minetest/bin/../mods/mod_manuel/smartfs.lua:19: in function 'create'
Last edited by domtron vox on Thu Feb 20, 2014 23:37, edited 1 time in total.

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

Lua is stupid.

It uses error instead of throw. Fixed.

Also, if you look at the error:

Form <name> already exists!

You have tried making the same form twice.

eg: you made two forms called foo:bar


As for button heights, it should work.
Last edited by rubenwardy on Fri Feb 21, 2014 17:05, edited 1 time in total.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
domtron vox
Member
Posts: 111
Joined: Thu Feb 20, 2014 21:07
GitHub: DomtronVox
IRC: Domtron
In-game: Domtron

by domtron vox » Post

rubenwardy wrote:Fixed.
Always a nice thing to hear.
rubenwardy wrote:You have tried making the same form twice.
Yep, I understood since your error is quite clear. I was just referring to the throw vs. error thing.
rubenwardy wrote:As for button heights, it should work.
Then I have no clue what I'm doing wrong. :/


I'm interested in eventually using tables for my mod. I think tables were added after last stable release. If so: are you planning on implementing tables or are you aiming for compatibility with the current minetest stable?

Anyway, don't be rushed on my account. At this stage labels are working quite nicely and your library is exactly what I need. Thanks for making it.

User avatar
domtron vox
Member
Posts: 111
Joined: Thu Feb 20, 2014 21:07
GitHub: DomtronVox
IRC: Domtron
In-game: Domtron

by domtron vox » Post

Hi, I did some documentation for you. Use it, lose it, or shred it. It is yours to do with as you please. I'll write up a section on creating new elements once I get around to trying it out. ;)

I'm not 100% sure how accurate it is.

If you don't use it I would like to point out that some of your function descriptions of the label and field elements refer to button instead of label/field.
Label
...
  • element:setText( text ) - set the caption of the button
Edit: For some reason the file wouldn't upload so I put it in a spoiler:
Spoiler
#Using Smart Formspec
Smartfs provides 2nd generation Minetest forms to replace clunky formspec strings. Each smartfs form is a container filled with GUI elements. A number of default elements are included with smartfs, but modders can also define their own custom elements. This document describes the basic usage of the smartfs API.

##Installation
Smartfs can be used as a library or a mod.

To use smartfs as a library, copy the smartfs.lua file to your mod folder and add
dofile(minetest.get\_modpath(minetest.get\_current\_modname()).."/smartfs.lua")
to the top of any files that use it.

To use smartfs as a mod, add it to your game's mods folder or to the user mods folder and enable it.

## Creating Forms
A form is a rectangular area of the screen upon which all elements are placed. Use the smartfs.create() function to create a new form. This function takes two arguments and returns a form object.

The first argument is a unique string that identifies the form. The second argument is a function that should take a single argument called state which is used to set form properties like size and background color. State also has constructors for all form elements and can be used with state:element_name. Below is a quick example.

form_name = smartfs.create("My Form",
function(state)
--sets the form's size
-- (width, hieght)
state:size(5,5)

--creates a label and places it on the form
--(x-pos, y-pos, name, text)
state:label(3,3,"ly label", "A label!")
end)
## Showing Forms
Forms can be shown to the player by using the show(target) function. The target argument is the name of the player that will see the form.

form_name:show("singleplayer")


## Inventory Support
Smartfs supports adding a button to Inventory+ or Unified Inventory which will open one of your own custom forms. Use the smartfs.add\_to\_inventory(form, icon, title) function where form is the smartfs form linked to by the button, icon is the button image(only for unified inventory), and title is the button text(only for inventory+.

smartfs.add_to_inventory(form_name, "my form icon.png", "Open My Form")

##Creating New Elements


#Full API
##Smartfs

* create( name,function ) - creates a new form with name and adds elements to it by running function.
* add\_to\_inventory( name,icon,title ) - adds a button with image icon if unified inventory or text title if inventory+ that links to the form belonging to the given name.

##Form

* state:size( width,height ) - sets the forms width and height.

##Button

###Creation

* state:button( x,y,w,h,name,text ) - create a new button at x,y with name and caption (text)

###Manipulation

* element:setPosition( x,y ) - change the position
* element:getPosition() - get the current position
* element:setSize( w,h ) - set the size
* element:getSize() - get the size
* element:setText( text ) - set the caption of the button
* element:getText() - get the caption of the button
* element:setImage( filename ) - sets the background of the button
* element:getImage() - get the background filename of the button

###Event Handling

* element:onClick( func(self,state) ) - specify a function to run when the button is clicked

##Toggle Button

###Creation

* state:toggle( x,y,w,h,name,list ) - create a new toggle button at x,y with name and possible list of values

###Manipulation

* element:setPosition( x,y ) - change the position
* element:getPosition() - get the current position
* element:setSize( w,h ) - set the size
* element:getSize() - get the size
* element:getText() - get the text of the toggle option
* element:setId( filename ) - sets the selected id
* element:getId() - get the selected id

###Event Handling

* element:onToggle( func(self,state) ) - specify a function to run when the value if toggled

##Label

###Creation

* state:label( x,y,name,text ) - create a new label at x,y with name and caption (text)

###Manipulation

* element:setPosition( x,y ) - change the position
* element:getPosition() - get the current position
* element:setText( text ) - set the caption of the label
* element:getText() - get the caption of the label

##Field and Text Area
###Creation

* state:field( x,y,w,h,name,label ) - create a new field at x,y with label
* state:pwdfield( x,y,w,h,name,label ) - create a password field
* state:textarea( x,y,w,h,name,label ) - create a new textarea

###Manipulation

* element:setPosition( x,y ) - change the position
* element:getPosition() - get the current position
* element:setSize( w,h ) - set the size
* element:getSize() - get the size
* element:setText( text ) - set the caption of the button
* element:getText() - get the caption of the field
* element:setImage( filename ) - sets the background of the field
* element:getImage() - get the background filename of the field

###Event Handling

* element:onClick( func(self,state) ) - specify a function to run when the field is clicked
Last edited by domtron vox on Thu Feb 27, 2014 21:46, edited 1 time in total.

User avatar
webdesigner97
Member
Posts: 1328
Joined: Mon Jul 30, 2012 19:16
GitHub: webD97
IRC: webdesigner97
In-game: webdesigner97
Location: Cologne, Germany
Contact:

by webdesigner97 » Post

Would this allow to run function on button click like this one:

Code: Select all

button:onlick(callAFunction(withButtonRelatedParam))
I hope you understand what I mean: I have a formspec with let's say 10 buttons. Whenever one of these buttons gets clicked, it should run a function with a parameter based on its e.g. label text.

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

Look good!

I will add this later. Good work!
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

User avatar
domtron vox
Member
Posts: 111
Joined: Thu Feb 20, 2014 21:07
GitHub: DomtronVox
IRC: Domtron
In-game: Domtron

by domtron vox » Post

webdesigner97 wrote:Would this allow to run function on button click like this one:

Code: Select all

button:onlick(callAFunction(withButtonRelatedParam))
if button is a variable that points to an element. For example if button is set using either

Code: Select all

local button = state:button(0,0, 1,4, "btn","a button")
or

Code: Select all

local button = state:get("btn")--if the button you want to get is called "btn"
in a form creation callback function(the function you pass to smartfs.create) then

Code: Select all

button:onlick(callAFunction(withButtonRelatedParam))
would work.

Here is a working example. Each time either button is pressed it adds a label to the form underneath it:

EDIT:I tweaked the code since rubenwardy said this would crash if multiple players joined a game. I tested it with a local host server and two clients.

Code: Select all

dofile(minetest.get_modpath(minetest.get_current_modname()).."/smartfs.lua")

minetest.register_on_joinplayer(function(player)

    local form = smartfs.create("test@"..player:get_player_name(),
                   function(state)

                       state:size(8,8)

                       --set variable using creation assignment
                       local button1 = state:button(0,0, 2,1, "btn1","A button")
                  
                       --set variable using state:get
                       state:button(2,0, 3,1, "btn2","Another button")
                       local button2 = state:get("btn2")

                       --incremented so each new label is in the right spot
                       local ypos1 = 2
                       local ypos2 = 2
                       
                       --button one on clicked function
                       state:get("btn1"):onClick(function(self, state)
                           state:label(0,ypos1, "lable1"..ypos1, "A label!")
                           ypos1 = ypos1 + 1
                       end)

                       --button two on clicked function
                       button2:onClick(function(self, state)
                           state:label(2,ypos2, "lable2"..ypos2, "A label!")
                           ypos2 = ypos2 + 1
                       end)                       


                   end)

    form:show(player:get_player_name())

end)
So far I haven't found a way to edit the elements of a form after creation all editing needs to be defined inside the creation callback.
Last edited by domtron vox on Tue Mar 04, 2014 15:40, edited 1 time in total.

User avatar
domtron vox
Member
Posts: 111
Joined: Thu Feb 20, 2014 21:07
GitHub: DomtronVox
IRC: Domtron
In-game: Domtron

by domtron vox » Post

And since I took the time to figure that out here are the docs on it. I put them after showing forms:
Spoiler
## Modifying Elements
Elements have functions of the form element:function(args) where you need to have access to the element object.

You can get the element object by assigning a variable to its creation function like so:

local button1 = state:button(0,0, 1,4, "btn1", "A button")
--button1 is now a table representing the button

You can also get the element by using state:get(name). The example below will retrieve a button with the name "btn1":

button1 = state:get("btn1")
--or
state:get("btn1"):onClick(your\_onclick\_function

Both of these methods need to be used inside the form creation callback function, the function you pass to smartfs.create.

Now that you have located your element you can modify it.

button1:setPos(4,0)
Again it is in .md format and yours to do with as you please.

User avatar
rubenwardy
Moderator
Posts: 6972
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy
Location: Bristol, United Kingdom
Contact:

by rubenwardy » Post

You could edit stuff outside of the callbacks, by getting the stored states.

Code: Select all

state = smartfs.opened[name]
-- gets the open state or null.

inv = smartfs.inv[name]
-- gets the open inventory smartfs, if set.
You could then do

Code: Select all

Btn = state:get("name")
Btn:setText("new")

State:_show_()
I will add a function for this, in the future, so you don't call a private function.
Last edited by rubenwardy on Mon Mar 03, 2014 19:00, edited 1 time in total.
Renewed Tab (my browser add-on) | Donate | Mods | Minetest Modding Book

Hello profile reader

Post Reply

Who is online

Users browsing this forum: No registered users and 22 guests