1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

Add formspec toolkit and refactor mainmenu to use it

Fix crash on using cursor keys in client menu without selected server
Add support for non fixed size tabviews
This commit is contained in:
sapier 2014-04-18 15:39:15 +02:00
parent 34d872628d
commit c3984569c0
27 changed files with 3518 additions and 2500 deletions

171
doc/fst_api.txt Normal file
View file

@ -0,0 +1,171 @@
Formspec toolkit api 0.0.3
==========================
Formspec toolkit is a set of functions to create basic ui elements.
File: fst/ui.lua
----------------
ui.lua adds base ui interface to add additional components to.
ui.add(component) -> returns name of added component
^ add component to ui
^ component: component to add
ui.delete(component) -> true/false if a component was deleted or not
^ remove a component from ui
^ component: component to delete
ui.set_default(name)
^ set component to show if not a single component is set visible
^ name: name of component to set as default
ui.find_by_name(name) --> returns component or nil
^ find a component within ui
^ name: name of component to look for
File: fst/tabview.lua
---------------------
tabview_create(name, size, tabheaderpos) --> returns tabview component
^ create a new tabview component
^ name: name of tabview (has to be unique per ui)
^ size: size of tabview
{
x,
y
}
^ tabheaderpos: upper left position of tabheader (relative to upper left fs corner)
{
x,
y
}
Class reference tabview:
methods:
- add_tab(tab)
^ add a tab to this tabview
^ tab:
{
name = "tabname", -- name of tab to create
caption = "tab caption", -- text to show for tab header
cbf_button_handler = function(tabview, fields, tabname, tabdata), -- callback for button events
--TODO cbf_events = function(tabview, event, tabname), -- callback for events
cbf_formspec = function(tabview, name, tabdata), -- get formspec
tabsize =
{
x, -- x width
y -- y height
}, -- special size for this tab (only relevant if no parent for tabview set)
on_change = function(type,old_tab,new_tab) -- called on tab chang, type is "ENTER" or "LEAVE"
}
- set_autosave_tab(value)
^ tell tabview to automaticaly save current tabname as "tabview_name"_LAST
^ value: true/false
- set_tab(name)
^ set's tab to tab named "name", returns true/false on success
^ name: name of tab to set
- set_global_event_handler(handler)
^ set a handler to be called prior calling tab specific event handler
^ handler: function(tabview,event) --> returns true to finish event processing false to continue
- set_global_button_handler(handler)
^ set a handler to be called prior calling tab specific button handler
^ handler: function(tabview,fields) --> returns true to finish button processing false to continue
- set_parent(parent)
^ set parent to attach tabview to. TV's with parent are hidden if their parent
is hidden and they don't set their specified size.
^ parent: component to attach to
- show()
^ show tabview
- hide()
^ hide tabview
- delete()
^ delete tabview
- set_fixed_size(state)
^ true/false set to fixed size, variable size
File: fst/dialog.lua
---------------------
Only one dialog can be shown at a time. If a dialog is closed it's parent is
gonna be activated and shown again.
dialog_create(name, cbf_formspec, cbf_button_handler, cbf_events)
^ create a dialog component
^ name: name of component (unique per ui)
^ cbf_formspec: function to be called to get formspec
function(dialogdata)
^ cbf_button_handler: function to handle buttons
function(dialog, fields)
^ cbf_events: function to handle events
function(dialog, event)
Class reference dialog:
methods:
- set_parent(parent)
^ set parent to attach a dialog to
^ parent: component to attach to
- show()
^ show dialog
- hide()
^ hide dialog
- delete()
^ delete dialog from ui
members:
- data
^ variable data attached to this dialog
- parent
^ parent component to return to on exit
File: fst/buttonbar.lua
-----------------------
buttonbar_create(name, cbf_buttonhandler, pos, orientation, size)
^ create a buttonbar
^ name: name of component (unique per ui)
^ cbf_buttonhandler: function to be called on button pressed
function(buttonbar,buttonname,buttondata)
^ pos: position relative to upper left of current shown formspec
{
x,
y
}
^ orientation: "vertical" or "horizontal"
^ size: size of bar
{
width,
height
}
Class reference buttonbar:
methods:
- add_button(btn_id, caption, button_image)
- set_parent(parent)
^ set parent to attach a buttonbar to
^ parent: component to attach to
- show()
^ show buttonbar
- hide()
^ hide buttonbar
- delete()
^ delete buttonbar from ui
Developer doc:
==============
Skeleton for any component:
{
name = "some id", -- unique id
type = "toplevel", -- type of component
-- toplevel: componant can be show without additional components
-- addon: component is an addon to be shown along toplevel component
hide = function(this) end, -- called to hide the component
show = function(this) end, -- called to show the component
delete = function(this) end, -- called to delete component from ui
handle_buttons = function(this,fields) -- called upon button press
handle_events = function(this,event) -- called upon event reception
get_formspec = function(this) -- has to return formspec to be displayed
}

