Add scroll_container[] (minor compatibility break)
This commit is contained in:
parent
ea5ed53b5a
commit
9a958355ca
13
README.md
13
README.md
|
@ -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`.
|
||||
Similar to `container`.
|
||||
- `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
|
||||
to `minetest.show_formspec`, however also accepts player objects and will
|
||||
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
|
||||
|
||||
### 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
|
||||
|
||||
- `formspec_version` (provided it is the first element) is moved to
|
||||
|
@ -73,7 +80,7 @@ readability.*
|
|||
},
|
||||
{
|
||||
type = "style",
|
||||
name = "name",
|
||||
selectors = {"name"},
|
||||
props = {
|
||||
bgcolor = "blue",
|
||||
textcolor = "yellow",
|
||||
|
@ -135,7 +142,7 @@ readability.*
|
|||
},
|
||||
{
|
||||
type = "style",
|
||||
name = "name",
|
||||
selectors = {"name"},
|
||||
props = {
|
||||
bgcolor = "blue",
|
||||
textcolor = "yellow",
|
||||
|
|
14
core.lua
14
core.lua
|
@ -156,7 +156,7 @@ end
|
|||
local function parse_value(elems, template)
|
||||
local elems_l, template_l = #elems, #template
|
||||
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
|
||||
elems[#elems] = nil
|
||||
end
|
||||
|
@ -320,10 +320,12 @@ function formspec_ast.parse(spec, custom_handlers)
|
|||
return nil, err
|
||||
end
|
||||
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)
|
||||
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 = table.remove(containers)
|
||||
if not container then
|
||||
|
@ -348,6 +350,7 @@ local function unparse_ellipsis(elem, obj1, res, inner)
|
|||
end
|
||||
elseif type(obj1[2]) == 'string' then
|
||||
local value = elem[obj1[1]]
|
||||
if value == nil then return end
|
||||
for k, v in ipairs(value) do
|
||||
table.insert(res, tostring(v))
|
||||
end
|
||||
|
@ -413,14 +416,15 @@ do
|
|||
end
|
||||
|
||||
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)
|
||||
if err then return err end
|
||||
for _, e in ipairs(elem) do
|
||||
local err = unparse_elem(e, res)
|
||||
if err then return err end
|
||||
end
|
||||
return unparse_elem({type='container_end'}, res, true)
|
||||
return unparse_elem({type=elem.type .. '_end'}, res, true)
|
||||
end
|
||||
|
||||
local data = elements[elem.type]
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -59,7 +59,7 @@ function formspec_ast.walk(tree)
|
|||
end
|
||||
i = i + 1
|
||||
|
||||
if res.type == 'container' then
|
||||
if res.type == 'container' or res.type == 'scroll_container' then
|
||||
table.insert(parents, {tree, i})
|
||||
tree = res
|
||||
i = 1
|
||||
|
|
1
init.lua
1
init.lua
|
@ -54,6 +54,7 @@ else
|
|||
end
|
||||
return text
|
||||
end
|
||||
minetest.log = print
|
||||
function string.trim(str)
|
||||
return str:gsub("^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
|
|
@ -14,9 +14,9 @@ def _make_known(**kwargs):
|
|||
|
||||
_known = _make_known(
|
||||
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',
|
||||
'fullscreen', 'noclip', 'drawborder', 'selected'),
|
||||
'fullscreen', 'noclip', 'drawborder', 'selected', 'force'),
|
||||
table=('param', 'opt', 'prop'),
|
||||
null=('',),
|
||||
)
|
||||
|
@ -99,6 +99,24 @@ def _size_hook(params):
|
|||
yield params
|
||||
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):
|
||||
data = data.split('\nElements\n--------\n', 1)[-1].split('\n----', 1)[0]
|
||||
for line in data.split('\n'):
|
||||
|
|
|
@ -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')
|
Loading…
Reference in New Issue