Add scroll_container[] (minor compatibility break)

This commit is contained in:
luk3yx 2020-09-07 17:48:51 +12:00
parent ea5ed53b5a
commit 9a958355ca
7 changed files with 223 additions and 12 deletions

View File

@ -24,7 +24,8 @@ A Minetest mod library to make modifying formspecs easier.
- `formspec_ast.apply_offset(tree, x, y)`: Shifts all elements in `tree`. - `formspec_ast.apply_offset(tree, x, y)`: Shifts all elements in `tree`.
Similar to `container`. Similar to `container`.
- `formspec_ast.flatten(tree)`: Removes all containers and offsets elements - `formspec_ast.flatten(tree)`: Removes all containers and offsets elements
that were in containers accordingly. that were in containers accordingly. **The use of this function is
discouraged as `scroll_container[]` elements are not flattened.**
- `formspec_ast.show_formspec(player_or_name, formname, formspec)`: Similar - `formspec_ast.show_formspec(player_or_name, formname, formspec)`: Similar
to `minetest.show_formspec`, however also accepts player objects and will to `minetest.show_formspec`, however also accepts player objects and will
pass `formspec` through `formspec_ast.interpret` first. pass `formspec` through `formspec_ast.interpret` first.
@ -41,6 +42,12 @@ all attributes are lowercase.
[the formspec element list]: https://github.com/minetest/minetest/blob/dee2210/doc/lua_api.txt#L1959 [the formspec element list]: https://github.com/minetest/minetest/blob/dee2210/doc/lua_api.txt#L1959
### Recent backwards incompatibilities
- The `style[]` element has a `selectors` field instead of `name`. Using
`name` when unparsing formspecs still works, however parsed formspecs
always use `selectors`.
### Special cases ### Special cases
- `formspec_version` (provided it is the first element) is moved to - `formspec_version` (provided it is the first element) is moved to
@ -73,7 +80,7 @@ readability.*
}, },
{ {
type = "style", type = "style",
name = "name", selectors = {"name"},
props = { props = {
bgcolor = "blue", bgcolor = "blue",
textcolor = "yellow", textcolor = "yellow",
@ -135,7 +142,7 @@ readability.*
}, },
{ {
type = "style", type = "style",
name = "name", selectors = {"name"},
props = { props = {
bgcolor = "blue", bgcolor = "blue",
textcolor = "yellow", textcolor = "yellow",

View File

@ -156,7 +156,7 @@ end
local function parse_value(elems, template) local function parse_value(elems, template)
local elems_l, template_l = #elems, #template local elems_l, template_l = #elems, #template
if elems_l < template_l or (elems_l > template_l and if elems_l < template_l or (elems_l > template_l and
template[template_l][2] ~= '...') then template_l > 0 and template[template_l][2] ~= '...') then
while #elems > #template and elems[#elems]:trim() == '' do while #elems > #template and elems[#elems]:trim() == '' do
elems[#elems] = nil elems[#elems] = nil
end end
@ -320,10 +320,12 @@ function formspec_ast.parse(spec, custom_handlers)
return nil, err return nil, err
end end
table.insert(container, ast_elem) table.insert(container, ast_elem)
if ast_elem.type == 'container' then if ast_elem.type == 'container' or
ast_elem.type == 'scroll_container' then
table.insert(containers, container) table.insert(containers, container)
container = ast_elem container = ast_elem
elseif ast_elem.type == 'container_end' then elseif ast_elem.type == 'end' or ast_elem.type == 'container_end' or
ast_elem.type == 'scroll_container_end' then
container[#container] = nil container[#container] = nil
container = table.remove(containers) container = table.remove(containers)
if not container then if not container then
@ -348,6 +350,7 @@ local function unparse_ellipsis(elem, obj1, res, inner)
end end
elseif type(obj1[2]) == 'string' then elseif type(obj1[2]) == 'string' then
local value = elem[obj1[1]] local value = elem[obj1[1]]
if value == nil then return end
for k, v in ipairs(value) do for k, v in ipairs(value) do
table.insert(res, tostring(v)) table.insert(res, tostring(v))
end end
@ -413,14 +416,15 @@ do
end end
local function unparse_elem(elem, res, force) local function unparse_elem(elem, res, force)
if elem.type == 'container' and not force then if (elem.type == 'container' or
elem.type == 'scroll_container') and not force then
local err = unparse_elem(elem, res, true) local err = unparse_elem(elem, res, true)
if err then return err end if err then return err end
for _, e in ipairs(elem) do for _, e in ipairs(elem) do
local err = unparse_elem(e, res) local err = unparse_elem(e, res)
if err then return err end if err then return err end
end end
return unparse_elem({type='container_end'}, res, true) return unparse_elem({type=elem.type .. '_end'}, res, true)
end end
local data = elements[elem.type] local data = elements[elem.type]

File diff suppressed because one or more lines are too long

View File

@ -59,7 +59,7 @@ function formspec_ast.walk(tree)
end end
i = i + 1 i = i + 1
if res.type == 'container' then if res.type == 'container' or res.type == 'scroll_container' then
table.insert(parents, {tree, i}) table.insert(parents, {tree, i})
tree = res tree = res
i = 1 i = 1

View File

@ -54,6 +54,7 @@ else
end end
return text return text
end end
minetest.log = print
function string.trim(str) function string.trim(str)
return str:gsub("^%s*(.-)%s*$", "%1") return str:gsub("^%s*(.-)%s*$", "%1")
end end

View File

@ -14,9 +14,9 @@ def _make_known(**kwargs):
_known = _make_known( _known = _make_known(
number=('x', 'y', 'w', 'h', 'selected_idx', 'version', number=('x', 'y', 'w', 'h', 'selected_idx', 'version',
'starting_item_index'), 'starting_item_index', 'scroll_factor'),
boolean=('auto_clip', 'fixed_size', 'transparent', 'draw_border', 'bool', boolean=('auto_clip', 'fixed_size', 'transparent', 'draw_border', 'bool',
'fullscreen', 'noclip', 'drawborder', 'selected'), 'fullscreen', 'noclip', 'drawborder', 'selected', 'force'),
table=('param', 'opt', 'prop'), table=('param', 'opt', 'prop'),
null=('',), null=('',),
) )
@ -99,6 +99,24 @@ def _size_hook(params):
yield params yield params
yield [[('w', 'number'), ('h', 'number')]] yield [[('w', 'number'), ('h', 'number')]]
# Fix style and style_type
@hook('style')
@hook('style_type')
def _style_hook(params):
# This is not used when parsing but keeps backwards compatibility when
# unparsing.
params[0] = [('name', 'string')]
yield params
params[0] = [(('selectors', 'string'), '...')]
yield params
# Fix scroll_container
@hook('scroll_container')
def _scroll_container_hook(params):
yield params
yield params[:4]
def _raw_parse(data): def _raw_parse(data):
data = data.split('\nElements\n--------\n', 1)[-1].split('\n----', 1)[0] data = data.split('\nElements\n--------\n', 1)[-1].split('\n----', 1)[0]
for line in data.split('\n'): for line in data.split('\n'):

181
tests.lua Normal file
View File

@ -0,0 +1,181 @@
-- dofile('init.lua')
dofile('test.lua')
local function equal(t1, t2)
if type(t1) ~= 'table' or type(t2) ~= 'table' then
return t1 == t2
end
for k, v in pairs(t1) do
if not equal(v, t2[k]) then
print(k, v, dump(t1), dump(t2))
return false
end
end
for k, v in pairs(t2) do
if t1[k] == nil then
return false
end
end
return true
end
local function assert_equal(obj1, ...)
for i = 1, select('#', ...) do
objn = select(i, ...)
if not equal(obj1, objn) then
error(('%s ~= %s'):format(obj1, objn))
end
end
end
local function test_parse(fs, expected_tree)
-- Make single elements lists and add formspec_version
if expected_tree.type then
expected_tree = {expected_tree}
end
if not expected_tree.formspec_version then
expected_tree.formspec_version = 1
end
local tree = assert(formspec_ast.parse(fs))
assert_equal(tree, expected_tree)
end
local function test_parse_unparse(fs, expected_tree)
test_parse(fs, expected_tree)
local unparsed_fs = assert(formspec_ast.unparse(expected_tree))
assert_equal(fs, unparsed_fs)
end
local fs = [[
formspec_version[2]
size[5,2]
container[1,1]
label[0,0;Containers are fun]
container[-1,-1]
button[0.5,0;4,1;name;Label]
container_end[]
label[0,1;Nested containers work too.]
scroll_container[0,2;1,1;scrollbar;vertical]
button[0.5,0;4,1;name;Label]
scroll_container_end[]
container_end[]
image[0,1;1,1;air.png]
set_focus[name;true]
]]
fs = ('\n' .. fs):gsub('\n[ \n]*', '')
test_parse_unparse(fs, {
formspec_version = 2,
{
type = "size",
w = 5,
h = 2,
},
{
type = "container",
x = 1,
y = 1,
{
type = "label",
x = 0,
y = 0,
label = "Containers are fun",
},
{
type = "container",
x = -1,
y = -1,
{
type = "button",
x = 0.5,
y = 0,
w = 4,
h = 1,
name = "name",
label = "Label",
},
},
{
type = "label",
x = 0,
y = 1,
label = "Nested containers work too.",
},
{
type = "scroll_container",
x = 0,
y = 2,
w = 1,
h = 1,
scrollbar_name = "scrollbar",
orientation = "vertical",
-- scroll_factor = nil,
{
h = 1,
y = 0,
label = "Label",
w = 4,
name = "name",
x = 0.5,
type = "button"
},
},
},
{
type = "image",
x = 0,
y = 1,
w = 1,
h = 1,
texture_name = "air.png",
},
{
type = "set_focus",
name = "name",
force = true,
}
})
-- Make sure style[] (un)parses correctly
local s = 'style[test1,test2;def=ghi]style_type[test;abc=def]'
assert_equal(s, assert(formspec_ast.interpret(s)))
test_parse('style[name,name2;bgcolor=blue;textcolor=yellow]', {
type = "style",
selectors = {
"name",
"name2",
},
props = {
bgcolor = "blue",
textcolor = "yellow",
},
})
-- Ensure the style[] unparse compatibility works correctly
assert_equal(
'style_type[test;abc=def]',
assert(formspec_ast.unparse({
{
type = 'style_type',
name = 'test',
props = {
abc = 'def',
},
}
})),
assert(formspec_ast.unparse({
{
type = 'style_type',
selectors = {
'test',
},
props = {
abc = 'def',
},
}
}))
)
print('Tests pass')