View file

@ -1,4 +1,4 @@
Minetest Lua Mainmenu API Reference 0.4.9
Minetest Lua Mainmenu API Reference 0.4.10
========================================
Introduction
@ -8,14 +8,14 @@ Description of formspec language to show your menu is in lua_api.txt
Callbacks
---------
engine.buttonhandler(fields): called when a button is pressed.
core.buttonhandler(fields): called when a button is pressed.
^ fields = {name1 = value1, name2 = value2, ...}
engine.event_handler(event)
core.event_handler(event)
^ event: "MenuQuit", "KeyEnter", "ExitButton" or "EditBoxEnter"
Gamedata
--------
The "gamedata" table is read when calling engine.start(). It should contain:
The "gamedata" table is read when calling core.start(). It should contain:
{
playername = <name>,
password = <password>,
@ -27,15 +27,15 @@ The "gamedata" table is read when calling engine.start(). It should contain:
Functions
---------
engine.start()
engine.close()
core.start()
core.close()
Filesystem:
engine.get_scriptdir()
core.get_scriptdir()
^ returns directory of script
engine.get_modpath() (possible in async calls)
core.get_modpath() (possible in async calls)
^ returns path to global modpath
engine.get_modstore_details(modid) (possible in async calls)
core.get_modstore_details(modid) (possible in async calls)
^ modid numeric id of mod in modstore
^ returns {
id = <numeric id of mod in modstore>,
@ -47,7 +47,7 @@ engine.get_modstore_details(modid) (possible in async calls)
license = <short description of license>,
rating = <float value of current rating>
}
engine.get_modstore_list() (possible in async calls)
core.get_modstore_list() (possible in async calls)
^ returns {
[1] = {
id = <numeric id of mod in modstore>,
@ -55,60 +55,60 @@ engine.get_modstore_list() (possible in async calls)
basename = <basename for mod>
}
}
engine.get_gamepath() (possible in async calls)
core.get_gamepath() (possible in async calls)
^ returns path to global gamepath
engine.get_texturepath() (possible in async calls)
core.get_texturepath() (possible in async calls)
^ returns path to default textures
engine.get_dirlist(path,onlydirs) (possible in async calls)
core.get_dirlist(path,onlydirs) (possible in async calls)
^ path to get subdirs from
^ onlydirs should result contain only dirs?
^ returns list of folders within path
engine.create_dir(absolute_path) (possible in async calls)
core.create_dir(absolute_path) (possible in async calls)
^ absolute_path to directory to create (needs to be absolute)
^ returns true/false
engine.delete_dir(absolute_path) (possible in async calls)
core.delete_dir(absolute_path) (possible in async calls)
^ absolute_path to directory to delete (needs to be absolute)
^ returns true/false
engine.copy_dir(source,destination,keep_soure) (possible in async calls)
core.copy_dir(source,destination,keep_soure) (possible in async calls)
^ source folder
^ destination folder
^ keep_source DEFAULT true --> if set to false source is deleted after copying
^ returns true/false
engine.extract_zip(zipfile,destination) [unzip within path required]
core.extract_zip(zipfile,destination) [unzip within path required]
^ zipfile to extract
^ destination folder to extract to
^ returns true/false
engine.download_file(url,target) (possible in async calls)
core.download_file(url,target) (possible in async calls)
^ url to download
^ target to store to
^ returns true/false
engine.get_version() (possible in async calls)
core.get_version() (possible in async calls)
^ returns current core version
engine.sound_play(spec, looped) -> handle
core.sound_play(spec, looped) -> handle
^ spec = SimpleSoundSpec (see lua-api.txt)
^ looped = bool
engine.sound_stop(handle)
core.sound_stop(handle)
Formspec:
engine.update_formspec(formspec)
engine.get_table_index(tablename) -> index
core.update_formspec(formspec)
core.get_table_index(tablename) -> index
^ can also handle textlists
engine.formspec_escape(string) -> string
core.formspec_escape(string) -> string
^ escapes characters [ ] \ , ; that can not be used in formspecs
engine.explode_table_event(string) -> table
core.explode_table_event(string) -> table
^ returns e.g. {type="CHG", row=1, column=2}
^ type: "INV" (no row selected), "CHG" (selected) or "DCL" (double-click)
engine.explode_textlist_event(string) -> table
core.explode_textlist_event(string) -> table
^ returns e.g. {type="CHG", index=1}
^ type: "INV" (no row selected), "CHG" (selected) or "DCL" (double-click)
GUI:
engine.set_background(type, texturepath)
core.set_background(type, texturepath)
^ type: "background", "overlay", "header" or "footer"
engine.set_clouds(<true/false>)
engine.set_topleft_text(text)
engine.show_keys_menu()
engine.file_open_dialog(formname,caption)
core.set_clouds(<true/false>)
core.set_topleft_text(text)
core.show_keys_menu()
core.file_open_dialog(formname,caption)
^ shows a file open dialog
^ formname is base name of dialog response returned in fields
^ -if dialog was accepted "_accepted"
@ -116,7 +116,7 @@ engine.file_open_dialog(formname,caption)
^ -if dialog was canceled "_cancelled"
^ will be added to fieldname value is set to formname itself
^ returns nil or selected file/folder
engine.get_screen_info()
core.get_screen_info()
^ returns {
density = <screen density 0.75,1.0,2.0,3.0 ... (dpi)>,
display_width = <width of display>,
@ -126,7 +126,7 @@ engine.get_screen_info()
}
Games:
engine.get_game(index)
core.get_game(index)
^ returns {
id = <id>,
path = <full path to game>,
@ -136,10 +136,10 @@ engine.get_game(index)
DEPRECATED:
addon_mods_paths = {[1] = <path>,},
}
engine.get_games() -> table of all games in upper format (possible in async calls)
core.get_games() -> table of all games in upper format (possible in async calls)
Favorites:
engine.get_favorites(location) -> list of favorites (possible in async calls)
core.get_favorites(location) -> list of favorites (possible in async calls)
^ location: "local" or "online"
^ returns {
[1] = {
@ -156,24 +156,24 @@ engine.get_favorites(location) -> list of favorites (possible in async calls)
port = <port>
},
}
engine.delete_favorite(id, location) -> success
core.delete_favorite(id, location) -> success
Logging:
engine.debug(line) (possible in async calls)
core.debug(line) (possible in async calls)
^ Always printed to stderr and logfile (print() is redirected here)
engine.log(line) (possible in async calls)
engine.log(loglevel, line) (possible in async calls)
core.log(line) (possible in async calls)
core.log(loglevel, line) (possible in async calls)
^ loglevel one of "error", "action", "info", "verbose"
Settings:
engine.setting_set(name, value)
engine.setting_get(name) -> string or nil (possible in async calls)
engine.setting_setbool(name, value)
engine.setting_getbool(name) -> bool or nil (possible in async calls)
engine.setting_save() -> nil, save all settings to config file
core.setting_set(name, value)
core.setting_get(name) -> string or nil (possible in async calls)
core.setting_setbool(name, value)
core.setting_getbool(name) -> bool or nil (possible in async calls)
core.setting_save() -> nil, save all settings to config file
Worlds:
engine.get_worlds() -> list of worlds (possible in async calls)
core.get_worlds() -> list of worlds (possible in async calls)
^ returns {
[1] = {
path = <full path to world>,
@ -181,16 +181,16 @@ engine.get_worlds() -> list of worlds (possible in async calls)
gameid = <gameid of world>,
},
}
engine.create_world(worldname, gameid)
engine.delete_world(index)
core.create_world(worldname, gameid)
core.delete_world(index)
Helpers:
engine.gettext(string) -> string
core.gettext(string) -> string
^ look up the translation of a string in the gettext message catalog
fgettext(string, ...) -> string
^ call engine.gettext(string), replace "$1"..."$9" with the given
^ extra arguments, call engine.formspec_escape and return the result
engine.parse_json(string[, nullvalue]) -> something (possible in async calls)
^ call core.gettext(string), replace "$1"..."$9" with the given
^ extra arguments, call core.formspec_escape and return the result
core.parse_json(string[, nullvalue]) -> something (possible in async calls)
^ see core.parse_json (lua_api.txt)
dump(obj, dumped={})
^ Return object serialized as a string
@ -202,7 +202,7 @@ core.is_yes(arg) (possible in async calls)
^ returns whether arg can be interpreted as yes
Async:
engine.handle_async(async_job,parameters,finished)
core.handle_async(async_job,parameters,finished)
^ execute a function asynchronously
^ async_job is a function receiving one parameter and returning one parameter
^ parameters parameter table passed to async_job
@ -212,8 +212,8 @@ engine.handle_async(async_job,parameters,finished)
Limitations of Async operations
-No access to global lua variables, don't even try
-Limited set of available functions
e.g. No access to functions modifying menu like engine.start,engine.close,
engine.file_open_dialog
e.g. No access to functions modifying menu like core.start,core.close,
core.file_open_dialog
Class reference