Moved state methods to new state class
Using setmetatable also
This commit is contained in:
parent
59eef1aa23
commit
e20e7aa7bd
715
smartfs.lua
715
smartfs.lua
@ -542,6 +542,374 @@ function element_class:getTooltipString()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Smartfs Framework - State class Methods
|
||||
------------------------------------------------------
|
||||
local state_class = {}
|
||||
function state_class:get(name)
|
||||
return self._ele[name]
|
||||
end
|
||||
|
||||
function state_class:close()
|
||||
self.closed = true
|
||||
end
|
||||
|
||||
function state_class:getSize()
|
||||
return self._size
|
||||
end
|
||||
|
||||
function state_class:size(w,h)
|
||||
self._size = {w=w,h=h}
|
||||
end
|
||||
state_class.setSize = state_class.size
|
||||
|
||||
function state_class:getNamespace()
|
||||
local ref = self
|
||||
local namespace = ""
|
||||
while ref.location.type == "container" do
|
||||
namespace = ref.location.containerElement.name.."#"..namespace
|
||||
ref = ref.location.parentState -- step near to the root
|
||||
end
|
||||
return namespace
|
||||
end
|
||||
|
||||
function state_class:_buildFormspec_(size)
|
||||
local res = ""
|
||||
if self._size and size then
|
||||
res = "size["..self._size.w..","..self._size.h.."]"
|
||||
end
|
||||
for key,val in pairs(self._ele) do
|
||||
if val:getVisible() then
|
||||
res = res .. val:getBackgroundString() .. val:build() .. val:getTooltipString()
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function state_class:_get_element_recursive_(field)
|
||||
local topfield
|
||||
for z in field:gmatch("[^#]+") do
|
||||
topfield = z
|
||||
break
|
||||
end
|
||||
local element = self._ele[topfield]
|
||||
if element and field == topfield then
|
||||
return element
|
||||
elseif element then
|
||||
if element._getSubElement_ then
|
||||
local rel_field = string.sub(field, string.len(topfield)+2)
|
||||
return element:_getSubElement_(rel_field)
|
||||
else
|
||||
return element
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- process onInput hook for the state
|
||||
function state_class:_sfs_process_oninput_(fields, player)
|
||||
if self._onInput then
|
||||
self:_onInput(fields, player)
|
||||
end
|
||||
-- recursive all onInput hooks on visible containers
|
||||
for elename, eledef in pairs(self._ele) do
|
||||
if eledef.getContainerState and eledef:getVisible() then
|
||||
eledef:getContainerState():_sfs_process_oninput_(fields, player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Receive fields and actions from formspec
|
||||
function state_class:_sfs_on_receive_fields_(player, fields)
|
||||
local fields_todo = {}
|
||||
for field, value in pairs(fields) do
|
||||
local element = self:_get_element_recursive_(field)
|
||||
if element then
|
||||
fields_todo[field] = { element = element, value = value }
|
||||
end
|
||||
end
|
||||
|
||||
for field, todo in pairs(fields_todo) do
|
||||
todo.element:setValue(todo.value)
|
||||
end
|
||||
|
||||
self:_sfs_process_oninput_(fields, player)
|
||||
|
||||
for field, todo in pairs(fields_todo) do
|
||||
if todo.element.submit then
|
||||
todo.element:submit(todo.value, player)
|
||||
end
|
||||
end
|
||||
-- handle key_enter
|
||||
if fields.key_enter and fields.key_enter_field then
|
||||
local element = self:_get_element_recursive_(fields.key_enter_field)
|
||||
if element and element.submit_key_enter then
|
||||
element:submit_key_enter(fields[fields.key_enter_field], player)
|
||||
end
|
||||
end
|
||||
|
||||
if not fields.quit and not self.closed and not self.obsolete then
|
||||
self:show()
|
||||
else
|
||||
self.players:disconnect(player)
|
||||
if not fields.quit and self.closed and not self.obsolete then
|
||||
--closed by application (without fields.quit). currently not supported, see: https://github.com/minetest/minetest/pull/4675
|
||||
minetest.show_formspec(player,"","size[5,1]label[0,0;Formspec closing not yet created!]")
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function state_class:onInput(func)
|
||||
self._onInput = func -- (fields, player)
|
||||
end
|
||||
|
||||
function state_class:load(file)
|
||||
local file = io.open(file, "r")
|
||||
if file then
|
||||
local table = minetest.deserialize(file:read("*all"))
|
||||
if type(table) == "table" then
|
||||
if table.size then
|
||||
self._size = table.size
|
||||
end
|
||||
for key,val in pairs(table.ele) do
|
||||
self:element(val.type,val)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function state_class:save(file)
|
||||
local res = {ele={}}
|
||||
|
||||
if self._size then
|
||||
res.size = self._size
|
||||
end
|
||||
|
||||
for key,val in pairs(self._ele) do
|
||||
res.ele[key] = val.data
|
||||
end
|
||||
|
||||
local file = io.open(file, "w")
|
||||
if file then
|
||||
file:write(minetest.serialize(res))
|
||||
file:close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function state_class:setparam(key,value)
|
||||
if not key then return end
|
||||
self.param[key] = value
|
||||
return true
|
||||
end
|
||||
|
||||
function state_class:getparam(key,default)
|
||||
if not key then return end
|
||||
return self.param[key] or default
|
||||
end
|
||||
|
||||
function state_class:element(typen,data)
|
||||
local type = smartfs._edef[typen]
|
||||
assert(type, "Element type "..typen.." does not exist!")
|
||||
assert(not self._ele[data.name], "Element "..data.name.." already exists")
|
||||
|
||||
local ele = setmetatable({}, {__index = element_class})
|
||||
ele.name = data.name
|
||||
ele.root = self
|
||||
ele.data = data
|
||||
ele.data.type = typen
|
||||
ele.data.visible = true
|
||||
for key, val in pairs(type) do
|
||||
ele[key] = val
|
||||
end
|
||||
self._ele[data.name] = ele
|
||||
type.onCreate(ele)
|
||||
return self._ele[data.name]
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Smartfs Framework - State class Methods - Build time only
|
||||
------------------------------------------------------
|
||||
function state_class:button(x, y, w, h, name, text, exitf)
|
||||
return self:element("button", {
|
||||
pos = {x=x,y=y},
|
||||
size = {w=w,h=h},
|
||||
name = name,
|
||||
value = text,
|
||||
closes = exitf or false
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:image_button(x, y, w, h, name, text, image, exitf)
|
||||
return self:element("button", {
|
||||
pos = {x=x,y=y},
|
||||
size = {w=w,h=h},
|
||||
name = name,
|
||||
value = text,
|
||||
image = image,
|
||||
closes = exitf or false
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:item_image_button(x, y, w, h, name, text, item, exitf)
|
||||
return self:element("button", {
|
||||
pos = {x=x,y=y},
|
||||
size = {w=w,h=h},
|
||||
name = name,
|
||||
value = text,
|
||||
item = item,
|
||||
closes = exitf or false
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:label(x, y, name, text)
|
||||
return self:element("label", {
|
||||
pos = {x=x,y=y},
|
||||
name = name,
|
||||
value = text,
|
||||
vertical = false
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:vertlabel(x, y, name, text)
|
||||
return self:element("label", {
|
||||
pos = {x=x,y=y},
|
||||
name = name,
|
||||
value = text,
|
||||
vertical = true
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:toggle(x, y, w, h, name, list)
|
||||
return self:element("toggle", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
id = 1,
|
||||
list = list
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:field(x, y, w, h, name, label)
|
||||
return self:element("field", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = "",
|
||||
label = label
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:pwdfield(x, y, w, h, name, label)
|
||||
local res = self:element("field", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = "",
|
||||
label = label
|
||||
})
|
||||
res:isPassword(true)
|
||||
return res
|
||||
end
|
||||
|
||||
function state_class:textarea(x, y, w, h, name, label)
|
||||
local res = self:element("field", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = "",
|
||||
label = label
|
||||
})
|
||||
res:isMultiline(true)
|
||||
return res
|
||||
end
|
||||
|
||||
function state_class:image(x, y, w, h, name, img)
|
||||
return self:element("image", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = img,
|
||||
imgtype = "image"
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:background(x, y, w, h, name, img)
|
||||
return self:element("image", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
background = img,
|
||||
imgtype = "background"
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:item_image(x, y, w, h, name, img)
|
||||
return self:element("image", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = img,
|
||||
imgtype = "item"
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:checkbox(x, y, name, label, selected)
|
||||
return self:element("checkbox", {
|
||||
pos = {x=x, y=y},
|
||||
name = name,
|
||||
value = selected,
|
||||
label = label
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:listbox(x, y, w, h, name, selected, transparent)
|
||||
return self:element("list", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
selected = selected,
|
||||
transparent = transparent
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:dropdown(x, y, w, h, name, selected)
|
||||
return self:element("dropdown", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
selected = selected
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:inventory(x, y, w, h, name)
|
||||
return self:element("inventory", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:container(x, y, name, relative)
|
||||
return self:element("container", {
|
||||
pos = {x=x, y=y},
|
||||
name = name,
|
||||
relative = false
|
||||
})
|
||||
end
|
||||
|
||||
function state_class:view(x, y, name, relative)
|
||||
return self:element("container", {
|
||||
pos = {x=x, y=y},
|
||||
name = name,
|
||||
relative = true
|
||||
})
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Smartfs Framework - create a form object (state)
|
||||
@ -569,11 +937,7 @@ function smartfs._makeState_(form, params, location, newplayer)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
------------------------------------------------------
|
||||
-- State - create returning state object
|
||||
------------------------------------------------------
|
||||
return {
|
||||
local state = {
|
||||
_ele = {},
|
||||
def = form,
|
||||
players = _make_players_(newplayer),
|
||||
@ -581,348 +945,9 @@ function smartfs._makeState_(form, params, location, newplayer)
|
||||
is_inv = (location.type == "inventory"), -- obsolete. Please use location.type="inventory" instead
|
||||
player = newplayer, -- obsolete. Please use location.player
|
||||
param = params or {},
|
||||
get = function(self,name)
|
||||
return self._ele[name]
|
||||
end,
|
||||
close = function(self)
|
||||
self.closed = true
|
||||
end,
|
||||
getSize = function(self)
|
||||
return self._size
|
||||
end,
|
||||
size = function(self,w,h)
|
||||
self._size = {w=w,h=h}
|
||||
end,
|
||||
setSize = function(self,w,h)
|
||||
self._size = {w=w,h=h}
|
||||
end,
|
||||
getNamespace = function(self)
|
||||
local ref = self
|
||||
local namespace = ""
|
||||
while ref.location.type == "container" do
|
||||
namespace = ref.location.containerElement.name.."#"..namespace
|
||||
ref = ref.location.parentState -- step near to the root
|
||||
end
|
||||
return namespace
|
||||
end,
|
||||
_buildFormspec_ = function(self,size)
|
||||
local res = ""
|
||||
if self._size and size then
|
||||
res = "size["..self._size.w..","..self._size.h.."]"
|
||||
end
|
||||
for key,val in pairs(self._ele) do
|
||||
if val:getVisible() then
|
||||
res = res .. val:getBackgroundString() .. val:build() .. val:getTooltipString()
|
||||
end
|
||||
end
|
||||
return res
|
||||
end,
|
||||
show = location._show_,
|
||||
_get_element_recursive_ = function(self, field)
|
||||
local topfield
|
||||
for z in field:gmatch("[^#]+") do
|
||||
topfield = z
|
||||
break
|
||||
end
|
||||
local element = self._ele[topfield]
|
||||
if element and field == topfield then
|
||||
return element
|
||||
elseif element then
|
||||
if element._getSubElement_ then
|
||||
local rel_field = string.sub(field, string.len(topfield)+2)
|
||||
return element:_getSubElement_(rel_field)
|
||||
else
|
||||
return element
|
||||
end
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
-- process onInput hook for the state
|
||||
_sfs_process_oninput_ = function(self, fields, player)
|
||||
if self._onInput then
|
||||
self:_onInput(fields, player)
|
||||
end
|
||||
-- recursive all onInput hooks on visible containers
|
||||
for elename, eledef in pairs(self._ele) do
|
||||
if eledef.getContainerState and eledef:getVisible() then
|
||||
eledef:getContainerState():_sfs_process_oninput_(fields, player)
|
||||
end
|
||||
end
|
||||
end,
|
||||
-- Receive fields and actions from formspec
|
||||
_sfs_on_receive_fields_ = function(self, player, fields)
|
||||
|
||||
local fields_todo = {}
|
||||
for field, value in pairs(fields) do
|
||||
local element = self:_get_element_recursive_(field)
|
||||
if element then
|
||||
fields_todo[field] = { element = element, value = value }
|
||||
end
|
||||
end
|
||||
|
||||
for field, todo in pairs(fields_todo) do
|
||||
todo.element:setValue(todo.value)
|
||||
end
|
||||
|
||||
self:_sfs_process_oninput_(fields, player)
|
||||
|
||||
for field, todo in pairs(fields_todo) do
|
||||
if todo.element.submit then
|
||||
todo.element:submit(todo.value, player)
|
||||
end
|
||||
end
|
||||
-- handle key_enter
|
||||
if fields.key_enter and fields.key_enter_field then
|
||||
local element = self:_get_element_recursive_(fields.key_enter_field)
|
||||
if element and element.submit_key_enter then
|
||||
element:submit_key_enter(fields[fields.key_enter_field], player)
|
||||
end
|
||||
end
|
||||
|
||||
if not fields.quit and not self.closed and not self.obsolete then
|
||||
self:show()
|
||||
else
|
||||
self.players:disconnect(player)
|
||||
if not fields.quit and self.closed and not self.obsolete then
|
||||
--closed by application (without fields.quit). currently not supported, see: https://github.com/minetest/minetest/pull/4675
|
||||
minetest.show_formspec(player,"","size[5,1]label[0,0;Formspec closing not yet created!]")
|
||||
end
|
||||
end
|
||||
return true
|
||||
end,
|
||||
onInput = function(self, func)
|
||||
self._onInput = func -- (fields, player)
|
||||
end,
|
||||
load = function(self,file)
|
||||
local file = io.open(file, "r")
|
||||
if file then
|
||||
local table = minetest.deserialize(file:read("*all"))
|
||||
if type(table) == "table" then
|
||||
if table.size then
|
||||
self._size = table.size
|
||||
end
|
||||
for key,val in pairs(table.ele) do
|
||||
self:element(val.type,val)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
save = function(self,file)
|
||||
local res = {ele={}}
|
||||
|
||||
if self._size then
|
||||
res.size = self._size
|
||||
end
|
||||
|
||||
for key,val in pairs(self._ele) do
|
||||
res.ele[key] = val.data
|
||||
end
|
||||
|
||||
local file = io.open(file, "w")
|
||||
if file then
|
||||
file:write(minetest.serialize(res))
|
||||
file:close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end,
|
||||
setparam = function(self,key,value)
|
||||
if not key then return end
|
||||
self.param[key] = value
|
||||
return true
|
||||
end,
|
||||
getparam = function(self,key,default)
|
||||
if not key then return end
|
||||
return self.param[key] or default
|
||||
end,
|
||||
element = function(self,typen,data)
|
||||
local type = smartfs._edef[typen]
|
||||
assert(type, "Element type "..typen.." does not exist!")
|
||||
assert(not self._ele[data.name], "Element "..data.name.." already exists")
|
||||
|
||||
local ele = setmetatable({}, {__index = element_class})
|
||||
ele.name = data.name
|
||||
ele.root = self
|
||||
ele.data = data
|
||||
ele.data.type = typen
|
||||
ele.data.visible = true
|
||||
|
||||
for key, val in pairs(type) do
|
||||
ele[key] = val
|
||||
end
|
||||
|
||||
self._ele[data.name] = ele
|
||||
|
||||
type.onCreate(ele)
|
||||
|
||||
return self._ele[data.name]
|
||||
end,
|
||||
|
||||
------------------------------------------------------
|
||||
-- State - Element Constructors
|
||||
------------------------------------------------------
|
||||
button = function(self, x, y, w, h, name, text, exitf)
|
||||
return self:element("button", {
|
||||
pos = {x=x,y=y},
|
||||
size = {w=w,h=h},
|
||||
name = name,
|
||||
value = text,
|
||||
closes = exitf or false
|
||||
})
|
||||
end,
|
||||
image_button = function(self, x, y, w, h, name, text, image, exitf)
|
||||
return self:element("button", {
|
||||
pos = {x=x,y=y},
|
||||
size = {w=w,h=h},
|
||||
name = name,
|
||||
value = text,
|
||||
image = image,
|
||||
closes = exitf or false
|
||||
})
|
||||
end,
|
||||
item_image_button = function(self, x, y, w, h, name, text, item, exitf)
|
||||
return self:element("button", {
|
||||
pos = {x=x,y=y},
|
||||
size = {w=w,h=h},
|
||||
name = name,
|
||||
value = text,
|
||||
item = item,
|
||||
closes = exitf or false
|
||||
})
|
||||
end,
|
||||
label = function(self, x, y, name, text)
|
||||
return self:element("label", {
|
||||
pos = {x=x,y=y},
|
||||
name = name,
|
||||
value = text,
|
||||
vertical = false
|
||||
})
|
||||
end,
|
||||
vertlabel = function(self, x, y, name, text)
|
||||
return self:element("label", {
|
||||
pos = {x=x,y=y},
|
||||
name = name,
|
||||
value = text,
|
||||
vertical = true
|
||||
})
|
||||
end,
|
||||
toggle = function(self, x, y, w, h, name, list)
|
||||
return self:element("toggle", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
id = 1,
|
||||
list = list
|
||||
})
|
||||
end,
|
||||
field = function(self, x, y, w, h, name, label)
|
||||
return self:element("field", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = "",
|
||||
label = label
|
||||
})
|
||||
end,
|
||||
pwdfield = function(self, x, y, w, h, name, label)
|
||||
local res = self:element("field", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = "",
|
||||
label = label
|
||||
})
|
||||
res:isPassword(true)
|
||||
return res
|
||||
end,
|
||||
textarea = function(self, x, y, w, h, name, label)
|
||||
local res = self:element("field", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = "",
|
||||
label = label
|
||||
})
|
||||
res:isMultiline(true)
|
||||
return res
|
||||
end,
|
||||
image = function(self, x, y, w, h, name, img)
|
||||
return self:element("image", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = img,
|
||||
imgtype = "image"
|
||||
})
|
||||
end,
|
||||
background = function(self, x, y, w, h, name, img)
|
||||
return self:element("image", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
background = img,
|
||||
imgtype = "background"
|
||||
})
|
||||
end,
|
||||
item_image = function(self, x, y, w, h, name, img)
|
||||
return self:element("image", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
value = img,
|
||||
imgtype = "item"
|
||||
})
|
||||
end,
|
||||
checkbox = function(self, x, y, name, label, selected)
|
||||
return self:element("checkbox", {
|
||||
pos = {x=x, y=y},
|
||||
name = name,
|
||||
value = selected,
|
||||
label = label
|
||||
})
|
||||
end,
|
||||
listbox = function(self, x, y, w, h, name, selected, transparent)
|
||||
return self:element("list", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
selected = selected,
|
||||
transparent = transparent
|
||||
})
|
||||
end,
|
||||
dropdown = function(self, x, y, w, h, name, selected)
|
||||
return self:element("dropdown", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name,
|
||||
selected = selected
|
||||
})
|
||||
end,
|
||||
inventory = function(self, x, y, w, h, name)
|
||||
return self:element("inventory", {
|
||||
pos = {x=x, y=y},
|
||||
size = {w=w, h=h},
|
||||
name = name
|
||||
})
|
||||
end,
|
||||
container = function(self, x, y, name, relative)
|
||||
return self:element("container", {
|
||||
pos = {x=x, y=y},
|
||||
name = name,
|
||||
relative = false
|
||||
})
|
||||
end,
|
||||
view = function(self, x, y, name, relative)
|
||||
return self:element("container", {
|
||||
pos = {x=x, y=y},
|
||||
name = name,
|
||||
relative = true
|
||||
})
|
||||
end,
|
||||
}
|
||||
return setmetatable(state, {__index = state_class})
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user