From 910a51466f0f447c3f3690d47875887110dab5b3 Mon Sep 17 00:00:00 2001 From: Sabine Lim Date: Thu, 18 May 2023 02:26:26 +1000 Subject: [PATCH 1/5] Fix addObject --- Basalt/objects/Container.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Basalt/objects/Container.lua b/Basalt/objects/Container.lua index 60e04d4..5194faf 100644 --- a/Basalt/objects/Container.lua +++ b/Basalt/objects/Container.lua @@ -73,11 +73,15 @@ return function(name, basalt) for event, _ in pairs(element:getRegisteredEvents()) do self:addEvent(event, element) end - - if(element.init~=nil)then element:init() end - if(element.load~=nil)then element:load() end - if(element.draw~=nil)then element:draw() end - + if (element.init~=nil) then + element:init() + end + if (element.load~=nil) then + element:load() + end + if (element.draw~=nil) then + element:draw() + end return element end -- 2.49.1 From 72bb660eba08133e65c79cc725c8a166037f4e23 Mon Sep 17 00:00:00 2001 From: Sabine Lim Date: Thu, 18 May 2023 00:40:24 +1000 Subject: [PATCH 2/5] Refactors Fix --- Basalt/libraries/xmlParser.lua | 2 +- Basalt/plugins/reactive.lua | 234 +++++++++++++-------------------- 2 files changed, 90 insertions(+), 146 deletions(-) diff --git a/Basalt/libraries/xmlParser.lua b/Basalt/libraries/xmlParser.lua index dde7575..fbc7781 100644 --- a/Basalt/libraries/xmlParser.lua +++ b/Basalt/libraries/xmlParser.lua @@ -70,7 +70,7 @@ local XMLParser = { if #stack > 1 then error("XMLParser: unclosed " .. stack[#stack].tag) end - return top + return top.children end } diff --git a/Basalt/plugins/reactive.lua b/Basalt/plugins/reactive.lua index b9d1485..c87f4b1 100644 --- a/Basalt/plugins/reactive.lua +++ b/Basalt/plugins/reactive.lua @@ -1,35 +1,36 @@ local XMLParser = require("xmlParser") -local utils = require("utils") -local uuid = utils.uuid -local function maybeExecuteScript(nodeTree, renderContext) - for _, node in ipairs(nodeTree.children) do - if (node.tag == "script") then - return load(node.value, nil, "t", renderContext.env)() +local Layout = { + fromXML = function(text) + local nodes = XMLParser.parseText(text) + 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 + return { + nodes = nodes, + script = script + } end +} + +local function executeScript(script, env) + return load(script, nil, "t", env)() end -local function registerFunctionEvent(self, event, script, renderContext) - local eventEnv = renderContext.env - event(self, function(...) - eventEnv.event = {...} - local success, msg = pcall(load(script, nil, "t", eventEnv)) +local function registerFunctionEvent(object, event, script, env) + event(object, function(...) + local success, msg = pcall(load(script, nil, "t", env)) if not success then error("XML Error: "..msg) 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 clearEffectDependencies = function(effect) @@ -46,12 +47,6 @@ end return { basalt = function(basalt) local object = { - layout = function(path) - return { - path = path, - } - end, - reactive = function(initialValue) local value = initialValue local observerEffects = {} @@ -102,139 +97,88 @@ return { setValue(computeFn()) end) return getValue; - end - } - return object - end, + end, - VisualObject = function(base, basalt) - - local object = { - 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) + layout = function(path) + if (not fs.exists(path)) then + error("Can't open file " .. path) end - registerFunctionEvents(self, node, { - "onClick", - "onClickUp", - "onHover", - "onScroll", - "onDrag", - "onKey", - "onKeyUp", - "onRelease", - "onChar", - "onGetFocus", - "onLoseFocus", - "onResize", - "onReposition", - "onEvent", - "onLeave" - }, renderContext) - return self + local f = fs.open(path, "r") + local text = f.readAll() + f.close() + return Layout.fromXML(text) end, - } - return object - end, - ChangeableObject = function(base, basalt) - local object = { - setValuesByXMLData = function(self, node, renderContext) - base.setValuesByXMLData(self, node, renderContext) - registerFunctionEvent(self, node, { - "onChange" - }, renderContext) - return self + createObjectsFromXMLNode = function(node, env) + local objects + local layout = env[node.tag] + if (layout ~= nil) then + local updateFns = {} + for prop, expression in pairs(node.attributes) do + updateFns[prop] = basalt.derived(function() + return load("return " .. expression, nil, "t", env)() + end) + end + local props = {} + setmetatable(props, { + __index = function(_, k) + return updateFns[k]() + end + }) + objects = basalt.createObjectsFromLayout(layout, props) + else + local object = basalt:createObject(node.tag, 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 + objects = {object} + end + return objects end, + + createObjectsFromLayout = function(layout, props) + local env = _ENV + env.props = props + if (layout.script ~= nil) then + executeScript(layout.script, env) + end + local objects = {} + for _, node in ipairs(layout.nodes) do + local _objects = basalt.createObjectsFromXMLNode(node, env) + for _, object in ipairs(_objects) do + table.insert(objects, object) + end + end + return objects + end } return object end, 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 = { - setValuesByXMLData = function(self, node, renderContext) - lastXMLReferences = {} - base.setValuesByXMLData(self, node, renderContext) - - local _OBJECTS = basalt.getObjects() - - for _, child in pairs(node.children) do - local tagName = child.tag - 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, - loadLayout = function(self, path, props) - if(fs.exists(path))then - 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) + local layout = basalt.layout(path) + local objects = basalt.createObjectsFromLayout(layout, props) + for _, object in ipairs(objects) do + self:addChild(object) end return self - end, - - getXMLElements = function(self) - return lastXMLReferences - end, + end } return object end -- 2.49.1 From c5effd002c29a55d3f0d58901ddb95819b8cfc64 Mon Sep 17 00:00:00 2001 From: Sabine Lim Date: Thu, 18 May 2023 02:58:23 +1000 Subject: [PATCH 3/5] Preserve lowercase first char behaviour --- Basalt/plugins/reactive.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Basalt/plugins/reactive.lua b/Basalt/plugins/reactive.lua index c87f4b1..8f583d7 100644 --- a/Basalt/plugins/reactive.lua +++ b/Basalt/plugins/reactive.lua @@ -127,7 +127,8 @@ return { }) objects = basalt.createObjectsFromLayout(layout, props) else - local object = basalt:createObject(node.tag, node.attributes["id"]) + 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) -- 2.49.1 From eee10606510a6adc21080344ef69643377a95653 Mon Sep 17 00:00:00 2001 From: Sabine Lim Date: Thu, 18 May 2023 03:05:00 +1000 Subject: [PATCH 4/5] Default empty props --- Basalt/plugins/reactive.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Basalt/plugins/reactive.lua b/Basalt/plugins/reactive.lua index 8f583d7..5ba3856 100644 --- a/Basalt/plugins/reactive.lua +++ b/Basalt/plugins/reactive.lua @@ -173,6 +173,9 @@ return { Container = function(base, basalt) local object = { loadLayout = function(self, path, props) + if (props == nil) then + props = {} + end local layout = basalt.layout(path) local objects = basalt.createObjectsFromLayout(layout, props) for _, object in ipairs(objects) do -- 2.49.1 From 5bd4f69004c6a70bdeb5e9333a749333c4f991de Mon Sep 17 00:00:00 2001 From: Sabine Lim Date: Thu, 18 May 2023 03:45:43 +1000 Subject: [PATCH 5/5] Allow for reactive props from imperative code --- Basalt/plugins/reactive.lua | 101 +++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/Basalt/plugins/reactive.lua b/Basalt/plugins/reactive.lua index 5ba3856..b8c81e1 100644 --- a/Basalt/plugins/reactive.lua +++ b/Basalt/plugins/reactive.lua @@ -18,11 +18,11 @@ local Layout = { end } -local function executeScript(script, env) +local executeScript = function(script, env) return load(script, nil, "t", env)() end -local function registerFunctionEvent(object, event, script, env) +local registerFunctionEvent = function(object, event, script, env) event(object, function(...) local success, msg = pcall(load(script, nil, "t", env)) if not success then @@ -46,6 +46,38 @@ end return { basalt = function(basalt) + local createObjectsFromXMLNode = function(node, env) + local layout = env[node.tag] + if (layout ~= nil) then + local props = {} + for prop, expression in pairs(node.attributes) do + 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) local value = initialValue @@ -109,57 +141,26 @@ return { return Layout.fromXML(text) end, - createObjectsFromXMLNode = function(node, env) - local objects - local layout = env[node.tag] - if (layout ~= nil) then - local updateFns = {} - for prop, expression in pairs(node.attributes) do - updateFns[prop] = basalt.derived(function() - return load("return " .. expression, nil, "t", env)() - end) - end - local props = {} - setmetatable(props, { - __index = function(_, k) - return updateFns[k]() - end - }) - objects = basalt.createObjectsFromLayout(layout, props) - else - 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 - objects = {object} - end - return objects - end, - createObjectsFromLayout = function(layout, props) local env = _ENV - env.props = props + env.props = {} + local updateFns = {} + for prop, getFn in pairs(props) do + updateFns[prop] = basalt.derived(function() + return getFn() + 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 = basalt.createObjectsFromXMLNode(node, env) + local _objects = createObjectsFromXMLNode(node, env) for _, object in ipairs(_objects) do table.insert(objects, object) end @@ -173,11 +174,17 @@ return { Container = function(base, basalt) local object = { loadLayout = function(self, path, props) + local wrappedProps = {} if (props == nil) then props = {} end + for prop, value in pairs(props) do + wrappedProps[prop] = function() + return value + end + end local layout = basalt.layout(path) - local objects = basalt.createObjectsFromLayout(layout, props) + local objects = basalt.createObjectsFromLayout(layout, wrappedProps) for _, object in ipairs(objects) do self:addChild(object) end -- 2.49.1