Mercurial > wow > skeletonkey
view LibKraken/LibKraken.lua @ 62:04c23ceaf9e0
- fix taint caused by loading in combat
author | Nenue |
---|---|
date | Mon, 05 Sep 2016 14:56:38 -0400 |
parents | 256585077cdd |
children |
line wrap: on
line source
--[[ -- KrakynTools -- AddOn prototyping library. -- --- Bundles an object into the handler queue, returning the core object, and handlers for debug output and co-routine. -- Addon name is determined in order of: (string first arg, second arg :GetName(), invoking filename). -- Once an addon name/object is registered, subsequent calls will return a debugger that reports the file or plugin name. -- @usage addon, print, wrap = KT.register(name, table) or KT.register(addon) or KT.register(addon, plugin) -- @param name - name of addon, as found in global varargs -- @param table - addon table from global varargs -- -- @param frame - frame object used by addon -- @param plugin - string name of plugin or an object table to check for lib handlers -- -- Handlers: -- :init() run immediately after KT sets itself up -- :profile("Name-TruncatedRealm") called the first time SavedVars data becomes available -- :variables() called upon variables being available -- :event(event, ...) replaces the event callback -- :ui() called by /ui when activating -- -- Embedded: -- NOTES: -- * `name' is passed as is into CreateFrame, so using nil produce an anonymous frame -- * `coord' is a 4 or 8 size table unpacked into Texture:SetTexCoords() -- -- tab = frame:tab(name, tooltip, texture, coord) -- produces a serial button that changes display tabs -- -- button = frame:button(name, text, tooltip, onClick) -- produces a button with OnClick script -- -- uibutton = frame:uibutton(name, text, tooltip, onClick, texture, coord) -- produces a header button with desired onClick -- -- ]]-- local LIBKT_MAJOR, LIBKT_MINOR = "LibKraken", 2 local KT = LibStub:NewLibrary(LIBKT_MAJOR, LIBKT_MINOR) --GLOBALS: KTErrorFrame, LibKTError, SlashCmdList, SLASH_RL1, SLASH_UI1 local CreateFrame, debugstack, tostring, select = CreateFrame, debugstack, tostring, select local max, unpack, tinsert, tremove = max, unpack, tinsert, tremove local ipairs, pairs, xpcall = ipairs, pairs, xpcall local type, assert = type, assert local IsLoggedIn = IsLoggedIn local db local print = DEVIAN_WORKSPACE and function(...) _G.print('LKT', ...) end or function() end local noFunc = function() end KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent) KT.addons = {} KT.initStack = {} local libInitialized = false local registeredHandles = {} local initialized = {} local enabled = {} local handlers = {} local debuggers = {} local pending = {} local Embed = function (target, template) for k,v in pairs(template) do if not target[k] then print(tostring(target),'<-', k) target[k] = template[k] end end end local LibKT_Error = function(msg) local dstack = debugstack(2) :gsub("Interface\\AddOns\\",'') :gsub("<(.-)>", function(a) return '|cFF00FFFF<'.. a ..'>|r' end) KTErrorFrame.errmsg:SetText(msg) KTErrorFrame.debugstack:SetText(dstack) KTErrorFrame:SetHeight(KTErrorFrame.debugstack:GetStringHeight() + KTErrorFrame.errmsg:GetStringHeight() + 12) KTErrorFrame:Show() end local LibKT_OnLoad = function(self) --- /rl -- ReloadUI shortcut SLASH_RL1 = "/rl" SlashCmdList.RL = function () ReloadUI() end libInitialized = true end local LibKT_OnEvent = function(self, event, arg1) print(event, arg1) if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then if not libInitialized then LibKT_OnLoad(self) end -- run any init blocks left in the queue for i, addon in ipairs(KT.initStack) do if not initialized[addon] then print('|cFF0088FF'..tostring(addon)..'|r:init()') if addon.init then xpcall(addon.init, LibKT_Error) end if addon.event then addon:SetScript('OnEvent', addon.event) end initialized[addon] = true end if #addon.modules >= 1 then for i, module in ipairs(addon.modules) do if not initialized[module] then print(i .. ' |cFF0088FF'..tostring(addon)..'|r.|cFF00FFFF'..tostring(module)..'|r:init()') if module.init then xpcall(module.init, LibKT_Error) end if module.event then module:SetScript('OnEvent', module.event) end initialized[module] = true end end end end -- run any variables blocks if player variables are ready if IsLoggedIn() then for i, addon in ipairs(KT.initStack) do print('|cFF88FF00'..tostring(addon)..'|r') if initialized[addon] then if not enabled[addon] then print('|cFF88FF00'..tostring(addon)..'|r:variables()') if addon.variables then xpcall(addon.variables, LibKT_Error) end enabled[addon] = true end if addon.modules and enabled[addon] then for i, module in ipairs(addon.modules) do print(i .. ' |cFF88FF00'..tostring(module)..'|r') if not enabled[module] then if module.variables then print(i..' |cFF88FF00'..tostring(addon)..'|r.|cFF00FFFF'.. tostring(module)..'|r:variables()') xpcall(module.variables, LibKT_Error) end enabled[module] = true end end end end end end end end --- GUI bits local defaultGUIAddon = {} do local GetButtonTemplate = function(name, parent, template, onClick) if _G[name] then return _G[name] end local button = CreateFrame('Button', name, parent, template) button:RegisterForClicks('AnyUp') button:SetScript('OnClick', onClick) return button end local SetButtonAnchor = function(self, collector, anchor, growth) if self:GetID() == 0 then self:SetID(#collector) print('registered TabButton #', self:GetID()) end if self:GetID() == 1 then self:SetPoint(unpack(anchor)) else growth[2] = collector[self:GetID()-1] self:SetPoint(unpack(growth)) end end defaultGUIAddon.tab = function(self, name, tooltip, texture, coords) local button = GetButtonTemplate(name, self, 'KTTabButton', self.SelectTab) button.icon:SetTexture(texture) button.tooltip = tooltip button:SetSize(unpack(self.tabSize)) if coords then button.icon:SetTexCoord(unpack(coords)) end SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth) return button end defaultGUIAddon.button = function(self, name, text, tooltip, onClick) local button = GetButtonTemplate(name, self, 'KTButton', onClick) button.tooltip = tooltip button:SetText(text) button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12)) SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth) return button end defaultGUIAddon.uibutton = function(self, name, text, tooltip, onClick, texture, coords) local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick) button.tooltip = tooltip button:SetText(text) if self.UIPanelIcon then local w, h, anchor, x, y = unpack(self.UIPanelIcon) button.icon:SetTexture(texture) button.icon:SetSize(w, h) button.icon:ClearAllPoints() button.icon:SetPoint(anchor, button, anchor, x, y) end if not self.UIPanelSize then button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5) else button:SetSize(unpack(self.UIPanelSize)) end if coords then button.icon:SetTexCoord(unpack(coords)) end SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth) return button end end local defaultAddon = {} do defaultAddon.print = function(module, ...) local msg = '|cFF00FFFF'..module:GetName()..'|r:' for i = 1, select('#', ...) do msg = msg .. ' ' .. tostring(select(i, ...)) end DEFAULT_CHAT_FRAME:AddMessage(msg) end local tickerQueue = {} local ticker defaultAddon.tick = function() if #tickerQueue == 0 then ticker:Cancel() ticker = nil end local func = tremove(tickerQueue, 1) if func then --print('#', #tickerQueue) func() end end defaultAddon.next = function(f) if not ticker then --print('create ticker') ticker = C_Timer.NewTicker(.001, defaultAddon.tick) end tinsert(tickerQueue, f) return #tickerQueue end --- default OnEvent local EmbedEventScript = function (handler) if not handler.SetScript then return end print('|cFF00FF88binding', handler:GetName()) -- enclose so .event can be overridden post-register handler:SetScript('OnEvent', function(self, event,...) print('|cFFFF4400' .. tostring(self) .. '|r', event, ...) print(self.event) print(self[event]) self.event(self, event, ...) end) handler.unhandled = 0 handler.missed = 0 handler.handled = 0 handler.firstEvent = false end local isHandled = false local nodebug = false defaultAddon.event = function (self, event, ...) print('what') --- reset state if self[event] then print('|cFFFFFF00'.. tostring(self) .. '|r', event) print(self.debug) self.debug(event, ...) self[event](self, event, ...) self.missed = 0 self.handled = self.handled + 1 isHandled = true else self.firstEvent = false self.unhandled = self.unhandled + 1 self.missed = self.missed + 1 end return end defaultAddon.wrap = function(addon, module, name) local moduleName = name or tostring(module) print(addon, module) print('|cFF0088FF'..tostring(addon)..'|r:wrap(|cFF00FFFF'.. moduleName .. '|r|cFFFFFF00)|r') addon.modules = addon.modules or {} tinsert(addon.modules, module) if (module.DEVIAN_PNAME and DEVIAN_PNAME == module.DEVIAN_PNAME) or ((not module.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then debuggers[module] = function(...) _G.print(moduleName, ...) end else debuggers[module] = noFunc end module.debug = debuggers[module] Embed(module, defaultAddon) EmbedEventScript(module) return debuggers[module] end defaultAddon.GetName = function(self) return tostring(self) end end --- Frame registration KT.register = function(addon, arg, noGUI) --print('register(', addon, arg, ')') local name, handler if type(addon) == 'string' and type(arg) == 'table' then name = addon -- it's a string, i.e. file vararg was passed if _G[addon] then -- check if it's the name of a frame and use that handler = _G[addon] else -- re-arrange handler = arg end else handler = addon assert(type(handler) == 'table', 'Usage: KT.register(name, table) or KT.register(table, plugin)') end local printName local isModule local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua") if registeredHandles[handler] then -- addon is already register; treat second argument as plugin target isModule = true if type(arg) == 'table' then local mt = getmetatable(arg) setmetatable(arg, {__index = mt.__index, __tostring = function() return scriptName end}) local debugger = handler:wrap(arg) return handler, debugger else print(' + "|cFF00FFFF'..scriptName..'|r"') end else -- new addon --local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua") local mt = getmetatable(handler) local nmt = {__index = mt.__index, __tostring = function() return scriptName end } handler = setmetatable(handler, nmt) Embed(handler, defaultAddon) name = tostring(handler) registeredHandles[handler] = name if handler.SetScript then handler:SetScript('OnEvent', function(...) handler.event(...) end) handler.unhandled = 0 handler.missed = 0 handler.handled = 0 handler.firstEvent = false end handler.modules = {} tinsert(KT.initStack, handler) if not noGUI then handler.UIPanelAnchor = {'TOPLEFT', handler, 'TOPLEFT', 12, -12 } handler.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0} Embed(handler, defaultGUIAddon) end print('|cFF0088FF'..tostring(addon)..'|r') end local debugFunc = noFunc --@debug@ local debugID = isModule and name or handler if (handler.DEVIAN_PNAME and DEVIAN_PNAME == handler.DEVIAN_PNAME) or ((not handler.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then debuggers[debugID] = debuggers[debugID] or function(...) _G.print(name, ...) end debugFunc = debuggers[debugID] end handler.debug = debugFunc --@end-debug@ return handler, debugFunc end KT.handler:RegisterEvent('ADDON_LOADED') KT.handler:RegisterEvent('PLAYER_LOGIN') KT.handler:SetScript('OnEvent', LibKT_OnEvent)