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`.
|
- `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",
|
||||||
|
|
14
core.lua
14
core.lua
|
@ -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
|
@ -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
|
||||||
|
|
1
init.lua
1
init.lua
|
@ -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
|
||||||
|
|
|
@ -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'):
|
||||||
|
|
|
@ -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