Refactor reactive framework #85

Merged
thesabinelim merged 5 commits from reactive-refactors into master 2023-05-18 02:02:29 +08:00
3 changed files with 110 additions and 151 deletions

View File

@@ -70,7 +70,7 @@ local XMLParser = {
if #stack > 1 then if #stack > 1 then
error("XMLParser: unclosed " .. stack[#stack].tag) error("XMLParser: unclosed " .. stack[#stack].tag)
end end
return top return top.children
end end
} }

View File

@@ -73,11 +73,15 @@ return function(name, basalt)
for event, _ in pairs(element:getRegisteredEvents()) do for event, _ in pairs(element:getRegisteredEvents()) do
self:addEvent(event, element) self:addEvent(event, element)
end end
if (element.init~=nil) then
if(element.init~=nil)then element:init() end element:init()
if(element.load~=nil)then element:load() end end
if(element.draw~=nil)then element:draw() end if (element.load~=nil) then
element:load()
end
if (element.draw~=nil) then
element:draw()
end
return element return element
end end

View File

@@ -1,35 +1,36 @@
local XMLParser = require("xmlParser") local XMLParser = require("xmlParser")
local utils = require("utils")
local uuid = utils.uuid
local function maybeExecuteScript(nodeTree, renderContext) local Layout = {
for _, node in ipairs(nodeTree.children) do fromXML = function(text)
if (node.tag == "script") then local nodes = XMLParser.parseText(text)
return load(node.value, nil, "t", renderContext.env)() local script = nil
for index, node in ipairs(nodes) do
if (node.tag == "script") then
script = node.value
table.remove(nodes, index)
break
end
end end
return {
nodes = nodes,
script = script
}
end end
}
local executeScript = function(script, env)
return load(script, nil, "t", env)()
end end
local function registerFunctionEvent(self, event, script, renderContext) local registerFunctionEvent = function(object, event, script, env)
local eventEnv = renderContext.env event(object, function(...)
event(self, function(...) local success, msg = pcall(load(script, nil, "t", env))
eventEnv.event = {...}
local success, msg = pcall(load(script, nil, "t", eventEnv))
if not success then if not success then
error("XML Error: "..msg) error("XML Error: "..msg)
end end
end) end)
end end
local function registerFunctionEvents(self, node, events, renderContext)
for _, event in pairs(events) do
local expression = node.attributes[event]
if (expression ~= nil) then
registerFunctionEvent(self, self[event], expression .. "()", renderContext)
end
end
end
local currentEffect = nil local currentEffect = nil
local clearEffectDependencies = function(effect) local clearEffectDependencies = function(effect)
@@ -45,13 +46,39 @@ end
return { return {
basalt = function(basalt) basalt = function(basalt)
local object = { local createObjectsFromXMLNode = function(node, env)
layout = function(path) local layout = env[node.tag]
return { if (layout ~= nil) then
path = path, local props = {}
} for prop, expression in pairs(node.attributes) do
end, props[prop] = load("return " .. expression, nil, "t", env)
end
return basalt.createObjectsFromLayout(layout, props)
end
local objectName = node.tag:gsub("^%l", string.upper)
local object = basalt:createObject(objectName, node.attributes["id"])
for attribute, expression in pairs(node.attributes) do
if (attribute:sub(1, 2) == "on") then
registerFunctionEvent(object, object[attribute], expression .. "()", env)
else
local update = function()
local value = load("return " .. expression, nil, "t", env)()
object:setProperty(attribute, value)
end
basalt.effect(update)
end
end
for _, child in ipairs(node.children) do
local childObjects = basalt.createObjectsFromXMLNode(child, env)
for _, childObject in ipairs(childObjects) do
object:addChild(childObject)
end
end
return {object}
end
local object = {
reactive = function(initialValue) reactive = function(initialValue)
local value = initialValue local value = initialValue
local observerEffects = {} local observerEffects = {}
@@ -102,139 +129,67 @@ return {
setValue(computeFn()) setValue(computeFn())
end) end)
return getValue; return getValue;
end end,
}
return object
end,
VisualObject = function(base, basalt) layout = function(path)
if (not fs.exists(path)) then
local object = { error("Can't open file " .. path)
setValuesByXMLData = function(self, node, renderContext)
renderContext.env[self:getName()] = self
for attribute, expression in pairs(node.attributes) do
local update = function()
local value = load("return " .. expression, nil, "t", renderContext.env)()
self:setProperty(attribute, value)
end
basalt.effect(update)
end end
registerFunctionEvents(self, node, { local f = fs.open(path, "r")
"onClick", local text = f.readAll()
"onClickUp", f.close()
"onHover", return Layout.fromXML(text)
"onScroll",
"onDrag",
"onKey",
"onKeyUp",
"onRelease",
"onChar",
"onGetFocus",
"onLoseFocus",
"onResize",
"onReposition",
"onEvent",
"onLeave"
}, renderContext)
return self
end, end,
}
return object
end,
ChangeableObject = function(base, basalt) createObjectsFromLayout = function(layout, props)
local object = { local env = _ENV
setValuesByXMLData = function(self, node, renderContext) env.props = {}
base.setValuesByXMLData(self, node, renderContext) local updateFns = {}
registerFunctionEvent(self, node, { for prop, getFn in pairs(props) do
"onChange" updateFns[prop] = basalt.derived(function()
}, renderContext) return getFn()
return self end)
end, end
setmetatable(env.props, {
__index = function(_, k)
return updateFns[k]()
end
})
if (layout.script ~= nil) then
executeScript(layout.script, env)
end
local objects = {}
for _, node in ipairs(layout.nodes) do
local _objects = createObjectsFromXMLNode(node, env)
for _, object in ipairs(_objects) do
table.insert(objects, object)
end
end
return objects
end
} }
return object return object
end, end,
Container = function(base, basalt) Container = function(base, basalt)
local lastXMLReferences = {}
local function xmlDefaultValues(node, obj, renderContext)
if (obj~=nil) then
obj:setValuesByXMLData(node, renderContext)
end
end
local function addXMLObjectType(node, addFn, self, renderContext)
if (node ~= nil) then
if (node.attributes ~= nil) then
node = {node}
end
for _, v in pairs(node) do
local obj = addFn(self, v["@id"] or uuid())
lastXMLReferences[obj:getName()] = obj
xmlDefaultValues(v, obj, renderContext)
end
end
end
local function insertChildLayout(self, layout, node, renderContext)
local updateFns = {}
for prop, expression in pairs(node.attributes) do
updateFns[prop] = basalt.derived(function()
return load("return " .. expression, nil, "t", renderContext.env)()
end)
end
local props = {}
setmetatable(props, {
__index = function(_, k)
return updateFns[k]()
end
})
self:loadLayout(layout.path, props)
end
local object = { local object = {
setValuesByXMLData = function(self, node, renderContext) loadLayout = function(self, path, props)
lastXMLReferences = {} local wrappedProps = {}
base.setValuesByXMLData(self, node, renderContext) if (props == nil) then
props = {}
local _OBJECTS = basalt.getObjects() end
for prop, value in pairs(props) do
for _, child in pairs(node.children) do wrappedProps[prop] = function()
local tagName = child.tag return value
if (tagName == "animation") then
addXMLObjectType(child, self.addAnimation, self, renderContext)
else
local layout = renderContext.env[tagName]
local objectKey = tagName:gsub("^%l", string.upper)
if (layout ~= nil) then
insertChildLayout(self, layout, child, renderContext)
elseif (_OBJECTS[objectKey] ~= nil) then
local addFn = self["add" .. objectKey]
addXMLObjectType(child, addFn, self, renderContext)
end
end end
end end
end, local layout = basalt.layout(path)
local objects = basalt.createObjectsFromLayout(layout, wrappedProps)
loadLayout = function(self, path, props) for _, object in ipairs(objects) do
if(fs.exists(path))then self:addChild(object)
local renderContext = {}
renderContext.env = _ENV
renderContext.env.props = props
local f = fs.open(path, "r")
local nodeTree = XMLParser.parseText(f.readAll())
f.close()
lastXMLReferences = {}
maybeExecuteScript(nodeTree, renderContext)
self:setValuesByXMLData(nodeTree, renderContext)
end end
return self return self
end, end
getXMLElements = function(self)
return lastXMLReferences
end,
} }
return object return object
end end