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

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

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

by rubenwardy » Mon Oct 28, 2013 15:33

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:

+ Example


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.
 

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

by fairiestoy » Mon Oct 28, 2013 19:04

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: 4812
Joined: Tue Jun 12, 2012 18:11
Location: United Kingdom
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy

by rubenwardy » Mon Oct 28, 2013 19:07

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.
 

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

by fairiestoy » Mon Oct 28, 2013 19:13

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: 525
Joined: Thu Jun 06, 2013 03:19

by LionsDen » Tue Oct 29, 2013 00:05

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
Developer
 
Posts: 4026
Joined: Fri Jul 20, 2012 16:19
Location: Germany
GitHub: PilzAdam
IRC: PilzAdam

by PilzAdam » Tue Oct 29, 2013 09:57

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 » Tue Oct 29, 2013 11:08

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: 4812
Joined: Tue Jun 12, 2012 18:11
Location: United Kingdom
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy

by rubenwardy » Tue Oct 29, 2013 14:51

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.
 

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

by rubenwardy » Tue Oct 29, 2013 15:15

Unfortunately state:close() does not currently work as there is no way to do this in the api.
 

User avatar
cHyper
Member
 
Posts: 629
Joined: Fri May 06, 2011 08:49
Location: Austria
IRC: cHyper
In-game: cHyper

by cHyper » Tue Nov 05, 2013 13:20

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?
--------------------------------------------------------
Specs
---------
OS - Win7 64bit and Linux Mint 18.1 64bit; Processor - Intel Core I7-720QM 1,6 ghz x4;memory - 6.144 mb; Graphics Card - NVIDIA GeForce GTS 250M 1 gb ram; 500 gb SSD;
 

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

by rubenwardy » Tue Nov 05, 2013 13:25

This element has not been added yet.

https://github.com/minetest/minetest/blob/master/doc/lua_api.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.
 

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

by rubenwardy » Tue Nov 05, 2013 13:30

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)
 

User avatar
cHyper
Member
 
Posts: 629
Joined: Fri May 06, 2011 08:49
Location: Austria
IRC: cHyper
In-game: cHyper

by cHyper » Tue Nov 05, 2013 13:41

rubenwardy wrote:This element has not been added yet.

https://github.com/minetest/minetest/blob/master/doc/lua_api.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?
--------------------------------------------------------
Specs
---------
OS - Win7 64bit and Linux Mint 18.1 64bit; Processor - Intel Core I7-720QM 1,6 ghz x4;memory - 6.144 mb; Graphics Card - NVIDIA GeForce GTS 250M 1 gb ram; 500 gb SSD;
 

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

by rubenwardy » Tue Nov 05, 2013 14:58

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.
 

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

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

by rubenwardy » Thu Feb 20, 2014 20:37

[h]Update![/h]

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.
 

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

by domtron vox » Thu Feb 20, 2014 22:42

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: 4812
Joined: Tue Jun 12, 2012 18:11
Location: United Kingdom
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy

by rubenwardy » Fri Feb 21, 2014 17:00

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.
 

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

by domtron vox » Fri Feb 21, 2014 19:27

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: 106
Joined: Thu Feb 20, 2014 21:07
GitHub: DomtronVox
IRC: Domtron
In-game: Domtron

by domtron vox » Thu Feb 27, 2014 21:44

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
[/spoiler]
Last edited by domtron vox on Thu Feb 27, 2014 21:46, edited 1 time in total.
 

User avatar
webdesigner97
Member
 
Posts: 1321
Joined: Mon Jul 30, 2012 19:16
Location: Bergisch Gladbach, Germany
GitHub: webD97
IRC: webdesigner97
In-game: webdesigner97

by webdesigner97 » Fri Feb 28, 2014 11:39

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: 4812
Joined: Tue Jun 12, 2012 18:11
Location: United Kingdom
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy

by rubenwardy » Fri Feb 28, 2014 11:40

Look good!

I will add this later. Good work!
 

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

by domtron vox » Fri Feb 28, 2014 19:56

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: 106
Joined: Thu Feb 20, 2014 21:07
GitHub: DomtronVox
IRC: Domtron
In-game: Domtron

by domtron vox » Fri Feb 28, 2014 20:15

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)[/spoiler]

Again it is in .md format and yours to do with as you please.
 

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

by rubenwardy » Fri Feb 28, 2014 22:51

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.
 

Next

Return to Mod Releases



Who is online

Users browsing this forum: SaKeL and 7 guests