You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
8.1 KiB

local _, T = ...
local function assert(cnd, text, el)
return cnd or error(text, el or 3)((0)[0])
end
local XU, factory = {}, {}
function XU:Create(otype, ...)
return assert(factory[otype], 'unknown object type')(...)
end
function XU:RegisterFactory(otype, createfunc)
assert(type(otype) == "string", 'invalid object type')
assert(type(createfunc) == "function", 'invalid factory function')
assert(factory[otype] == nil, 'object type already registered')
factory[otype] = createfunc
end
local ObjectData, getWidgetData, newWidgetData, setWidgetData = {} do
local S, skipProtoKeys = {}, {api=1, super=1, meta=1, init=1, ObjectType=1}
local altMethods = setmetatable({}, {__index=function(t, k)
t[k] = function(self, ...)
local s2 = getWidgetData(self, ObjectData).self2
return s2[k](s2, ...)
end
return t[k]
end})
function getWidgetData(self, dataType)
local r = S[dataType]
return r and r[self[0]]
end
function newWidgetData(self, dataType, proto, self2)
local d, pin = setWidgetData(self, dataType, {}), proto.init
if proto.super == nil then
local api, s2m = proto.api, self2 and proto.self2methods or nil
proto.super, proto.self2methods = getmetatable(self).__index, nil
for k,v in next, proto.super do
if api[k] == nil and (s2m and s2m[k]) == nil then
api[k] = v
end
end
if self2 then
proto.super2 = getmetatable(self2).__index
for k,v in next, proto.super2 do
if api[k] == nil and type(v) == "function" then
api[k] = altMethods[k]
end
end
end
proto.meta = {__index=api}
end
for k,v in next, proto do
if not skipProtoKeys[k] then
d[k] = v
end
end
d.self, d.self2, d.proto = self, self2, proto
setmetatable(self, proto.meta)
for i=1, pin and #pin or 0 do
pin[i](self, proto, self2)
end
return d
end
function setWidgetData(self, dataType, dat)
local SD = S[dataType] or {}
if SD[self[0]] == nil then
S[dataType], SD[self[0]] = SD, dat
end
return dat
end
end
local AddObjectMethods, CallObjectScript do
local Object, objectProto, temp = {}, {}, {}
local loScriptName, loObjectType = {exuiobject="exUIObject"}, {}
local function NIL_HANDLER() end
local function findScriptName(d, q)
local p = d.proto
if p[q] == NIL_HANDLER then
return q
end
local lq = loScriptName[q:lower()]
if p[lq] == NIL_HANDLER then
return lq
end
end
local function initObject(self, proto, self2)
local d, op = {}, objectProto[proto]
for k, v in next, op do
if v == NIL_HANDLER then
d[k] = v
end
end
op.super, op.super2 = op.super or proto.super, op.super2 or proto.super2
d.proto, d.self, d.self2 = op, self, op.super2 and self2 or nil
setWidgetData(self, ObjectData, d)
end
function CallObjectScript(self, handlerName, ...)
local d = assert(getWidgetData(self, ObjectData), "Invalid object type")
local dh = d[findScriptName(d, handlerName)]
if dh and dh ~= NIL_HANDLER then
return securecall(dh, self, ...)
end
end
local wrapCallbackFor, unwrapCallback do
local orig = {}
function wrapCallbackFor(self, self2, callback)
local r = callback and function(cself, ...)
-- also accepts self because there is no HookScript unwrap
assert(cself == self2 or cself == self, 'Invalid object type')
return callback(self, ...)
end
orig[r or 0] = callback
return r
end
function unwrapCallback(c)
return orig[c] or c
end
end
local function IsAltScript(d, self, handlerName)
local s, s2, sc2 = d.proto.super, d.proto.super2, d.proto.scripts2
if s2 == nil or (sc2 and (sc2[handlerName] or sc2[loScriptName[handlerName:lower()]])) == nil and s.HasScript(self, handlerName) then
return false, s, self
end
return true, s2, d.self2
end
function Object:GetObjectType()
local d = assert(getWidgetData(self, ObjectData), "Invalid object type")
return d.proto.ObjectType
end
function Object:IsObjectType(objectType)
local d = assert(getWidgetData(self, ObjectData), "Invalid object type")
assert(type(objectType) == "string", 'Syntax: is = Object:IsObjectType("objectType")')
local r = d.proto.isa[objectType]
if r == nil then
r = d.proto.isa[loObjectType[objectType:lower()]]
end
if r == nil then
return d.proto.super.IsObjectType(self, objectType)
end
return r ~= false
end
function Object:SetScript(handlerName, callback)
local d = assert(getWidgetData(self, ObjectData), "Invalid object type")
assert(type(handlerName) == 'string' and (callback == nil or type(callback) == 'function'), 'Syntax: Object:SetScript("handlerName", callback)')
local hn = findScriptName(d, handlerName)
if not hn then
local alt, sp, se = IsAltScript(d, self, handlerName)
return sp.SetScript(se, handlerName, alt and wrapCallbackFor(self, se, callback) or callback)
end
d[hn] = callback == nil and NIL_HANDLER or callback
end
function Object:HookScript(handlerName, callback)
local d = assert(getWidgetData(self, ObjectData), "Invalid object type")
assert(type(handlerName) == 'string' and type(callback) == 'function', 'Syntax: Object:HookScript("handlerName", callback)')
local hn = findScriptName(d, handlerName)
if not hn then
local alt, sp, se = IsAltScript(d, self, handlerName)
return sp.HookScript(se, handlerName, alt and wrapCallbackFor(self, se, callback) or callback)
end
local oc = Object.GetScript(self, hn)
if oc then
temp.f = oc
hooksecurefunc(temp, "f", callback)
callback, temp.f = temp.f
end
d[hn] = callback == nil and NIL_HANDLER or callback
end
function Object:GetScript(handlerName)
local d = assert(getWidgetData(self, ObjectData), "Invalid object type")
assert(type(handlerName) == 'string', 'Syntax: callback = Object:GetScript("handlerName")')
local hn = findScriptName(d, handlerName)
if not hn then
local alt, sp, se = IsAltScript(d, self, handlerName)
local r = sp.GetScript(se, handlerName)
return alt and unwrapCallback(r) or r
end
local dh = d[hn]
return dh ~= NIL_HANDLER and dh or nil
end
function Object:HasScript(handlerName)
local d = assert(getWidgetData(self, ObjectData), "Invalid object type")
assert(type(handlerName) == 'string', 'Syntax: hasScript = Object:HasScript("handlerName")')
local s2 = d.proto.super2
return findScriptName(d, handlerName) and true or (s2 and s2.HasScript(d.self2, handlerName)) or d.proto.super.HasScript(self, handlerName)
end
local function buildCaselessMap(sourceArray, caseMap, sideMap, sideValue, errorText)
for i=1, sourceArray and #sourceArray or 0 do
local ik = sourceArray[i]
local lo = ik:lower()
assert((caseMap[lo] or ik) == ik, errorText, 4)
sideMap[ik], caseMap[lo] = sideValue, ik
end
end
function AddObjectMethods(isarr, proto)
local api, scripts, psc2 = proto.api, proto.scripts, proto.self2scripts
local op, isa, pin, sc2 = {}, {exUIObject=true}, proto.init or {}, psc2 and {} or nil
proto.init, proto.scripts, proto.self2scripts = pin, nil
for k,v in next, Object do
if api[k] == nil then
api[k] = v
end
end
for i=1,#pin+1 do
if (pin[i] or initObject) == initObject then
pin[i] = initObject
break
end
end
buildCaselessMap(isarr, loObjectType, isa, true, 'divergent object type case')
buildCaselessMap(scripts, loScriptName, op, NIL_HANDLER, 'divergent script name case')
buildCaselessMap(psc2, loScriptName, sc2, 1, 'divergent script2 name case')
objectProto[proto], op.isa, op.ObjectType, op.scripts, op.scripts2 = op, isa, isarr[1], scripts, sc2
return proto
end
end
do -- ObjectGroup
local mcache = {}
local function genMethod(t, k)
if type(k) == "string" and type(t[1][k]) == "function" then
local r = mcache[k]
if r == nil then
r = function(self, ...)
for i=#self,2,-1 do
securecall(self[i][k], self[i], ...)
end
return securecall(self[1][k], self[1], ...)
end
mcache[k] = r
end
return r
end
end
local groupMeta = {__index=genMethod, __metatable=false}
XU:RegisterFactory("ObjectGroup", function(...)
return setmetatable({...}, groupMeta)
end)
end
local hum = {}
function hum:GetImpl()
return assert, getWidgetData, newWidgetData, setWidgetData, AddObjectMethods, CallObjectScript
end
T.exUI, hum.HUM = setmetatable(XU, {__index=hum}), hum