2022-01-17 15:25:42 +11:00
|
|
|
|
|
|
|
-- `minetest.formspec_escape` with option to not escape commas
|
|
|
|
local function fs_escape(text, is_list)
|
|
|
|
if text ~= nil then
|
|
|
|
text = string.gsub(text, "\\", "\\\\")
|
|
|
|
text = string.gsub(text, "%]", "\\]")
|
|
|
|
text = string.gsub(text, "%[", "\\[")
|
|
|
|
text = string.gsub(text, ";", "\\;")
|
|
|
|
if not is_list then
|
|
|
|
text = string.gsub(text, ",", "\\,")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return text
|
|
|
|
end
|
|
|
|
|
|
|
|
local function num(value, default_value)
|
|
|
|
if type(value) ~= "number" then
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
return string.format("%.4g", value)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function str(value, default_value)
|
|
|
|
if type(value) ~= "string" then
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
return fs_escape(value)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function bool(value, default_value)
|
|
|
|
if type(value) ~= "boolean" then
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
return value and "true" or "false"
|
|
|
|
end
|
|
|
|
|
|
|
|
local function list(value, default_value)
|
|
|
|
if type(value) == "string" then
|
|
|
|
return fs_escape(value, true)
|
|
|
|
end
|
|
|
|
if type(value) ~= "table" or #value < 1 then
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
local new_list = {}
|
|
|
|
for _,v in ipairs(value) do
|
|
|
|
if type(v) == "string" then
|
|
|
|
table.insert(new_list, fs_escape(v))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if #new_list < 1 then
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
return table.concat(new_list, ",")
|
|
|
|
end
|
|
|
|
|
|
|
|
local function middle(value, default_value) -- Only for `background9`
|
|
|
|
if type(value) == "number" then
|
|
|
|
return string.format("%i", value)
|
|
|
|
end
|
|
|
|
if type(value) ~= "string" then
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
if string.match(value, "^%-?%d+$") or string.match(value, "^%-?%d+,%-?%d+$")
|
|
|
|
or string.match(value, "^%-?%d+,%-?%d+,%-?%d+,%-?%d+$") then
|
|
|
|
return value
|
|
|
|
end
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
|
|
|
|
local function fullscreen(value, default_value) -- Only for `bgcolor`
|
|
|
|
if type(value) == "boolean" then
|
|
|
|
return value and "true" or "false"
|
|
|
|
end
|
|
|
|
if value == "true" or value == "false"
|
|
|
|
or value == "both" or value == "neither" then
|
|
|
|
return value
|
|
|
|
end
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
|
|
|
|
local function prop(value, default_value) -- Only for `stlye` and `style_type`
|
|
|
|
if type(value) ~= "table" or next(value) == nil then
|
|
|
|
return default_value
|
|
|
|
end
|
|
|
|
local new_prop = {}
|
|
|
|
for k,v in pairs(value) do
|
|
|
|
if type(k) == "string" then
|
|
|
|
k = fs_escape(k).."="
|
|
|
|
if type(v) == "string" then
|
|
|
|
table.insert(new_prop, k..fs_escape(v, true))
|
|
|
|
elseif type(v) == "number" then
|
|
|
|
table.insert(new_prop, k..string.format("%.4g", v))
|
|
|
|
elseif type(v) == "boolean" then
|
|
|
|
table.insert(new_prop, k..(v and "true" or "false"))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return table.concat(new_prop, ";")
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local formspec_elements = {
|
|
|
|
tooltip = {
|
|
|
|
"tooltip[%s;%s;%s;%s]",
|
|
|
|
{"element_name", "tooltip_text", "bgcolor", "fontcolor"},
|
|
|
|
{"button", "tooltip", "#303030", "#ffffff"},
|
|
|
|
{str, str, str, str},
|
|
|
|
},
|
|
|
|
tooltip_area = {
|
|
|
|
"tooltip[%s,%s;%s,%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "tooltip_text", "bgcolor", "fontcolor"},
|
|
|
|
{"0", "0", "100", "100", "tooltip area", "#303030", "#ffffff"},
|
|
|
|
{num, num, num, num, str, str, str},
|
|
|
|
},
|
|
|
|
image = {
|
|
|
|
"image[%s,%s;%s,%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "texture_name"},
|
|
|
|
{"0", "0", "1", "1", "default_dirt.png"},
|
|
|
|
{num, num, num, num, str}
|
|
|
|
},
|
|
|
|
animated_image = {
|
|
|
|
"animated_image[%s,%s;%s,%s;%s;%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "texture_name", "frame_count", "frame_duration", "frame_start"},
|
|
|
|
{"0", "0", "1", "1", "animated_image", "default_lava_flowing_animated.png", "16", "200", "1"},
|
|
|
|
{num, num, num, num, str, str, num, num, num}
|
|
|
|
},
|
|
|
|
model = {
|
|
|
|
"model[%s,%s;%s,%s;%s;%s;%s;%s,%s;%s;%s;0,0]",
|
|
|
|
{"X", "Y", "W", "H", "name", "mesh", "textures", "rotation_x", "rotation_y", "continuous", "mouse_control"},
|
|
|
|
{"0", "0", "2", "2", "model", "character.b3d", "character.png", "0", "0", "", ""},
|
|
|
|
{num, num, num, num, str, str, list, num, num, bool, bool}
|
|
|
|
},
|
|
|
|
item_image = {
|
|
|
|
"item_image[%s,%s;%s,%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "item_name"},
|
|
|
|
{"0", "0", "1", "1", "default:dirt_with_grass"},
|
|
|
|
{num, num, num, num, str}
|
|
|
|
},
|
|
|
|
bgcolor = {
|
|
|
|
"bgcolor[%s;%s;%s]",
|
|
|
|
{"bgcolor", "fullscreen", "fbgcolor"},
|
|
|
|
{"#ffffff", "", ""},
|
|
|
|
{str, fullscreen, str}
|
|
|
|
},
|
|
|
|
background = {
|
|
|
|
"background[%s,%s;%s,%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "texture_name", "auto_clip"},
|
|
|
|
{"0", "0", "0", "0", "digistuff_ts_bg.png", "true"},
|
|
|
|
{num, num, num, num, str, bool}
|
|
|
|
},
|
|
|
|
background9 = {
|
|
|
|
"background9[%s,%s;%s,%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "texture_name", "auto_clip", "middle"},
|
|
|
|
{"0", "0", "0", "0", "digistuff_ts_bg.png", "true", "3"},
|
|
|
|
{num, num, num, num, str, bool, middle}
|
|
|
|
},
|
|
|
|
pwdfield = {
|
|
|
|
"pwdfield[%s,%s;%s,%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "label"},
|
|
|
|
{"0", "0", "3", "0.8", "pwdfield", ""},
|
|
|
|
{num, num, num, num, str, str}
|
|
|
|
},
|
|
|
|
field = {
|
|
|
|
"field[%s,%s;%s,%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "label", "default"},
|
|
|
|
{"0", "0", "3", "0.8", "field", "field", ""},
|
|
|
|
{num, num, num, num, str, str, str}
|
|
|
|
},
|
|
|
|
field_close_on_enter = {
|
|
|
|
"field_close_on_enter[%s;%s]",
|
|
|
|
{"name", "close_on_enter"},
|
|
|
|
{"field", "true"},
|
|
|
|
{str, bool}
|
|
|
|
},
|
|
|
|
textarea = {
|
|
|
|
"textarea[%s,%s;%s,%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "label", "default"},
|
|
|
|
{"0", "0", "4", "3", "textarea", "", ""},
|
|
|
|
{num, num, num, num, str, str, str}
|
|
|
|
},
|
|
|
|
label = {
|
|
|
|
"label[%s,%s;%s]",
|
|
|
|
{"X", "Y", "label"},
|
|
|
|
{"0", "0", "label"},
|
|
|
|
{num, num, str}
|
|
|
|
},
|
|
|
|
hypertext = {
|
|
|
|
"hypertext[%s,%s;%s,%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "text"},
|
|
|
|
{"0", "0", "4", "3", "hypertext", "<i>hypertext</i>"},
|
|
|
|
{num, num, num, num, str, str}
|
|
|
|
},
|
|
|
|
vertlabel = {
|
|
|
|
"vertlabel[%s,%s;%s]",
|
|
|
|
{"X", "Y", "label"},
|
|
|
|
{"0", "0", "vertlabel"},
|
|
|
|
{num, num, str}
|
|
|
|
},
|
|
|
|
button = {
|
|
|
|
"button[%s,%s;%s,%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "label"},
|
|
|
|
{"0", "0", "3", "0.8", "button", "button"},
|
|
|
|
{num, num, num, num, str, str}
|
|
|
|
},
|
|
|
|
image_button = {
|
|
|
|
"image_button[%s,%s;%s,%s;%s;%s;%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "texture_name", "name", "label", "noclip", "drawborder", "pressed_texture_name"},
|
|
|
|
{"0", "0", "1", "1", "default_stone_block.png", "image_button", "button", "", "", ""},
|
|
|
|
{num, num, num, num, str, str, str, bool, bool, str}
|
|
|
|
},
|
|
|
|
item_image_button = {
|
|
|
|
"item_image_button[%s,%s;%s,%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "item_name", "name", "label"},
|
|
|
|
{"0", "0", "1", "1", "default:stone_block", "item_image_button", ""},
|
|
|
|
{num, num, num, num, str, str, str}
|
|
|
|
},
|
|
|
|
button_exit = {
|
|
|
|
"button_exit[%s,%s;%s,%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "label"},
|
|
|
|
{"0", "0", "3", "0.8", "button_exit", "button"},
|
|
|
|
{num, num, num, num, str, str}
|
|
|
|
},
|
|
|
|
image_button_exit = {
|
|
|
|
"image_button_exit[%s,%s;%s,%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "texture_name", "name", "label"},
|
|
|
|
{"0", "0", "1", "1", "default_mese_block.png", "image_button_exit", "button"},
|
|
|
|
{num, num, num, num, str, str, str}
|
|
|
|
},
|
|
|
|
textlist = {
|
|
|
|
"textlist[%s,%s;%s,%s;%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "name", "listelements", "selected_id", "transparent"},
|
|
|
|
{"0", "0", "4", "3", "textlist", "a,b,c", "0", "false"},
|
|
|
|
{num, num, num, num, str, list, num, bool}
|
|
|
|
},
|
|
|
|
tabheader = {
|
|
|
|
"tabheader[%s,%s;%s;%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "name", "captions", "current_tab", "transparent", "draw_border"},
|
|
|
|
{"0", "0", "tabheader", "a,b,c", "0", "false", "false"},
|
|
|
|
{num, num, str, list, num, bool, bool}
|
|
|
|
},
|
|
|
|
box = {
|
|
|
|
"box[%s,%s;%s,%s;%s]",
|
|
|
|
{"X", "Y", "W", "H", "color"},
|
|
|
|
{"0", "0", "1", "1", "#ffffff"},
|
|
|
|
{num, num, num, num, str}
|
|
|
|
},
|
|
|
|
dropdown = {
|
|
|
|
"dropdown[%s,%s;%s,%s;%s;%s;%s;%s]",
|
2022-01-22 13:55:04 +11:00
|
|
|
{"X", "Y", "W", "H", "name", "choices", "selected_id", "index_event"},
|
2022-01-17 15:25:42 +11:00
|
|
|
{"0", "0", "3", "0.8", "dropdown", "a,b,c", "0", "false"},
|
|
|
|
{num, num, num, num, str, list, num, bool}
|
|
|
|
},
|
|
|
|
checkbox = {
|
|
|
|
"checkbox[%s,%s;%s;%s;%s]",
|
|
|
|
{"X", "Y", "name", "label", "selected"},
|
|
|
|
{"0", "0", "checkbox", "checkbox", "false"},
|
|
|
|
{num, num, str, str, bool}
|
|
|
|
},
|
|
|
|
style = {
|
|
|
|
"style[%s;%s]",
|
|
|
|
{"selectors", "properties"},
|
|
|
|
{"", ""},
|
|
|
|
{list, prop}
|
|
|
|
},
|
|
|
|
style_type = {
|
|
|
|
"style_type[%s;%s]",
|
|
|
|
{"selectors", "properties"},
|
|
|
|
{"", ""},
|
|
|
|
{list, prop}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
local table_options = {
|
|
|
|
color = str,
|
|
|
|
background = str,
|
|
|
|
border = bool,
|
|
|
|
highlight = str,
|
|
|
|
highlight_text = str,
|
|
|
|
opendepth = num
|
|
|
|
}
|
|
|
|
|
|
|
|
local function column(value)
|
|
|
|
if type(value) == "string" then
|
|
|
|
return fs_escape(value, true)
|
|
|
|
end
|
|
|
|
if type(value) ~= "table" or type(value.type) ~= "string" then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
local new_column = {str(value.type)}
|
|
|
|
for k,v in pairs(value) do
|
|
|
|
if type(k) == "string" and k ~= "type" then
|
|
|
|
k = fs_escape(k).."="
|
|
|
|
if type(v) == "string" then
|
|
|
|
table.insert(new_column, k..fs_escape(v, true))
|
|
|
|
elseif type(v) == "number" then
|
|
|
|
table.insert(new_column, k..string.format("%.4g", v))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return table.concat(new_column, ",")
|
|
|
|
end
|
|
|
|
|
|
|
|
formspec_elements.table = function(values)
|
|
|
|
local tbl = ""
|
|
|
|
local name = str(values.name, "table")
|
|
|
|
local cells = list(values.cells, "a,b,c")
|
|
|
|
for v,d in pairs({X = 0, Y = 0, W = 4, H = 3, selected_id = 0}) do
|
|
|
|
if type(values[v]) ~= "number" then
|
|
|
|
values[v] = d
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local options = {}
|
|
|
|
for v,f in pairs(table_options) do
|
|
|
|
local value = f(values[v])
|
|
|
|
if value ~= nil then
|
|
|
|
table.insert(options, v.."="..value)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if #options > 0 then
|
|
|
|
tbl = tbl.."tableoptions["..table.concat(options, ";").."]"
|
|
|
|
end
|
|
|
|
local columns = {}
|
|
|
|
if type(values.columns) == "table" then
|
|
|
|
for _,v in ipairs(values.columns) do
|
|
|
|
table.insert(columns, column(v, true))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if #columns > 0 then
|
|
|
|
tbl = tbl.."tablecolumns["..table.concat(columns, ";").."]"
|
|
|
|
end
|
|
|
|
return tbl..string.format("table[%s,%s;%s,%s;%s;%s;%s]",
|
|
|
|
values.X, values.Y, values.W, values.H, name, cells, values.selected_id)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
formspec_elements.item_grid = function(values)
|
|
|
|
if values.interactable ~= false then
|
|
|
|
values.name = str(values.name, "grid").."_"
|
|
|
|
end
|
|
|
|
for v,d in pairs({X = 0, Y = 0, W = 1, H = 1, spacing = 0, size = 1, offset = 1}) do
|
|
|
|
if type(values[v]) ~= "number" then
|
|
|
|
values[v] = d
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local items = type(values.items) == "table" and values.items or {}
|
|
|
|
local offset = math.max(1, math.floor(values.offset)) - 1
|
|
|
|
local x, y, n, item = values.X, values.Y, 1
|
|
|
|
local grid = {}
|
|
|
|
for _=1, values.H do
|
|
|
|
for _=1, values.W do
|
|
|
|
item = items[n + offset]
|
|
|
|
if type(item) ~= "string" then
|
|
|
|
return table.concat(grid)
|
|
|
|
end
|
|
|
|
item = string.match(item, "^[^ %[%]\\,;]* ?%d* ?%d*")
|
|
|
|
if values.interactable ~= false then
|
|
|
|
grid[n] = string.format("item_image_button[%s,%s;%s,%s;%s;%s;]",
|
|
|
|
x, y, values.size, values.size, item, values.name..n)
|
|
|
|
else
|
|
|
|
grid[n] = string.format("item_image[%s,%s;%s,%s;%s]",
|
|
|
|
x, y, values.size, values.size, item)
|
|
|
|
end
|
|
|
|
n = n + 1
|
|
|
|
x = x + values.size + values.spacing
|
|
|
|
end
|
|
|
|
x = values.X
|
|
|
|
y = y + values.size + values.spacing
|
|
|
|
end
|
|
|
|
return table.concat(grid)
|
|
|
|
end
|
|
|
|
|
|
|
|
return formspec_elements
|