First commit
commit
4b6093a706
|
@ -0,0 +1,2 @@
|
|||
nofs_lib
|
||||
nofs_extra?
|
|
@ -0,0 +1,137 @@
|
|||
--[[
|
||||
nofs_demo for Minetest - Demonstration mod for nofs_lib
|
||||
(c) Pierre-Yves Rollo
|
||||
|
||||
This file is part of nofs_lib.
|
||||
|
||||
signs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
signs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with signs. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
nofs_demo = {}
|
||||
nofs_demo.name = minetest.get_current_modname()
|
||||
nofs_demo.path = minetest.get_modpath(nofs_demo.name)
|
||||
|
||||
--dofile(nofs.path..'/stack.lua')
|
||||
|
||||
local itemlist={}
|
||||
|
||||
minetest.after(0, function()
|
||||
for name, item in pairs(minetest.registered_items) do
|
||||
if item.description and item.description ~= "" and name ~= "air" then
|
||||
table.insert(itemlist, name)
|
||||
end
|
||||
end
|
||||
table.sort(itemlist)
|
||||
end
|
||||
)
|
||||
--[[
|
||||
new_form('test_form:itemlist', function(context)
|
||||
if not context.page then context.page = 1 end
|
||||
|
||||
local formspec
|
||||
local grid = { x = 8, y = 7 }
|
||||
local pagesize = grid.x * grid.y
|
||||
local lastpage = math.ceil(#itemlist / pagesize)
|
||||
|
||||
if context.page < 1 then context.page = 1 end
|
||||
if context.page > lastpage then context.page = lastpage end
|
||||
|
||||
local count = pagesize * (context.page - 1)
|
||||
|
||||
formspec = "size[8,9]label[0,0;Page "..context.page.." of "..lastpage.."]"
|
||||
|
||||
local x, y
|
||||
for y = 1, grid.y do
|
||||
for x = 1, grid.x do
|
||||
count = count + 1
|
||||
if count < #itemlist then
|
||||
formspec = formspec.."item_image_button["..(x-1)..","..y..";1,1;"..itemlist[count]..";test_form:bi_"..itemlist[count]..";]"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
formspec = formspec.."button[6,0;1,1;b_prev;<]button[7,0;1,1;b_next;>]button_exit[7,8;1,1;button_exit;Exit]"
|
||||
|
||||
return formspec
|
||||
end)
|
||||
|
||||
set_form_callback('test_form:itemlist', 'b_prev', function(formname, player, fields, context)
|
||||
context.page = context.page - 1
|
||||
refresh_form(formname, player)
|
||||
end)
|
||||
|
||||
set_form_callback('test_form:itemlist', 'b_next', function(formname, player, fields, context)
|
||||
context.page = context.page + 1
|
||||
-- refresh_form(formname, player)
|
||||
refresh_form('test_form:test', player)
|
||||
end)
|
||||
|
||||
new_form('test_form:test', function(context)
|
||||
return render_form(test_form)
|
||||
--return "size[3,3]button[0,0;3,1;item;Item chooser]button[0,1;3,1;test;Test]button_exit[0,2;3,1;button_exit;Exit]"
|
||||
end)
|
||||
|
||||
set_form_callback('test_form:test', 'item', function(formname, player, fields, context)
|
||||
stack_form ("test_form:itemlist", player:get_player_name())
|
||||
end)
|
||||
|
||||
set_form_callback('test_form:test', 'test', function(formname, player, fields, context)
|
||||
minetest.show_formspec(player_name(player), "", "")
|
||||
end)
|
||||
--]]
|
||||
|
||||
-- TODO :: PROBLEME : Les tableaux associatifs ne sont pas parcourrus dans l'ordre
|
||||
|
||||
local main_form = {
|
||||
id = 'test_form',
|
||||
type = 'vbox',
|
||||
{ type = 'hbox',
|
||||
{ type = 'label', width = 2, height = 1, label = 'A simple text', },
|
||||
{ type = 'button', width = 2, height = 1, label = 'Test',
|
||||
on_action = function() print ('Button test clicked') end,
|
||||
},
|
||||
{ type = 'button', width = 2, height = 1, label = 'Test 2',
|
||||
on_action = function() print ('Button test 2 clicked') end,
|
||||
},
|
||||
},
|
||||
{ type = 'hbox',
|
||||
{ id = 'field 1', type = 'field', height = 1, width = 4,
|
||||
label = 'Field', default = 'toto',},
|
||||
{ type = 'button', height = 1, width = 1, label = 'Exit', exit = 'true',
|
||||
on_action = nofs.close_form, },
|
||||
},
|
||||
{ type = "checkbox", id = "chk1", label = "Select this", },
|
||||
}
|
||||
|
||||
local function is_player_connected(name)
|
||||
if minetest.player_exists(name)
|
||||
then
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
if name == player:get_player_name() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("nofs", { params = "", description = "NOFS demo",
|
||||
func = function(name, param)
|
||||
if is_player_connected(name)
|
||||
then
|
||||
nofs.show_form(name, main_form)
|
||||
end
|
||||
end,
|
||||
}
|
||||
)
|
|
@ -0,0 +1,86 @@
|
|||
# Items by type
|
||||
|
||||
## Common to all
|
||||
|
||||
- **id**: Unique (in the form) name of the widget. Default: key of the list element.
|
||||
- **type**: Type of widget
|
||||
|
||||
## Container
|
||||
|
||||
- **layout**: Layout of the container, ''vbox'' vertical box (default), ''hbox'', horizontal box
|
||||
|
||||
## Button
|
||||
|
||||
- **height**, **width** : Size of the button
|
||||
- **label**: Label displayed on the button
|
||||
- **image**: Image displayed on the button
|
||||
- **item**: Item displayed on the button
|
||||
|
||||
A button can't have both **image** and **item** attributes. In such case **item** attribute is ignored.
|
||||
|
||||
- **exit**: If true, the button will exit on click. Doesn't work if button has an **item** attribute. **close_form** can be used for such buttons.
|
||||
|
||||
## Field
|
||||
|
||||
- **height**, **width** : Size of the field
|
||||
- **default**: Default value for this field if empty
|
||||
- **hidden**: If true, the field will not display text (password field)
|
||||
|
||||
## Label
|
||||
|
||||
- **height**, **width** : Size of the label. Actually for placement purpose, the text is not bound by this size
|
||||
- **label**: Text displayed
|
||||
- **direction**: ''horizontal'' (default) or ''vertical'' text direction
|
||||
|
||||
|
||||
|
||||
|
||||
- **context**: Context var ?
|
||||
- **meta**: Associated metadata if <-- Voir comment on peut faire ça; associer un noeud au contexte ?
|
||||
|
||||
|
||||
|
||||
| Formspec element | nofs widget | Status |
|
||||
| --- | --- |
|
||||
| size[W,H] | Form level | Done |
|
||||
| position[X,Y] | Form level | Not started |
|
||||
| anchor[X,Y] | Form level | Not started |
|
||||
| no_prepend[] | Form level | Not started |
|
||||
| container[X,Y] | | Useless |
|
||||
| container_end[] | | Useless |
|
||||
| list[invloc;listname;X,Y;W,H;] | ? | Not started |
|
||||
| list[invloc;listname;X,Y;W,H;index] | ? | Not started |
|
||||
| listring[invloc;listname] | ? | Not started |
|
||||
| listring[] | ? | Not started |
|
||||
| listcolors[bgcolor;bgcolorhover] | ? | Not started |
|
||||
| listcolors[bgcolor;bgcolorhover;border] | ? | Not started |
|
||||
| listcolors[bgcolor;bgcolorhover;border;tooltip_bg;tooltip_fontcolor] | ? | Not started |
|
||||
| tooltip[id;text;bgcolor;fontcolor] | ? | Not started |
|
||||
| tooltip[X,Y;W,H;text;bgcolor;fontcolor] | ? | Not started |
|
||||
| image[X,Y;W,H;texture] | ? | Not started |
|
||||
| item_image[X,Y;W,H;itemname] | ? | Not started |
|
||||
| bgcolor[color;fullscreen] | Form level | Not started |
|
||||
| background[X,Y;W,H;texture] | Form level | Not started |
|
||||
| background[X,Y;W,H;texture;auto_clip] | Form level | Not started |
|
||||
| pwdfield[X,Y;W,H;name;label] | field | Done |
|
||||
| field[X,Y;W,H;name;label;default] | field | Done |
|
||||
| field[name;label;default] | field | Done |
|
||||
| field_close_on_enter[name;close_on_enter] | field | Not started |
|
||||
| textarea[X,Y;W,H;name;label;default] | ? | Not started |
|
||||
| label[X,Y;label] | label | Done |
|
||||
| vertlabel[X,Y;label] | label | Done |
|
||||
| button[X,Y;W,H;name;label] | button | Done |
|
||||
| image_button[X,Y;W,H;image;name;label] | button | Done |
|
||||
| image_button[X,Y;W,H;image;name;label;noclip;drawborder;pressedimage] | button | Not started |
|
||||
| item_image_button[X,Y;W,H;item name;name;label] | button | Done |
|
||||
| button_exit[X,Y;W,H;name;label] | button | Done |
|
||||
| image_button_exit[X,Y;W,H;image;name;label] | button | Done |
|
||||
| textlist[X,Y;W,H;name;listelem 1,listelem 2,...,listelem n] | ? | Not started |
|
||||
| tabheader[X,Y;name;caption 1,caption 2,...,caption n;current_tab;transparent;draw_border] | ? | Not started |
|
||||
| textlist[X,Y;W,H;name;listelem 1,listelem 2,...,listelem n;selected idx;transparent] | ? | Not started |
|
||||
| box[X,Y;W,H;color] | vbox/hbox | Not started |
|
||||
| checkbox[X,Y;label] | checkbox | WIP |
|
||||
| dropdown[X,Y;W;name;item 1,item 2, ...,item n;selected idx] | ? | Not started |
|
||||
| scrollbar[X,Y;W,H;orientation;name;value] | ? | Not started |
|
||||
| table[X,Y;W,H;name;cell 1,cell 2,...,cell n;selected idx] | ? | Not started |
|
||||
| tablecolumns[type 1,opt 1a,opt 1b,...;type 2,opt 2a,opt 2b;...] | ? Not started |
|
|
@ -0,0 +1 @@
|
|||
,pyrollo,localhost.localdomain,15.12.2018 12:49,file:///home/pyrollo/.config/libreoffice/4;
|
Binary file not shown.
|
@ -0,0 +1,58 @@
|
|||
--[[
|
||||
nofs_lib for Minetest - NO FormSpec API
|
||||
(c) Pierre-Yves Rollo
|
||||
|
||||
This file is part of nofs_lib.
|
||||
|
||||
signs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
signs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with signs. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
-- Event management
|
||||
-- ================
|
||||
|
||||
-- TODO : Trigger on forms and containers ? = if one of the decendant matches
|
||||
function nofs.trigger_event(player, form, fields, element, eventname)
|
||||
if element['on_'..eventname] and element.id and fields[element.id] then
|
||||
element['on_'..eventname](player, form, fields)
|
||||
end
|
||||
|
||||
if element.children then
|
||||
for index, child in ipairs(element.children) do
|
||||
nofs.trigger_event(player, form, fields, child, eventname)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Generic trigger propagation
|
||||
minetest.register_on_player_receive_fields(
|
||||
function(player, formname, fields)
|
||||
-- Find form
|
||||
local form = nofs.stack_get_by_id(player, formname)
|
||||
if form then
|
||||
-- Trigger on_click event
|
||||
nofs.trigger_event(player, form, fields, form, 'action')
|
||||
|
||||
-- If form exit, unstack
|
||||
if fields.quit == "true" then
|
||||
-- Trigger on_close event
|
||||
nofs.trigger_event(player, form, fields, form, 'close')
|
||||
nofs.stack_remove(player)
|
||||
local form = nofs.stack_get_top(player)
|
||||
if form then
|
||||
nofs.refresh_form(form, player)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
|
@ -0,0 +1,29 @@
|
|||
--[[
|
||||
nofs_lib for Minetest - NO FormSpec API
|
||||
(c) Pierre-Yves Rollo
|
||||
|
||||
This file is part of nofs_lib.
|
||||
|
||||
signs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
signs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with signs. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
nofs = {}
|
||||
nofs.name = minetest.get_current_modname()
|
||||
nofs.path = minetest.get_modpath(nofs.name)
|
||||
|
||||
dofile(nofs.path..'/stack.lua')
|
||||
dofile(nofs.path..'/render.lua')
|
||||
dofile(nofs.path..'/events.lua')
|
||||
dofile(nofs.path..'/main.lua')
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
--[[
|
||||
nofs_lib for Minetest - NO FormSpec API
|
||||
(c) Pierre-Yves Rollo
|
||||
|
||||
This file is part of nofs_lib.
|
||||
|
||||
signs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
signs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with signs. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
-- Top level functions
|
||||
-- ===================
|
||||
|
||||
-- Refreshes the top form
|
||||
function nofs.refresh_form(player)
|
||||
local player = player
|
||||
local form = nofs.stack_get_top(player)
|
||||
if type(player) ~= "string" then
|
||||
player = player:get_player_name()
|
||||
end
|
||||
|
||||
if form then
|
||||
minetest.show_formspec(player, form.id,
|
||||
nofs.render_form(form))
|
||||
else
|
||||
-- Hide form
|
||||
minetest.show_formspec(player, "", "")
|
||||
end
|
||||
end
|
||||
|
||||
-- Show form
|
||||
|
||||
|
||||
|
||||
-- LE TRUC QUI NE VA PAS :
|
||||
-- Dans render, on met en place une instance de la form d'apres sa definition
|
||||
-- Mais toute la partie ID est utile pour la gestion des evenements. Il faudrait
|
||||
-- avoir deux objets : form def et form instance
|
||||
function nofs.show_form(player, form, params)
|
||||
-- TODO : test form validity
|
||||
nofs.stack_add(player, form)
|
||||
|
||||
if params then
|
||||
form.context['params'] = params
|
||||
end
|
||||
|
||||
nofs.trigger_event(player, form, { }, form, 'open')
|
||||
nofs.refresh_form(player)
|
||||
end
|
||||
|
||||
-- Close top form
|
||||
function nofs.close_form(player)
|
||||
local form = nofs.stack_get_top(player)
|
||||
if form then
|
||||
-- Like if "esc" key was pressed -- TODO: Check this
|
||||
nofs.trigger_event(player, form, { quit = "true" }, form, 'close')
|
||||
end
|
||||
nofs.stack_remove(player)
|
||||
nofs.refresh_form(player)
|
||||
end
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
--[[
|
||||
nofs_lib for Minetest - NO FormSpec API
|
||||
(c) Pierre-Yves Rollo
|
||||
|
||||
This file is part of nofs_lib.
|
||||
|
||||
signs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
signs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with signs. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
local widgets
|
||||
|
||||
-- Helpers
|
||||
local function message(message)
|
||||
minetest.log('warning', '['..nofs.name..'] '..message)
|
||||
end
|
||||
|
||||
-- Forward declarations
|
||||
--local set_defs_and_collect_ids, add_missing_ids,
|
||||
local size_element, render_element
|
||||
|
||||
|
||||
-- Standard offset position and size
|
||||
local function fspos(buffer, offset)
|
||||
local widgetoffset = widgets[buffer.def.type].offset
|
||||
if widgetoffset then
|
||||
return string.format("%g,%g",
|
||||
buffer.pos.x + offset.x + widgetoffset.x,
|
||||
buffer.pos.y + offset.y + widgetoffset.y)
|
||||
else
|
||||
return string.format("%g,%g",
|
||||
buffer.pos.x + offset.x, buffer.pos.y + offset.y)
|
||||
end
|
||||
end
|
||||
|
||||
local function fspossize(buffer, offset)
|
||||
local widgetoffset = widgets[buffer.def.type].offset
|
||||
if widgetoffset then
|
||||
return string.format("%g,%g;%g,%g",
|
||||
buffer.pos.x + offset.x + widgetoffset.x,
|
||||
buffer.pos.y + offset.y + widgetoffset.y,
|
||||
buffer.size.x, buffer.size.y)
|
||||
else
|
||||
return string.format("%g,%g;%g,%g",
|
||||
buffer.pos.x + offset.x, buffer.pos.y + offset.y,
|
||||
buffer.size.x, buffer.size.y)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sizing vbox and hbox
|
||||
--
|
||||
local function size_box(buffer, boxtype)
|
||||
buffer.size = { 0, 0 }
|
||||
|
||||
local main, other
|
||||
|
||||
-- Process vbox and hbox the same way, just inverting coordinates
|
||||
if boxtype == 'h' then
|
||||
main = 'x' other = 'y'
|
||||
else
|
||||
main = 'y' other = 'x'
|
||||
end
|
||||
|
||||
local pos = 0 -- TODO:MARGIN
|
||||
local size = 0
|
||||
|
||||
-- Main positionning and other size
|
||||
for index, child in ipairs(buffer.children) do
|
||||
size_element(buffer.children[index])
|
||||
buffer.children[index].pos[main] = pos
|
||||
pos = pos + buffer.children[index].size[main] -- TODO:Spacing
|
||||
if buffer.children[index].size[other] > size then
|
||||
size = buffer.children[index].size[other]
|
||||
end
|
||||
end
|
||||
|
||||
-- Size + margin*2
|
||||
|
||||
-- Other positionning
|
||||
for index, child in ipairs(buffer.children) do
|
||||
-- TODO: This is center, add left & right
|
||||
buffer.children[index].pos[other] =
|
||||
( size - buffer.children[index].size[other] ) / 2
|
||||
end
|
||||
|
||||
buffer.size[main] = pos
|
||||
buffer.size[other] = size
|
||||
end
|
||||
|
||||
-- Containers generic rendering
|
||||
--
|
||||
local function render_container(buffer, offset)
|
||||
local inneroffset = {
|
||||
x = offset.x + buffer.pos.x,
|
||||
y = offset.y + buffer.pos.y
|
||||
}
|
||||
local fs = ""
|
||||
for index, child in ipairs(buffer.children) do
|
||||
fs = fs..render_element(child, inneroffset)
|
||||
end
|
||||
return fs
|
||||
end
|
||||
|
||||
-- Widget definitions
|
||||
--
|
||||
|
||||
widgets = {
|
||||
vbox = {
|
||||
sizing = function(buffer)
|
||||
size_box(buffer, 'v')
|
||||
end,
|
||||
rendering = render_container,
|
||||
},
|
||||
hbox = {
|
||||
sizing = function(buffer)
|
||||
size_box(buffer, 'h')
|
||||
end,
|
||||
rendering = render_container,
|
||||
},
|
||||
button = {
|
||||
needs_id = true,
|
||||
rendering = function(buffer, offset)
|
||||
-- Some warnings
|
||||
if buffer.def.item ~= nil then
|
||||
if buffer.def.image ~= nil then
|
||||
message('WARNING: Button can\'t have "image" and "item" attributes at once. '..
|
||||
'Ignoring "item" attribute.')
|
||||
end
|
||||
if buffer.def.exit == 'true' then
|
||||
message('WARNING: Button can\'t have exit=true and item attributes at once. '..
|
||||
'Ignoring exit=true attribute.')
|
||||
end
|
||||
end
|
||||
|
||||
-- Now, render !
|
||||
if buffer.def.image then
|
||||
if buffer.def.exit == "true" then
|
||||
return string.format("image_button_exit[%s;%s;%s;%s]",
|
||||
fspossize(buffer, offset), buffer.def.image, buffer.id, buffer.def.label or "")
|
||||
else
|
||||
return string.format("image_button[%s;%s;%s;%s]",
|
||||
fspossize(buffer, offset), buffer.def.image, buffer.id, buffer.def.label or "")
|
||||
end
|
||||
elseif buffer.def.item then
|
||||
return string.format("item_image_button[%s;%s;%s;%s]",
|
||||
fspossize(buffer, offset), buffer.def.item, buffer.id, buffer.def.label or "")
|
||||
else
|
||||
if buffer.def.exit == "true" then
|
||||
return string.format("button_exit[%s;%s;%s]",
|
||||
fspossize(buffer, offset), buffer.id, buffer.def.label or "")
|
||||
else
|
||||
return string.format("button[%s;%s;%s]",
|
||||
fspossize(buffer, offset), buffer.id, buffer.def.label or "")
|
||||
end
|
||||
end
|
||||
end,
|
||||
},
|
||||
field = {
|
||||
needs_id = true,
|
||||
offset = { x = 0.3, y = 0.32 },
|
||||
rendering = function(buffer, offset)
|
||||
-- Some warnings
|
||||
if buffer.def.hidden == 'true' then
|
||||
if buffer.def.default ~= nil then
|
||||
message('WARNING: Hidden field can\'t have a default value. '..
|
||||
'Ignoring "default" attribute.')
|
||||
end
|
||||
-- TODO : Can't have bound variable neither
|
||||
end
|
||||
|
||||
-- Render
|
||||
if buffer.def.hidden == 'true' then
|
||||
return string.format("pwdfield[%s;%s;%s]", fspossize(buffer, offset),
|
||||
buffer.id, (buffer.def.label or ""))
|
||||
else
|
||||
return string.format("field[%s;%s;%s;%s]", fspossize(buffer, offset),
|
||||
buffer.id, (buffer.def.label or ""), (buffer.def.default or ""))
|
||||
end
|
||||
end
|
||||
},
|
||||
label = {
|
||||
offset = { x = 0, y = 0.2 },
|
||||
rendering = function(buffer, offset)
|
||||
if buffer.def.direction and buffer.def.direction == 'vertical' then
|
||||
return string.format("vertlabel[%s;%s]",
|
||||
fspos(buffer, offset), (buffer.def.label or ""))
|
||||
else
|
||||
return string.format("label[%s;%s]",
|
||||
fspos(buffer, offset), (buffer.def.label or ""))
|
||||
end
|
||||
end
|
||||
},
|
||||
}
|
||||
|
||||
-- Form rendering
|
||||
-- ==============
|
||||
-- Pass 1 : Basic controls, add a def member pointing to definition, collect ids
|
||||
-- Pass 2 : Add missing IDs
|
||||
-- Pass 3 : Compute parent size from children sizes (pre-render)
|
||||
-- Pass 4 : Place everything and render (render)
|
||||
|
||||
-- Pass 1 : Basic controls, add a def member pointing to definition, collect ids
|
||||
--
|
||||
local function set_defs_and_collect_ids(buffer, elements_by_id)
|
||||
local def = buffer.def
|
||||
assert(def.type, 'Element must have a type.')
|
||||
assert(widgets[def.type], 'Element type "'..def.type..'" unknown.')
|
||||
|
||||
if def.id then
|
||||
assert(elements_by_id[def.id] == nil,
|
||||
'Id "'..def.id..'" already used in the same form.')
|
||||
buffer.id = def.id
|
||||
elements_by_id[def.id] = buffer
|
||||
end
|
||||
|
||||
buffer.children = {}
|
||||
|
||||
for index, child in ipairs(def) do
|
||||
buffer.children[index] = { def = child }
|
||||
set_defs_and_collect_ids(buffer.children[index], elements_by_id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Pass 2 : Add missing IDs
|
||||
--
|
||||
local function add_missing_ids(buffer, elements_by_id)
|
||||
local def = buffer.def
|
||||
if not buffer.id and widgets[def.type].needs_id then
|
||||
local i = 1
|
||||
while elements_by_id[def.type..i] do
|
||||
i = i + 1
|
||||
end
|
||||
buffer.id = def.type..i
|
||||
elements_by_id[buffer.id] = buffer
|
||||
end
|
||||
|
||||
if buffer.children then
|
||||
for index, child in ipairs(buffer.children) do
|
||||
add_missing_ids(child, elements_by_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Pass 3 : Compute parent size from children sizes (pre-render)
|
||||
--
|
||||
size_element = function(buffer)
|
||||
local def = buffer.def
|
||||
buffer.pos = { x = 0, y = 0 }
|
||||
buffer.size = { x = 0, y = 0 }
|
||||
|
||||
local widget = widgets[def.type]
|
||||
if widget.sizing then
|
||||
widget.sizing(buffer)
|
||||
else -- Default case for simple elements
|
||||
buffer.size = { x=def.width, y=def.height }
|
||||
end
|
||||
end
|
||||
|
||||
-- Pass 4 : Place everything and render (render)
|
||||
--
|
||||
render_element = function(buffer, offset)
|
||||
local widget = widgets[buffer.def.type]
|
||||
if widget.rendering then
|
||||
return widget.rendering(buffer, offset)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
-- Entry point
|
||||
function nofs.render_form(form)
|
||||
local buffer = { def = form, elements_by_id = {} }
|
||||
|
||||
if not buffer.def.type then
|
||||
buffer.def.type = "vbox"
|
||||
end
|
||||
|
||||
set_defs_and_collect_ids(buffer, buffer.elements_by_id)
|
||||
add_missing_ids(buffer, buffer.elements_by_id)
|
||||
size_element(buffer)
|
||||
local render = render_element(buffer, {x = 0, y = 0})
|
||||
return string.format("size[%g,%g]%s", buffer.size.x, buffer.size.y, render)
|
||||
end
|
|
@ -0,0 +1,84 @@
|
|||
--[[
|
||||
nofs_lib for Minetest - NO FormSpec API
|
||||
(c) Pierre-Yves Rollo
|
||||
|
||||
This file is part of nofs_lib.
|
||||
|
||||
signs is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
signs is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with signs. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
|
||||
-- Form stack mechanism
|
||||
-- ====================
|
||||
|
||||
local stacks = {}
|
||||
|
||||
local function get_player_stack(player)
|
||||
if type(player) ~= "string" then
|
||||
player = player:get_player_name()
|
||||
end
|
||||
|
||||
if stacks[player] == nil then
|
||||
stacks[player] = {}
|
||||
end
|
||||
|
||||
return stacks[player]
|
||||
end
|
||||
|
||||
|
||||
-- Get top form
|
||||
function nofs.stack_get_top(player)
|
||||
local stack = get_player_stack(player)
|
||||
return stack[#stack]
|
||||
end
|
||||
|
||||
-- Find a form by its id
|
||||
function nofs.stack_get_by_id(player, form_id)
|
||||
for _, form in pairs(get_player_stack(player)) do
|
||||
if form.id == form_id then
|
||||
return form
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Remove top form from stack
|
||||
function nofs.stack_remove(player)
|
||||
local stack = get_player_stack(player)
|
||||
if #stack then
|
||||
table.remove(stack, #stack)
|
||||
end
|
||||
end
|
||||
|
||||
-- Add a form on top of the stack (and add some extra fields on form)
|
||||
function nofs.stack_add(player, form)
|
||||
if form and type(form)=="table" then
|
||||
|
||||
local stack = get_player_stack(player)
|
||||
|
||||
stack[#stack + 1] = form
|
||||
|
||||
-- Form complementation
|
||||
if not form.context then
|
||||
form.context = {}
|
||||
end
|
||||
|
||||
-- Name form if it is not named
|
||||
if not form.id then
|
||||
form.id = nofs.name..":form"..#stack
|
||||
end
|
||||
print (form.id)
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue