diff options
-rw-r--r-- | ClientHamr/GuiLua/skang.lua | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/ClientHamr/GuiLua/skang.lua b/ClientHamr/GuiLua/skang.lua new file mode 100644 index 0000000..25aedac --- /dev/null +++ b/ClientHamr/GuiLua/skang.lua | |||
@@ -0,0 +1,186 @@ | |||
1 | -- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated. | ||
2 | |||
3 | -- Wrapping the entire module in do .. end helps if people just join a bunch of modules together, which apparently is popular. | ||
4 | -- By virtue of the fact we are stuffing our result into package.loaded[], just plain running this works as "loading the module". | ||
5 | -- TODO - Except for the "passing the name in as ..." part. B-( | ||
6 | do -- Only I'm not gonna indent this. | ||
7 | |||
8 | |||
9 | local skangModuleBegin = function (name, author, copyright, version, timestamp, skin) | ||
10 | local _M = {} -- This is what we return to require(). | ||
11 | _M._NAME = name -- Catch the module name passed in from require(), so that the file name can change. | ||
12 | _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M. | ||
13 | _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name. | ||
14 | |||
15 | _M.AUTHOR = author | ||
16 | _M.COPYRIGHT = copyright .. ' ' .. author | ||
17 | _M.VERSION = version .. ' lookup version here ' .. timestamp | ||
18 | -- TODO - If there's no skin passed in, try to find the file skin .. '.skang' and load that instead. | ||
19 | _M.DEFAULT_SKANG = skin | ||
20 | |||
21 | package.loaded[_M._NAME] = _M -- Stuff the result into where require() can find it, instead of returning it at the end. | ||
22 | -- Returning it at the end does the same thing. | ||
23 | -- This is so that we can have all the module stuff at the top. | ||
24 | -- Should do this before any further require(), so that circular references don't blow out. | ||
25 | |||
26 | --_G[_M._NAME] = _M -- Stuff it into a global of the same name. | ||
27 | -- Not such a good idea to stomp on global name space. | ||
28 | -- It's also redundant coz we get stored in package.loaded[_M._NAME] anyway. | ||
29 | -- This is why module() is broken. | ||
30 | |||
31 | --local _G = _G -- Stop stuff from here leaking into the callers _G. | ||
32 | -- Also works around the setfenv() below discarding all the old globals. | ||
33 | -- Though now we have to use _G.foo to access globals. | ||
34 | |||
35 | -- An alternative to the local _G is to declare as local ALL our imports here. May be worthwhile to do both this and local _G? | ||
36 | -- basic, is it called "basic", might be called "base"? Might have to include individual names. | ||
37 | _M.print = print | ||
38 | _M.getfenv = getfenv | ||
39 | _M.setfenv = setfenv | ||
40 | _M.require = require | ||
41 | _M.coroutine = coroutine -- Apparently this is part of basic, but it comes in it's own table anyway. | ||
42 | _M.package = package | ||
43 | _M.string = string | ||
44 | _M.table = table | ||
45 | _M.math = math | ||
46 | _M.io = io | ||
47 | _M.os = os | ||
48 | _M.debug = debug | ||
49 | |||
50 | _M.savedEnvironment = getfenv(3) | ||
51 | -- setfenv() sets the environment for the FUNCTION, but we are not in a function. | ||
52 | -- Though if we are being require()ed, then require() calls a loader, which calls us, hence we are the function. | ||
53 | -- The number is the stack level - | ||
54 | -- 0 running thread, 1 current function, 2 function that called this function, etc | ||
55 | setfenv(3, _M) -- Use the result for our internal global environment, so we don't need to qualify our internal names. | ||
56 | -- So the below "_M.bar" becomes "bar". Which might not be what we want, since we are using | ||
57 | -- _M.bar for the Thing, not the local bar the Thing wraps. So we leave _M.bar as is, coz we have _M._M above. B-) | ||
58 | -- Since _M is empty at this point, we loose the other globals, but that's we why declare local copies of stuff above. | ||
59 | -- Dunno if this causes problems with the do ... end style of joining modules. It does. | ||
60 | -- 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. | ||
61 | |||
62 | return _M | ||
63 | end | ||
64 | |||
65 | -- This is so the setfenv() stack count above is correct, and we can access ThingSpace in the final moduleBegin() version below, and STILL use this for ourselves. lol | ||
66 | local smb = function (name, author, copyright, version, timestamp, skin) | ||
67 | local result = skangModuleBegin(name, author, copyright, version, timestamp, skin) | ||
68 | return result | ||
69 | end | ||
70 | |||
71 | local _M = smb(..., 'David Seikel', '2014', '0.0', '2014-03-19 19:01:00', nil) | ||
72 | |||
73 | ThingSpace = {} | ||
74 | ThingSpace.cache = {} | ||
75 | ThingSpace.commands = {} | ||
76 | ThingSpace.modules = {} | ||
77 | ThingSpace.parameters = {} | ||
78 | ThingSpace.widgets = {} | ||
79 | |||
80 | moduleBegin = function (name, author, copyright, version, timestamp, skin) | ||
81 | local result = skangModuleBegin(name, author, copyright, version, timestamp, skin) | ||
82 | ThingSpace.modules[name] = {module = _M, name = name, } | ||
83 | return result | ||
84 | end | ||
85 | |||
86 | module = function () | ||
87 | end | ||
88 | load = function () | ||
89 | end | ||
90 | clear = function () | ||
91 | end | ||
92 | window = function (width, height, title) | ||
93 | end | ||
94 | quit = function () | ||
95 | end | ||
96 | |||
97 | -- skang.newParam stashes the default value into _M['bar'], and the details into ThingSpace.parameters['bar']. | ||
98 | -- Actually, if it's not required, and there's no default, then skip setting _M['bar']. | ||
99 | -- Could even use _index to skip setting it if it's not required and there is a default. | ||
100 | -- Also should add a metatable, and __newindex() that passes all setting of this variable to skang so it can update other stuff like linked widgets. | ||
101 | -- TODO - Users might want to use two or more copies of this module. Keep that in mind. local a = require 'test', b = require 'test' might handle that though? | ||
102 | -- Not unless skang.newParam() knows about a and b, which it wont. | ||
103 | -- Both a and b get the same table, not different copies of it. | ||
104 | -- Perhaps clone the table if it exists? There is no Lua table cloner, would have to write one. Only clone the parameters, the rest can be linked back to the original. | ||
105 | -- Then we have to deal with widgets linking to specific clones. | ||
106 | -- Actually, not sure matrix-RAD solved that either. lol | ||
107 | -- This could even be done with an array of these arguments, not including the _M. | ||
108 | newParam = function (module, name, required, shortcut, default, help) | ||
109 | module[name] = default | ||
110 | ThingSpace.parameters[name] = {module = module, name = name, required = required, shortcut = shortcut, default = default, help = help, } | ||
111 | print(name .. ' -> ' .. shortcut .. ' -> ' .. help) | ||
112 | end | ||
113 | |||
114 | -- skang.newCommand stashes the function into _M['func'], and stashes it all (including the function) into ThingSpace.commands['func']. | ||
115 | -- TODO - Could use _call so that ThingSpace.commands['foo'](arg) works. | ||
116 | newCommand = function (module, name, types, help, func) | ||
117 | module[name] = func | ||
118 | ThingSpace.commands[name] = {module = module, name = name, help = help, func = func, } | ||
119 | print(name .. '(' .. types .. ') -> ' .. help) | ||
120 | end | ||
121 | |||
122 | |||
123 | -- TODO - Some function stubs, for now. Fill them up later. | ||
124 | module = function (name) | ||
125 | end | ||
126 | clear = function () | ||
127 | end | ||
128 | window = function (width, height, title) | ||
129 | end | ||
130 | load = function (name) | ||
131 | end | ||
132 | get = function (name) | ||
133 | end | ||
134 | set = function (name, value) | ||
135 | end | ||
136 | quit = function () | ||
137 | end | ||
138 | |||
139 | newCommand(_M, 'module', 'name', '', module) | ||
140 | newCommand(_M, 'clear', '', '', clear) | ||
141 | newCommand(_M, 'window', 'w,h,name', '', window) | ||
142 | newCommand(_M, 'load', 'name', '', load) | ||
143 | newCommand(_M, 'get', 'name', '', get) -- This should be in the modules, not actually here. | ||
144 | newCommand(_M, 'set', 'name,data', '', set) -- This should be in the modules, not actually here. | ||
145 | newCommand(_M, 'quit', '', '', quit) | ||
146 | |||
147 | |||
148 | -- Restore the environment. | ||
149 | moduleEnd = function (module) | ||
150 | setfenv(2, module.savedEnvironment) | ||
151 | end | ||
152 | |||
153 | moduleEnd(_M) | ||
154 | |||
155 | end | ||
156 | |||
157 | |||
158 | |||
159 | |||
160 | -- Gotta check out this _ENV thing, 5.2 only. Seems to replace the need for setfenv(). Seems like setfenv should do what we want, and is more backward compatible. | ||
161 | -- "_ENV is not supported directly in 5.1, so its use can prevent a module from remaining compatible with 5.1. | ||
162 | -- Maybe you can simulate _ENV with setfenv and trapping gets/sets to it via __index/__newindex metamethods, or just avoid _ENV." | ||
163 | --[[ This is a Lua version of what module() does. Apparently the _LOADED stuff is needed somehow, even though it's a local? Think that was bogus. | ||
164 | |||
165 | local _LOADED = package.loaded | ||
166 | function _G.module (modname, ...) | ||
167 | local ns = _LOADED[modname] | ||
168 | if type(ns) ~= "table" then | ||
169 | ns = findtable (_G, modname) | ||
170 | if not ns then | ||
171 | error (string.format ("name conflict for module '%s'", modname)) | ||
172 | end | ||
173 | _LOADED[modname] = ns | ||
174 | end | ||
175 | if not ns._NAME then | ||
176 | ns._NAME = modname | ||
177 | ns._M = ns | ||
178 | ns._PACKAGE = gsub (modname, "[^.]*$", "") | ||
179 | end | ||
180 | setfenv (2, ns) | ||
181 | for i, f in ipairs (arg) do | ||
182 | f (ns) | ||
183 | end | ||
184 | end | ||
185 | |||
186 | ]] | ||