|
1 | 1 | local XMLParser = require("xmlParser") |
2 | | -local utils = require("utils") |
3 | | -local uuid = utils.uuid |
4 | 2 |
|
5 | | -local function maybeExecuteScript(nodeTree, renderContext) |
6 | | - for _, node in ipairs(nodeTree.children) do |
7 | | - if (node.tag == "script") then |
8 | | - return load(node.value, nil, "t", renderContext.env)() |
| 3 | +local Layout = { |
| 4 | + fromXML = function(text) |
| 5 | + local nodes = XMLParser.parseText(text) |
| 6 | + local script = nil |
| 7 | + for index, node in ipairs(nodes) do |
| 8 | + if (node.tag == "script") then |
| 9 | + script = node.value |
| 10 | + table.remove(nodes, index) |
| 11 | + break |
| 12 | + end |
9 | 13 | end |
| 14 | + return { |
| 15 | + nodes = nodes, |
| 16 | + script = script |
| 17 | + } |
10 | 18 | end |
| 19 | +} |
| 20 | + |
| 21 | +local executeScript = function(script, env) |
| 22 | + return load(script, nil, "t", env)() |
11 | 23 | end |
12 | 24 |
|
13 | | -local function registerFunctionEvent(self, event, script, renderContext) |
14 | | - local eventEnv = renderContext.env |
15 | | - event(self, function(...) |
16 | | - eventEnv.event = {...} |
17 | | - local success, msg = pcall(load(script, nil, "t", eventEnv)) |
| 25 | +local registerFunctionEvent = function(object, event, script, env) |
| 26 | + event(object, function(...) |
| 27 | + local success, msg = pcall(load(script, nil, "t", env)) |
18 | 28 | if not success then |
19 | 29 | error("XML Error: "..msg) |
20 | 30 | end |
21 | 31 | end) |
22 | 32 | end |
23 | 33 |
|
24 | | -local function registerFunctionEvents(self, node, events, renderContext) |
25 | | - for _, event in pairs(events) do |
26 | | - local expression = node.attributes[event] |
27 | | - if (expression ~= nil) then |
28 | | - registerFunctionEvent(self, self[event], expression .. "()", renderContext) |
29 | | - end |
30 | | - end |
31 | | -end |
32 | | - |
33 | 34 | local currentEffect = nil |
34 | 35 |
|
35 | 36 | local clearEffectDependencies = function(effect) |
|
45 | 46 |
|
46 | 47 | return { |
47 | 48 | basalt = function(basalt) |
48 | | - local object = { |
49 | | - layout = function(path) |
50 | | - return { |
51 | | - path = path, |
52 | | - } |
53 | | - end, |
| 49 | + local createObjectsFromXMLNode = function(node, env) |
| 50 | + local layout = env[node.tag] |
| 51 | + if (layout ~= nil) then |
| 52 | + local props = {} |
| 53 | + for prop, expression in pairs(node.attributes) do |
| 54 | + props[prop] = load("return " .. expression, nil, "t", env) |
| 55 | + end |
| 56 | + return basalt.createObjectsFromLayout(layout, props) |
| 57 | + end |
| 58 | + |
| 59 | + local objectName = node.tag:gsub("^%l", string.upper) |
| 60 | + local object = basalt:createObject(objectName, node.attributes["id"]) |
| 61 | + for attribute, expression in pairs(node.attributes) do |
| 62 | + if (attribute:sub(1, 2) == "on") then |
| 63 | + registerFunctionEvent(object, object[attribute], expression .. "()", env) |
| 64 | + else |
| 65 | + local update = function() |
| 66 | + local value = load("return " .. expression, nil, "t", env)() |
| 67 | + object:setProperty(attribute, value) |
| 68 | + end |
| 69 | + basalt.effect(update) |
| 70 | + end |
| 71 | + end |
| 72 | + for _, child in ipairs(node.children) do |
| 73 | + local childObjects = basalt.createObjectsFromXMLNode(child, env) |
| 74 | + for _, childObject in ipairs(childObjects) do |
| 75 | + object:addChild(childObject) |
| 76 | + end |
| 77 | + end |
| 78 | + return {object} |
| 79 | + end |
54 | 80 |
|
| 81 | + local object = { |
55 | 82 | reactive = function(initialValue) |
56 | 83 | local value = initialValue |
57 | 84 | local observerEffects = {} |
@@ -102,139 +129,67 @@ return { |
102 | 129 | setValue(computeFn()) |
103 | 130 | end) |
104 | 131 | return getValue; |
105 | | - end |
106 | | - } |
107 | | - return object |
108 | | - end, |
109 | | - |
110 | | - VisualObject = function(base, basalt) |
| 132 | + end, |
111 | 133 |
|
112 | | - local object = { |
113 | | - setValuesByXMLData = function(self, node, renderContext) |
114 | | - renderContext.env[self:getName()] = self |
115 | | - for attribute, expression in pairs(node.attributes) do |
116 | | - local update = function() |
117 | | - local value = load("return " .. expression, nil, "t", renderContext.env)() |
118 | | - self:setProperty(attribute, value) |
119 | | - end |
120 | | - basalt.effect(update) |
| 134 | + layout = function(path) |
| 135 | + if (not fs.exists(path)) then |
| 136 | + error("Can't open file " .. path) |
121 | 137 | end |
122 | | - registerFunctionEvents(self, node, { |
123 | | - "onClick", |
124 | | - "onClickUp", |
125 | | - "onHover", |
126 | | - "onScroll", |
127 | | - "onDrag", |
128 | | - "onKey", |
129 | | - "onKeyUp", |
130 | | - "onRelease", |
131 | | - "onChar", |
132 | | - "onGetFocus", |
133 | | - "onLoseFocus", |
134 | | - "onResize", |
135 | | - "onReposition", |
136 | | - "onEvent", |
137 | | - "onLeave" |
138 | | - }, renderContext) |
139 | | - return self |
| 138 | + local f = fs.open(path, "r") |
| 139 | + local text = f.readAll() |
| 140 | + f.close() |
| 141 | + return Layout.fromXML(text) |
140 | 142 | end, |
141 | | - } |
142 | | - return object |
143 | | - end, |
144 | 143 |
|
145 | | - ChangeableObject = function(base, basalt) |
146 | | - local object = { |
147 | | - setValuesByXMLData = function(self, node, renderContext) |
148 | | - base.setValuesByXMLData(self, node, renderContext) |
149 | | - registerFunctionEvent(self, node, { |
150 | | - "onChange" |
151 | | - }, renderContext) |
152 | | - return self |
153 | | - end, |
| 144 | + createObjectsFromLayout = function(layout, props) |
| 145 | + local env = _ENV |
| 146 | + env.props = {} |
| 147 | + local updateFns = {} |
| 148 | + for prop, getFn in pairs(props) do |
| 149 | + updateFns[prop] = basalt.derived(function() |
| 150 | + return getFn() |
| 151 | + end) |
| 152 | + end |
| 153 | + setmetatable(env.props, { |
| 154 | + __index = function(_, k) |
| 155 | + return updateFns[k]() |
| 156 | + end |
| 157 | + }) |
| 158 | + if (layout.script ~= nil) then |
| 159 | + executeScript(layout.script, env) |
| 160 | + end |
| 161 | + local objects = {} |
| 162 | + for _, node in ipairs(layout.nodes) do |
| 163 | + local _objects = createObjectsFromXMLNode(node, env) |
| 164 | + for _, object in ipairs(_objects) do |
| 165 | + table.insert(objects, object) |
| 166 | + end |
| 167 | + end |
| 168 | + return objects |
| 169 | + end |
154 | 170 | } |
155 | 171 | return object |
156 | 172 | end, |
157 | 173 |
|
158 | 174 | Container = function(base, basalt) |
159 | | - local lastXMLReferences = {} |
160 | | - |
161 | | - local function xmlDefaultValues(node, obj, renderContext) |
162 | | - if (obj~=nil) then |
163 | | - obj:setValuesByXMLData(node, renderContext) |
164 | | - end |
165 | | - end |
166 | | - |
167 | | - local function addXMLObjectType(node, addFn, self, renderContext) |
168 | | - if (node ~= nil) then |
169 | | - if (node.attributes ~= nil) then |
170 | | - node = {node} |
171 | | - end |
172 | | - for _, v in pairs(node) do |
173 | | - local obj = addFn(self, v["@id"] or uuid()) |
174 | | - lastXMLReferences[obj:getName()] = obj |
175 | | - xmlDefaultValues(v, obj, renderContext) |
176 | | - end |
177 | | - end |
178 | | - end |
179 | | - |
180 | | - local function insertChildLayout(self, layout, node, renderContext) |
181 | | - local updateFns = {} |
182 | | - for prop, expression in pairs(node.attributes) do |
183 | | - updateFns[prop] = basalt.derived(function() |
184 | | - return load("return " .. expression, nil, "t", renderContext.env)() |
185 | | - end) |
186 | | - end |
187 | | - local props = {} |
188 | | - setmetatable(props, { |
189 | | - __index = function(_, k) |
190 | | - return updateFns[k]() |
191 | | - end |
192 | | - }) |
193 | | - self:loadLayout(layout.path, props) |
194 | | - end |
195 | | - |
196 | 175 | local object = { |
197 | | - setValuesByXMLData = function(self, node, renderContext) |
198 | | - lastXMLReferences = {} |
199 | | - base.setValuesByXMLData(self, node, renderContext) |
200 | | - |
201 | | - local _OBJECTS = basalt.getObjects() |
202 | | - |
203 | | - for _, child in pairs(node.children) do |
204 | | - local tagName = child.tag |
205 | | - if (tagName == "animation") then |
206 | | - addXMLObjectType(child, self.addAnimation, self, renderContext) |
207 | | - else |
208 | | - local layout = renderContext.env[tagName] |
209 | | - local objectKey = tagName:gsub("^%l", string.upper) |
210 | | - if (layout ~= nil) then |
211 | | - insertChildLayout(self, layout, child, renderContext) |
212 | | - elseif (_OBJECTS[objectKey] ~= nil) then |
213 | | - local addFn = self["add" .. objectKey] |
214 | | - addXMLObjectType(child, addFn, self, renderContext) |
215 | | - end |
| 176 | + loadLayout = function(self, path, props) |
| 177 | + local wrappedProps = {} |
| 178 | + if (props == nil) then |
| 179 | + props = {} |
| 180 | + end |
| 181 | + for prop, value in pairs(props) do |
| 182 | + wrappedProps[prop] = function() |
| 183 | + return value |
216 | 184 | end |
217 | 185 | end |
218 | | - end, |
219 | | - |
220 | | - loadLayout = function(self, path, props) |
221 | | - if(fs.exists(path))then |
222 | | - local renderContext = {} |
223 | | - renderContext.env = _ENV |
224 | | - renderContext.env.props = props |
225 | | - local f = fs.open(path, "r") |
226 | | - local nodeTree = XMLParser.parseText(f.readAll()) |
227 | | - f.close() |
228 | | - lastXMLReferences = {} |
229 | | - maybeExecuteScript(nodeTree, renderContext) |
230 | | - self:setValuesByXMLData(nodeTree, renderContext) |
| 186 | + local layout = basalt.layout(path) |
| 187 | + local objects = basalt.createObjectsFromLayout(layout, wrappedProps) |
| 188 | + for _, object in ipairs(objects) do |
| 189 | + self:addChild(object) |
231 | 190 | end |
232 | 191 | return self |
233 | | - end, |
234 | | - |
235 | | - getXMLElements = function(self) |
236 | | - return lastXMLReferences |
237 | | - end, |
| 192 | + end |
238 | 193 | } |
239 | 194 | return object |
240 | 195 | end |
|
0 commit comments