Fix a few bugs and add automatic sizing for layout containers.
This commit is contained in:
parent
be73993547
commit
969030a026
2 changed files with 153 additions and 48 deletions
197
init.lua
197
init.lua
|
|
@ -62,7 +62,26 @@ local function add_elem_style(e, state, props)
|
|||
props = state
|
||||
state = "default"
|
||||
end
|
||||
e._styles[state] = imfs.style(e.__id..":"..state, props, true)
|
||||
|
||||
-- Permit targeting pseudo-elements as well.
|
||||
if state:sub(1, 1) == "." then
|
||||
e._styles[state] = imfs.style(e.__id..state, props, true)
|
||||
else
|
||||
e._styles[state] = imfs.style(e.__id..":"..state, props, true)
|
||||
end
|
||||
|
||||
return e
|
||||
end
|
||||
|
||||
-- Bulk version of `add_elem_style`.
|
||||
local function add_elem_styles(e, states)
|
||||
for state, props in pairs(states) do
|
||||
if state:sub(1, 1) == "." then
|
||||
e._styles[state] = imfs.style(e.__id..state, props, true)
|
||||
else
|
||||
e._styles[state] = imfs.style(e.__id..":"..state, props, true)
|
||||
end
|
||||
end
|
||||
return e
|
||||
end
|
||||
|
||||
|
|
@ -75,16 +94,22 @@ local function add_elem_tooltip(e, text, enabled, bgcolor, txtcolor)
|
|||
txtcolor = bgcolor
|
||||
bgcolor = enabled
|
||||
end
|
||||
-- We must overload `render()` both to ensure that tooltips don't clutter the element tree and mess with layout containers
|
||||
-- and (for area tooltips) in order to ensure the tooltip's position and size match the parent's exactly in all cases.
|
||||
|
||||
-- For named elements, prefer name-associated tooltips.
|
||||
if e.__id then
|
||||
imfs._named_tooltip(e.__id, text, bgcolor, txtcolor)
|
||||
-- For other elements, if they have a size, use an area tooltip instead.
|
||||
elseif e.w then
|
||||
-- We must overload `render()` in order to ensure the tooltip's position matches the parent's exactly in all cases.
|
||||
local render = e.render
|
||||
e.render = function(e, x, y, w, h)
|
||||
local out = render(e, x, y, w, h)
|
||||
return table.concat{out, imfs.tooltip.init(x or e.x, y or e.y, w or e.w, h or e.h, text, bgcolor, txtcolor, true):render()}
|
||||
return out..imfs.tooltip.named(e.__id, text, bgcolor, txtcolor):render()
|
||||
end
|
||||
-- For other elements, if they have a size, use an area tooltip instead.
|
||||
elseif e.w then
|
||||
local render = e.render
|
||||
e.render = function(e, x, y, w, h)
|
||||
local out = render(e, x, y, w, h)
|
||||
return out..imfs.tooltip.init(x or e.x, y or e.y, w or e.w, h or e.h, text, bgcolor, txtcolor):render()
|
||||
end
|
||||
end
|
||||
return e
|
||||
|
|
@ -248,6 +273,11 @@ local fs_tooltip = {
|
|||
setmetatable(e, imfs.tooltip)
|
||||
return e
|
||||
end,
|
||||
named = function(name, text, bgcolor, txtcolor)
|
||||
local e = {_name = name, text = text, bgcolor = bgcolor or imfs.theme.tooltip_bg or "#444", txtcolor = txtcolor or imfs.theme.tooltip_text or "#aaa"}
|
||||
setmetatable(e, imfs.tooltip)
|
||||
return e
|
||||
end,
|
||||
render = function(e, x, y, w, h)
|
||||
if e._name then
|
||||
return string.format("tooltip[%s;%s;%s;%s]", e._name, get(e, "text"), get(e, "bgcolor"), get(e, "txtcolor"))
|
||||
|
|
@ -273,13 +303,6 @@ setmetatable(fs_tooltip, {
|
|||
end
|
||||
})
|
||||
|
||||
local function fs_named_tooltip(name, text, bgcolor, txtcolor)
|
||||
local e = {_name = name, text = text, bgcolor = bgcolor or imfs.theme.tooltip_bg or "#444", txtcolor = txtcolor or imfs.theme.tooltip_text or "#aaa"}
|
||||
setmetatable(e, fs_tooltip)
|
||||
table.insert(ctx, e)
|
||||
return e
|
||||
end
|
||||
|
||||
local fs_label = {
|
||||
render = function(e, x, y)
|
||||
return string.format("label[%f,%f;%s]", x or get(e, "x"), y or get(e, "y"), get(e, "txt"))
|
||||
|
|
@ -326,19 +349,31 @@ setmetatable(fs_arealabel, {
|
|||
|
||||
local fs_hypertext = {
|
||||
render = function(e, x, y, w, h)
|
||||
return string.format("hypertext[%f,%f;%f,%f;%s;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), e.__id, get(e, "txt"))
|
||||
local out = {}
|
||||
for _, x in pairs(e._styles) do
|
||||
out[#out +1] = x:render()
|
||||
end
|
||||
|
||||
out[#out +1] = string.format("hypertext[%f,%f;%f,%f;%s;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), e.__id, get(e, "txt"))
|
||||
return table.concat(out)
|
||||
end,
|
||||
onaction = function(e, fn)
|
||||
ctx._events.on_click[e.__id] = fn
|
||||
return e
|
||||
end,
|
||||
named = function(e, name)
|
||||
e.__id = name
|
||||
return e
|
||||
end,
|
||||
styles = add_elem_styles,
|
||||
style = add_elem_style,
|
||||
tooltip = add_elem_tooltip,
|
||||
}
|
||||
fs_hypertext.__index = fs_hypertext
|
||||
setmetatable(fs_hypertext, {
|
||||
__call = function(_, x, y, w, h, txt)
|
||||
local e = {x = x, y = y, w = w, h = h, txt = txt}
|
||||
e.__id = "_"..minetest.get_us_time().."_"..math.random(1, 100000)
|
||||
local e = {x = x, y = y, w = w, h = h, txt = txt, _styles = {}}
|
||||
e.__id = new_id()
|
||||
setmetatable(e, fs_hypertext)
|
||||
table.insert(ctx, e)
|
||||
return e
|
||||
|
|
@ -470,6 +505,7 @@ local fs_model = {
|
|||
return e
|
||||
end,
|
||||
style = add_elem_style,
|
||||
styles = add_elem_styles,
|
||||
tooltip = add_elem_tooltip,
|
||||
}
|
||||
fs_model.__index = fs_model
|
||||
|
|
@ -521,6 +557,7 @@ local fs_button = {
|
|||
return e
|
||||
end,
|
||||
style = add_elem_style,
|
||||
styles = add_elem_styles,
|
||||
tooltip = add_elem_tooltip,
|
||||
}
|
||||
fs_button.__index = fs_button
|
||||
|
|
@ -557,16 +594,32 @@ setmetatable(fs_checkbox, {
|
|||
|
||||
local fs_list = {
|
||||
render = function(e, x, y, w, h)
|
||||
x = x or get(e, "x")
|
||||
y = y or get(e, "y")
|
||||
w = w or get(e, "w")
|
||||
h = h or get(e, "h")
|
||||
|
||||
return string.format("list[%s;%s;%f,%f;%d,%d;%s]", get(e, "location"), get(e, "list"), x or get(e, "x"), y or get(e, "y"), w, h, get(e, "start"))
|
||||
local width = (4 *w +1) /5
|
||||
local height = (4 *h +1) /5
|
||||
|
||||
local padding_x = (w -(width *1.25) +0.25) /2
|
||||
local padding_y = (h -(height *1.25) +0.25) /2
|
||||
|
||||
return string.format("list[%s;%s;%f,%f;%d,%d;%s]", get(e, "location"), get(e, "list"), x +padding_x, y +padding_y, width, height, get(e, "start"))
|
||||
end
|
||||
}
|
||||
fs_list.__index = fs_list
|
||||
setmetatable(fs_list, {
|
||||
__call = function(_, x, y, w, h, location, list, start)
|
||||
local e = {x = x, y = y, w = type(w == "string") and w or w +((w -1) /4), h = type(h) == "string" and h or h +((h -1) /4), location = location or "current_player", list = list or "main", start = start or ""}
|
||||
local e = {
|
||||
x = x,
|
||||
y = y,
|
||||
w = type(w) == "string" and w or w +((w -1) /4),
|
||||
h = type(h) == "string" and h or h +((h -1) /4),
|
||||
location = location or "current_player",
|
||||
list = list or "main",
|
||||
start = start or ""
|
||||
}
|
||||
e.__id = new_id()
|
||||
setmetatable(e, fs_list)
|
||||
table.insert(ctx, e)
|
||||
|
|
@ -644,6 +697,7 @@ local fs_field = {
|
|||
return e
|
||||
end,
|
||||
style = add_elem_style,
|
||||
styles = add_elem_styles,
|
||||
tooltip = add_elem_tooltip,
|
||||
}
|
||||
fs_field.__index = fs_field
|
||||
|
|
@ -665,6 +719,10 @@ end
|
|||
local fs_scrollbar = {
|
||||
render = function(e, x, y, w, h)
|
||||
local out = {}
|
||||
for _, x in pairs(e._styles) do
|
||||
out[#out +1] = x:render()
|
||||
end
|
||||
|
||||
if e._options then
|
||||
out[#out +1] = "scrollbaroptions["
|
||||
local first = true
|
||||
|
|
@ -682,6 +740,11 @@ local fs_scrollbar = {
|
|||
|
||||
out[#out +1] = string.format("scrollbar[%f,%f;%f,%f;%s;%s;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "orientation"), e.__id, get(e, "value"))
|
||||
|
||||
-- If we were given custom options, reset them all so they don't interfere with future scrollbars (particularly those automatically configured by scroll containers).
|
||||
if e._options then
|
||||
out[#out +1] = "scrollbaroptions[min=-1;max=-1;smallstep=-1;largestep=-1;thumbsize=-1;arrows=default]"
|
||||
end
|
||||
|
||||
return table.concat(out)
|
||||
end,
|
||||
options = function(e, props)
|
||||
|
|
@ -693,13 +756,14 @@ local fs_scrollbar = {
|
|||
return e
|
||||
end,
|
||||
style = add_elem_style,
|
||||
styles = add_elem_styles,
|
||||
tooltip = add_elem_tooltip,
|
||||
}
|
||||
fs_scrollbar.__index = fs_scrollbar
|
||||
setmetatable(fs_scrollbar, {
|
||||
__call = function(_, x, y, w, h, orientation, value)
|
||||
local e = {x = x, y = y, w = w, h = h, orientation = orientation or "vertical", value = value or "", _styles = {}}
|
||||
e.__id = "_"..minetest.get_us_time().."_"..math.random(1, 100000)
|
||||
e.__id = new_id()
|
||||
setmetatable(e, fs_scrollbar)
|
||||
ctx[#ctx +1] = e
|
||||
return e
|
||||
|
|
@ -741,9 +805,7 @@ local fs_scroll_container = {
|
|||
end
|
||||
out[#out +1] = "scroll_container_end[]"
|
||||
|
||||
if e._scrollbar then
|
||||
out[#out +1] = e._scrollbar:render()
|
||||
else
|
||||
if not e._scrollbar then
|
||||
local v = e.__fs._ctx.fields[e.__id]
|
||||
out[#out +1] = string.format("scrollbar[-800,-800;0,0;%s;%s;%s]", get(e, "orientation"), e.__id, v and v:sub(5) or "")
|
||||
end
|
||||
|
|
@ -769,6 +831,7 @@ local fs_scroll_container = {
|
|||
fn()
|
||||
else
|
||||
fs_scrollbar(fn, ...)
|
||||
:options({arrows = "hide"})
|
||||
end
|
||||
e._scrollbar = ctx[1]
|
||||
ctx = ctx.__parent
|
||||
|
|
@ -806,7 +869,7 @@ setmetatable(fs_scroll_container, {
|
|||
__index = ctx._events,
|
||||
__newindex = ctx._events
|
||||
}),
|
||||
__fs = ctx.__parent or ctx
|
||||
__fs = ctx.__fs or ctx
|
||||
}
|
||||
e.__id = new_id()
|
||||
setmetatable(e, fs_scroll_container)
|
||||
|
|
@ -821,6 +884,10 @@ local function fs_scroll_container_end()
|
|||
minetest.log("warn", "`scroll_container_end` has no scroll container to end; it will be ignored.")
|
||||
return
|
||||
end
|
||||
-- If the container has a custom scrollbar, we add it _after_ the container's contents.
|
||||
if ctx._scrollbar then
|
||||
table.insert(ctx.__parent, ctx._scrollbar)
|
||||
end
|
||||
ctx = ctx.__parent
|
||||
end
|
||||
|
||||
|
|
@ -858,7 +925,7 @@ local fs_group = {
|
|||
fs_group.__index = fs_group
|
||||
setmetatable(fs_group, {
|
||||
__call = function(_, x, y, w, h)
|
||||
local e = {x = x, y = y, w = w, h = h, _gap = 0, __parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__parent or ctx}
|
||||
local e = {x = x, y = y, w = w, h = h, _gap = 0, __parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__fs or ctx}
|
||||
setmetatable(e, fs_group)
|
||||
table.insert(ctx, e)
|
||||
ctx = e
|
||||
|
|
@ -871,6 +938,19 @@ local function fs_group_end()
|
|||
minetest.log("warn", "`group_end` has no group to end; it will be ignored.")
|
||||
return
|
||||
end
|
||||
for i = 0, 1 do
|
||||
local axis = i == 0 and "w" or "h"
|
||||
if ctx[axis] == "auto" then
|
||||
local size = 0
|
||||
for i = 1, #ctx do
|
||||
local child_size = tonumber(ctx[i][axis]) or 0
|
||||
if child_size > size then
|
||||
size = child_size
|
||||
end
|
||||
end
|
||||
ctx[axis] = size
|
||||
end
|
||||
end
|
||||
ctx = ctx.__parent
|
||||
end
|
||||
|
||||
|
|
@ -894,16 +974,14 @@ local fs_row = {
|
|||
-- Pass 1: Collect total sizing information to allow layout computation.
|
||||
for i = 1, #e do
|
||||
local c = e[i]
|
||||
if not c._no_layout then
|
||||
if c.w and not c._no_layout then
|
||||
local ca, ca_flex = resolve_flex_layout_units(get(c, e._direction == "column" and "h" or "w"), axis_size)
|
||||
if c.w then
|
||||
if ca_flex then
|
||||
flex_found = true
|
||||
c.__flex = ca
|
||||
total_grow = total_grow +ca
|
||||
else
|
||||
used_space = used_space +(ca or 0)
|
||||
end
|
||||
if ca_flex then
|
||||
flex_found = true
|
||||
c.__flex = ca
|
||||
total_grow = total_grow +ca
|
||||
else
|
||||
used_space = used_space +(ca or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -924,8 +1002,10 @@ local fs_row = {
|
|||
if c.__flex then
|
||||
c.__flex = c.__flex *grow_basis
|
||||
current = current +c.__flex +e._gap
|
||||
else
|
||||
elseif c.w then
|
||||
current = current +resolve_layout_units(get(c, e._direction == "column" and "h" or "w"), axis_size) +e._gap
|
||||
else
|
||||
current = current +e._gap
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -956,9 +1036,9 @@ local fs_row = {
|
|||
out[#out +1] = c:render(cx, cy, cw, ch)
|
||||
elseif c.w then
|
||||
if e._direction == "column" then
|
||||
out[#out +1] = c:render(resolve_layout_units(get(c, "x"), w) +x, base +c.__flex_offset, resolve_layout_units(get(c, "w"), w), c.__flex)
|
||||
out[#out +1] = c:render(resolve_layout_units(get(c, "x"), w) +x, base +c.__flex_offset, resolve_layout_units(get(c, "w"), w), c.__flex or resolve_layout_units(get(c, "h"), h))
|
||||
else
|
||||
out[#out +1] = c:render(base +c.__flex_offset, resolve_layout_units(get(c, "y"), h) +y, c.__flex, resolve_layout_units(get(c, "h"), h))
|
||||
out[#out +1] = c:render(base +c.__flex_offset, resolve_layout_units(get(c, "y"), h) +y, c.__flex or resolve_layout_units(get(c, "w"), w), resolve_layout_units(get(c, "h"), h))
|
||||
end
|
||||
elseif c.x then
|
||||
if e._direction == "column" then
|
||||
|
|
@ -987,7 +1067,7 @@ local fs_row = {
|
|||
fs_row.__index = fs_row
|
||||
setmetatable(fs_row, {
|
||||
__call = function(_, x, y, w, h)
|
||||
local e = {x = x, y = y, w = w, h = h, _gap = 0, __parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__parent or ctx}
|
||||
local e = {x = x, y = y, w = w, h = h, _gap = 0, __parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__fs or ctx}
|
||||
setmetatable(e, fs_row)
|
||||
table.insert(ctx, e)
|
||||
ctx = e
|
||||
|
|
@ -1001,6 +1081,17 @@ local function fs_row_end()
|
|||
minetest.log("warn", "`row_end` has no row to end; it will be ignored.")
|
||||
return
|
||||
end
|
||||
local axis = ctx._direction == "column" and "w" or "h"
|
||||
if ctx[axis] == "auto" then
|
||||
local size = 0
|
||||
for i = 1, #ctx do
|
||||
local child_size = tonumber(ctx[i][axis]) or 0
|
||||
if child_size > size then
|
||||
size = child_size
|
||||
end
|
||||
end
|
||||
ctx[axis] = size
|
||||
end
|
||||
ctx = ctx.__parent
|
||||
end
|
||||
|
||||
|
|
@ -1157,15 +1248,7 @@ local Context = {
|
|||
|
||||
fs._ctx = e
|
||||
|
||||
for _, el in ipairs(fs) do
|
||||
for _, x in pairs(el) do
|
||||
local mt = getmetatable(x)
|
||||
if mt == state or mt == DerivedState then
|
||||
x._getters[e.id] = e
|
||||
e._linked_states[x] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
e:collect_state_depends()
|
||||
|
||||
e._events = fs._events
|
||||
|
||||
|
|
@ -1178,6 +1261,22 @@ local Context = {
|
|||
minetest.show_formspec(e.target, e.id, str)
|
||||
end
|
||||
end,
|
||||
get_states_from_elem = function(e, el)
|
||||
for k, x in pairs(el) do
|
||||
local mt = getmetatable(x)
|
||||
if mt == state or mt == DerivedState then
|
||||
x._getters[e.id] = e
|
||||
e._linked_states[x] = true
|
||||
elseif tonumber(k) then
|
||||
e:get_states_from_elem(x)
|
||||
end
|
||||
end
|
||||
end,
|
||||
collect_state_depends = function(e)
|
||||
for _, el in ipairs(e._window) do
|
||||
e:get_states_from_elem(el)
|
||||
end
|
||||
end,
|
||||
clear_state_bindings = function(e)
|
||||
for x in pairs(e._linked_states) do
|
||||
x._getters[e.id] = nil
|
||||
|
|
@ -1361,6 +1460,9 @@ imfs = {
|
|||
_contexts = contexts,
|
||||
_inventories = inventories,
|
||||
|
||||
_new_id = new_id,
|
||||
_unique_id = unique_id,
|
||||
|
||||
state = state,
|
||||
derive = derive,
|
||||
get_field = get,
|
||||
|
|
@ -1374,7 +1476,7 @@ imfs = {
|
|||
resolve_layout_units = resolve_layout_units,
|
||||
resolve_flex_layout_units = resolve_flex_layout_units,
|
||||
container_start = function()
|
||||
local container = {__parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__parent or ctx}
|
||||
local container = {__parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__fs or ctx}
|
||||
table.insert(ctx, container)
|
||||
ctx = container
|
||||
end,
|
||||
|
|
@ -1389,7 +1491,6 @@ imfs = {
|
|||
|
||||
style = fs_style,
|
||||
tooltip = fs_tooltip,
|
||||
_named_tooltip = fs_named_tooltip,
|
||||
label = fs_label,
|
||||
arealabel = fs_arealabel,
|
||||
hypertext = fs_hypertext,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue