diff options
-rw-r--r-- | ClientHamr/GuiLua/skang.lua | 244 |
1 files changed, 120 insertions, 124 deletions
diff --git a/ClientHamr/GuiLua/skang.lua b/ClientHamr/GuiLua/skang.lua index 0ba16cb..418cd10 100644 --- a/ClientHamr/GuiLua/skang.lua +++ b/ClientHamr/GuiLua/skang.lua | |||
@@ -52,12 +52,79 @@ The old skang argument types are - | |||
52 | ]] | 52 | ]] |
53 | 53 | ||
54 | 54 | ||
55 | |||
56 | -- Wrapping the entire module in do .. end helps if people just join a bunch of modules together, which apparently is popular. | 55 | -- Wrapping the entire module in do .. end helps if people just join a bunch of modules together, which apparently is popular. |
57 | -- By virtue of the fact we are stuffing our result into package.loaded[], just plain running this works as "loading the module". | 56 | -- By virtue of the fact we are stuffing our result into package.loaded[], just plain running this works as "loading the module". |
58 | do -- Only I'm not gonna indent this. | 57 | do -- Only I'm not gonna indent this. |
59 | 58 | ||
60 | 59 | ||
60 | -- There is no ThingSpace, now it's all just in this table, and meta table. Predefined here coz moduleBegin references Thing. | ||
61 | things = {} | ||
62 | Thing = {} | ||
63 | |||
64 | |||
65 | -- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated. | ||
66 | moduleBegin = function (name, author, copyright, version, timestamp, skin) | ||
67 | local _M = {} -- This is what we return to require(). | ||
68 | local level = 2 | ||
69 | |||
70 | package.loaded[name] = _M -- Stuff the result into where require() can find it, instead of returning it at the end. | ||
71 | -- Returning it at the end does the same thing. | ||
72 | -- This is so that we can have all the module stuff at the top, in this function. | ||
73 | -- Should do this before any further require(), so that circular references don't blow out. | ||
74 | |||
75 | -- Save the callers environment. | ||
76 | local savedEnvironment = getfenv(level) | ||
77 | |||
78 | -- Clone the environment into _M, so the module can access everything as usual after the setfenv() below. | ||
79 | --[[ TODO - Check if this also clones _G or _ENV. And see if it leaks stuff in either direction. | ||
80 | local _G = _G -- Only sets a local _G for this function. | ||
81 | _M._G = _G -- This clone loop might do this, but we don't want to be able to access the old _G from outside via this leak. | ||
82 | In Lua 5.1 at least, _G was special. In 5.2, _ENV sorta replaces setfenv(), but no idea if this clone loop stomps on that. | ||
83 | ]] | ||
84 | for k, v in pairs(savedEnvironment) do | ||
85 | _M[k] = v | ||
86 | end | ||
87 | |||
88 | _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M. | ||
89 | _M._NAME = name | ||
90 | _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name. | ||
91 | |||
92 | -- TODO - Should parse in an entire copyright message, and strip that down into bits, to put back together. | ||
93 | _M.AUTHOR = author | ||
94 | _M.COPYRIGHT = copyright .. ' ' .. author | ||
95 | -- TODO - Translate the version number into a version string. | ||
96 | _M.VERSION = version .. ' lookup version here ' .. timestamp | ||
97 | -- TODO - If there's no skin passed in, try to find the file skin .. '.skang' and load that instead. | ||
98 | _M.DEFAULT_SKANG = skin | ||
99 | |||
100 | |||
101 | --_G[_M._NAME] = _M -- Stuff it into a global of the same name. | ||
102 | -- Not such a good idea to stomp on global name space. | ||
103 | -- It's also redundant coz we get stored in package.loaded[_M._NAME] anyway. | ||
104 | -- This is why module() is broken. | ||
105 | |||
106 | setmetatable(_M, Thing) | ||
107 | _M.savedEnvironment = savedEnvironment | ||
108 | -- setfenv() sets the environment for the FUNCTION, stack level deep. | ||
109 | -- The number is the stack level - | ||
110 | -- 0 running thread, 1 current function, 2 function that called this function, etc | ||
111 | setfenv(level, _M) -- Use the result for the modules internal global environment, so they don't need to qualify internal names. | ||
112 | -- Dunno if this causes problems with the do ... end style of joining modules. It does. So we need to restore in moduleEnd(). | ||
113 | -- Next question, does this screw with the environment of the skang module? No it doesn't, coz that's set up at require 'skang' time. | ||
114 | |||
115 | return _M | ||
116 | end | ||
117 | |||
118 | -- Restore the environment. | ||
119 | moduleEnd = function (module) | ||
120 | setfenv(2, module.savedEnvironment) | ||
121 | end | ||
122 | |||
123 | -- Call this now so that from now on, this is like any other module. | ||
124 | local _M = moduleBegin('skang', 'David Seikel', '2014', '0.0', '2014-03-19 19:01:00') | ||
125 | |||
126 | |||
127 | |||
61 | --[[ Thing package | 128 | --[[ Thing package |
62 | 129 | ||
63 | matrix-RAD had Thing as the base class of everything. Lua doesn't have | 130 | matrix-RAD had Thing as the base class of everything. Lua doesn't have |
@@ -119,38 +186,31 @@ Other Thing things are - | |||
119 | Actually, not sure matrix-RAD solved that either. lol | 186 | Actually, not sure matrix-RAD solved that either. lol |
120 | ]] | 187 | ]] |
121 | 188 | ||
189 | Thing.action = 'nada' -- An optional action to perform. | ||
190 | Thing.tell = '' -- The skang command that created this Thing. | ||
122 | 191 | ||
123 | -- There is no ThingSpace, now it's just in this table - | 192 | Thing.append = function (self,data) -- Append to the value of this Thing. |
124 | things = | 193 | end |
125 | { | ||
126 | } | ||
127 | |||
128 | Thing = | ||
129 | { | ||
130 | action = 'nada', -- An optional action to perform. | ||
131 | tell = '', -- The skang command that created this Thing. | ||
132 | 194 | ||
133 | append = function (self,data) -- Append to the value of this Thing. | 195 | Thing.isValid = function (self) -- Check if this Thing is valid, return resulting error messages in errors. |
134 | end, | ||
135 | isValid = function (self) -- Check if this Thing is valid, return resulting error messages in errors. | ||
136 | self.errors = {} | 196 | self.errors = {} |
137 | -- TODO - Should check for required, matching mask, matching type, etc. | 197 | -- TODO - Should check for required, matching mask, matching type, etc. |
138 | return true | 198 | return true |
139 | end, | 199 | end |
140 | remove = function (self) -- Delete this Thing. | ||
141 | end, | ||
142 | 200 | ||
143 | errors = {}, -- A list of errors returned by isValid(). | 201 | Thing.remove = function (self) -- Delete this Thing. |
202 | end | ||
144 | 203 | ||
145 | isReadOnly = false, -- Is this Thing read only? | 204 | Thing.errors = {} -- A list of errors returned by isValid(). |
146 | isServer = false, -- Is this Thing server side? | ||
147 | isStub = false, -- Is this Thing a stub? | ||
148 | isStubbed = false, -- Is this Thing stubbed elsewhere? | ||
149 | 205 | ||
150 | hasCrashed = 0, -- How many times this Thing has crashed. | 206 | Thing.isReadOnly = false -- Is this Thing read only? |
207 | Thing.isServer = false -- Is this Thing server side? | ||
208 | Thing.isStub = false -- Is this Thing a stub? | ||
209 | Thing.isStubbed = false -- Is this Thing stubbed elsewhere? | ||
151 | 210 | ||
211 | Thing.hasCrashed = 0 -- How many times this Thing has crashed. | ||
152 | 212 | ||
153 | __index = function (table, key) | 213 | Thing.__index = function (table, key) |
154 | -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. | 214 | -- This only works for keys that don't exist. By definition a value of nil means it doesn't exist. |
155 | local thing = things[key] | 215 | local thing = things[key] |
156 | 216 | ||
@@ -158,7 +218,7 @@ Thing = | |||
158 | if thing then | 218 | if thing then |
159 | local result = nil | 219 | local result = nil |
160 | if key ~= thing.names[1] then | 220 | if key ~= thing.names[1] then |
161 | result = table[thing.names[1] ] | 221 | result = table[thing.names[1] ] -- This might be recursive. |
162 | end | 222 | end |
163 | return result or thing.default | 223 | return result or thing.default |
164 | end | 224 | end |
@@ -169,14 +229,14 @@ Thing = | |||
169 | 229 | ||
170 | -- If all else fails, return nil. | 230 | -- If all else fails, return nil. |
171 | return nil | 231 | return nil |
172 | end, | 232 | end |
173 | |||
174 | 233 | ||
175 | __newindex = function (table, key, value) | 234 | Thing.__newindex = function (table, key, value) |
176 | local thing = things[key] | 235 | local thing = things[key] |
177 | 236 | ||
178 | if thing then | 237 | if thing then |
179 | local name = thing.names[1] | 238 | local name = thing.names[1] |
239 | rawset(table, name, value) -- Only stuff it under the first name, the rest are left as nil. | ||
180 | if 'function' == type(value) then | 240 | if 'function' == type(value) then |
181 | thing.func = value | 241 | thing.func = value |
182 | local types = '' | 242 | local types = '' |
@@ -189,114 +249,22 @@ Thing = | |||
189 | print(thing.types[1] .. ' ' .. thing.module._NAME .. '.' .. name .. ' = ' .. (value or 'nil') .. ' -> ' .. thing.help) | 249 | print(thing.types[1] .. ' ' .. thing.module._NAME .. '.' .. name .. ' = ' .. (value or 'nil') .. ' -> ' .. thing.help) |
190 | -- TODO - Go through it's linked things and set them to. | 250 | -- TODO - Go through it's linked things and set them to. |
191 | end | 251 | end |
192 | rawset(table, name, value) | ||
193 | else | 252 | else |
194 | rawset(table, key, value) | 253 | rawset(table, key, value) -- Stuff it normally. |
195 | end | 254 | end |
196 | end, | 255 | end |
197 | |||
198 | 256 | ||
199 | -- TODO - Seemed like a good idea at the time, but do we really need it? | 257 | -- TODO - Seemed like a good idea at the time, but do we really need it? |
200 | -- __call = function (func, ...) | 258 | --Thing.__call = function (func, ...) |
201 | -- return func.func(...) | 259 | -- return func.func(...) |
202 | -- end, | 260 | -- end |
203 | } | ||
204 | |||
205 | |||
206 | -- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated. | ||
207 | moduleBegin = function (name, author, copyright, version, timestamp, skin) | ||
208 | local _M = {} -- This is what we return to require(). | ||
209 | local level = 2 | ||
210 | |||
211 | package.loaded[name] = _M -- Stuff the result into where require() can find it, instead of returning it at the end. | ||
212 | -- Returning it at the end does the same thing. | ||
213 | -- This is so that we can have all the module stuff at the top, in this function. | ||
214 | -- Should do this before any further require(), so that circular references don't blow out. | ||
215 | 261 | ||
216 | -- Save the callers environment. | ||
217 | local savedEnvironment = getfenv(level) | ||
218 | |||
219 | -- Clone the environment into _M, so the module can access everything as usual after the setfenv() below. | ||
220 | --[[ TODO - Check if this also clones _G or _ENV. And see if it leaks stuff in either direction. | ||
221 | local _G = _G -- Only sets a local _G for this function. | ||
222 | _M._G = _G -- This clone loop might do this, but we don't want to be able to access the old _G from outside via this leak. | ||
223 | In Lua 5.1 at least, _G was special. In 5.2, _ENV sorta replaces setfenv(), but no idea if this clone loop stomps on that. | ||
224 | ]] | ||
225 | for k, v in pairs(savedEnvironment) do | ||
226 | _M[k] = v | ||
227 | end | ||
228 | |||
229 | _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M. | ||
230 | _M._NAME = name | ||
231 | _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name. | ||
232 | |||
233 | -- TODO - Should parse in an entire copyright message, and strip that down into bits, to put back together. | ||
234 | _M.AUTHOR = author | ||
235 | _M.COPYRIGHT = copyright .. ' ' .. author | ||
236 | -- TODO - Translate the version number into a version string. | ||
237 | _M.VERSION = version .. ' lookup version here ' .. timestamp | ||
238 | -- TODO - If there's no skin passed in, try to find the file skin .. '.skang' and load that instead. | ||
239 | _M.DEFAULT_SKANG = skin | ||
240 | |||
241 | |||
242 | --_G[_M._NAME] = _M -- Stuff it into a global of the same name. | ||
243 | -- Not such a good idea to stomp on global name space. | ||
244 | -- It's also redundant coz we get stored in package.loaded[_M._NAME] anyway. | ||
245 | -- This is why module() is broken. | ||
246 | |||
247 | setmetatable(_M, Thing) | ||
248 | _M.savedEnvironment = savedEnvironment | ||
249 | -- setfenv() sets the environment for the FUNCTION, stack level deep. | ||
250 | -- The number is the stack level - | ||
251 | -- 0 running thread, 1 current function, 2 function that called this function, etc | ||
252 | setfenv(level, _M) -- Use the result for the modules internal global environment, so they don't need to qualify internal names. | ||
253 | -- Dunno if this causes problems with the do ... end style of joining modules. It does. So we need to restore in moduleEnd(). | ||
254 | -- Next question, does this screw with the environment of the skang module? No it doesn't, coz that's set up at require 'skang' time. | ||
255 | |||
256 | return _M | ||
257 | end | ||
258 | |||
259 | -- Restore the environment. | ||
260 | moduleEnd = function (module) | ||
261 | setfenv(2, module.savedEnvironment) | ||
262 | end | ||
263 | |||
264 | -- Call this now so that from now on, this is like any other module. | ||
265 | local _M = moduleBegin('skang', 'David Seikel', '2014', '0.0', '2014-03-19 19:01:00') | ||
266 | |||
267 | |||
268 | --[[ TODO - It might be worth it to combine parameters and commands, since in Lua, functions are first class types like numbers and strings. | ||
269 | Merging widgets might work to. B-) | ||
270 | This does make the entire "Things with the same name link automatically" deal work easily, since they ARE the same Thing. | ||
271 | |||
272 | Parameter gets a type, which might help since Lua is untyped, versus Java being strongly typed. | ||
273 | Widgets get a type as well, which would be label, button, edit, grid, etc. | ||
274 | A grid could even have sub types - grid,number,string,button,date. B-) | ||
275 | |||
276 | Required commands makes no sense, but can just be ignored. | ||
277 | A required widget might mean that the window HAS to have one. | ||
278 | |||
279 | Default for a command would be the actual function. | ||
280 | Default being a function makes this Thing a command. | ||
281 | Default for a widget could be the default creation arguments - '"Press me", 1, 1, 10, 50' | ||
282 | |||
283 | skang.thing(_M, 'foo,s,fooAlias', 'Foo is a bar, not the drinking type.', function () print('foo') end, nil, '"button", "The foo :"' 1, 1, 10, 50') | ||
284 | myButton = skang.widget('foo') -- Gets the default widget creation arguments. | ||
285 | myButton:colour(1, 2, 3, 4) | ||
286 | myEditor = skang.widget('foo', "edit", "Edit foo :", 5, 15, 10, 100) | ||
287 | myEditor:colour(1, 2, 3, 4, 5, 6, 7, 8) | ||
288 | myButton = 'Not default' -- myEditor and _M.foo change to. Though now _M.foo is a command, not a parameter, so maybe don't change that. | ||
289 | -- Though the 'quit' Thing could have a function that does quitting, this is just an example of NOT linking to a Thing. | ||
290 | -- If we had linked to this theoretical 'quit' Thing, then pushing that Quit button would invoke it's Thing function. | ||
291 | quitter = skang.widget(nil, 'button', 'Quit', 0.5, 0.5, 0.5, 0.5) | ||
292 | quitter:action('quit') | ||
293 | ]] | ||
294 | 262 | ||
295 | -- skang.thing() stashes the default value into _M['bar'], and the details into things['bar']. | 263 | -- skang.thing() stashes the default value into _M['bar'], and the details into things['bar']. |
296 | -- names - a comma seperated list of names, aliasas, and shortcuts. The first one is the official name. | 264 | -- names - a comma seperated list of names, aliasas, and shortcuts. The first one is the official name. |
297 | -- help - help text describing this Thing. | 265 | -- help - help text describing this Thing. |
298 | -- default - the default value. This could be a funcion, making this a command. | 266 | -- default - the default value. This could be a funcion, making this a command. |
299 | -- types - a comma separated list of types. The first is the type of the Thing itself, the rest are for multi value Things. Or argument types for functions. | 267 | -- types - a comma separated list of types. The first is the type of the Thing itself, the rest are for multi value Things. Or argument types for commands. |
300 | -- widget - default widget command arguments for creating this Thing as a widget. | 268 | -- widget - default widget command arguments for creating this Thing as a widget. |
301 | -- required - "boolean" to say if this thing is required. TODO - Maybe fold this into types somehow, or acl? | 269 | -- required - "boolean" to say if this thing is required. TODO - Maybe fold this into types somehow, or acl? |
302 | -- acl - Access Control List defining security restrains. | 270 | -- acl - Access Control List defining security restrains. |
@@ -336,6 +304,34 @@ thing = function (module, names, help, default, types, widget, required, acl, bo | |||
336 | module[name] = default | 304 | module[name] = default |
337 | end | 305 | end |
338 | 306 | ||
307 | --[[ TODO - It might be worth it to combine parameters and commands, since in Lua, functions are first class types like numbers and strings. | ||
308 | Merging widgets might work to. B-) | ||
309 | This does make the entire "Things with the same name link automatically" deal work easily, since they ARE the same Thing. | ||
310 | |||
311 | Parameter gets a type, which might help since Lua is untyped, versus Java being strongly typed. | ||
312 | Widgets get a type as well, which would be label, button, edit, grid, etc. | ||
313 | A grid could even have sub types - grid,number,string,button,date. B-) | ||
314 | |||
315 | Required commands makes no sense, but can just be ignored. | ||
316 | A required widget might mean that the window HAS to have one. | ||
317 | |||
318 | Default for a command would be the actual function. | ||
319 | Default being a function makes this Thing a command. | ||
320 | Default for a widget could be the default creation arguments - '"Press me", 1, 1, 10, 50' | ||
321 | |||
322 | skang.thing(_M, 'foo,s,fooAlias', 'Foo is a bar, not the drinking type.', function () print('foo') end, nil, '"button", "The foo :"' 1, 1, 10, 50') | ||
323 | myButton = skang.widget('foo') -- Gets the default widget creation arguments. | ||
324 | myButton:colour(1, 2, 3, 4) | ||
325 | myEditor = skang.widget('foo', "edit", "Edit foo :", 5, 15, 10, 100) | ||
326 | myEditor:colour(1, 2, 3, 4, 5, 6, 7, 8) | ||
327 | myButton = 'Not default' -- myEditor and _M.foo change to. Though now _M.foo is a command, not a parameter, so maybe don't change that. | ||
328 | -- Though the 'quit' Thing could have a function that does quitting, this is just an example of NOT linking to a Thing. | ||
329 | -- If we had linked to this theoretical 'quit' Thing, then pushing that Quit button would invoke it's Thing function. | ||
330 | quitter = skang.widget(nil, 'button', 'Quit', 0.5, 0.5, 0.5, 0.5) | ||
331 | quitter:action('quit') | ||
332 | ]] | ||
333 | |||
334 | |||
339 | -- TODO - Some function stubs, for now. Fill them up later. | 335 | -- TODO - Some function stubs, for now. Fill them up later. |
340 | nada = function () end | 336 | nada = function () end |
341 | 337 | ||