annotate LibKraken/LibKraken.lua @ 60:256585077cdd

missed a function rename
author Nenue
date Sat, 27 Aug 2016 11:01:25 -0400
parents d8bb2629fea8
children 04c23ceaf9e0
rev   line source
Nenue@37 1 --[[
Nenue@37 2 -- KrakynTools
Nenue@37 3 -- AddOn prototyping library.
Nenue@37 4 --
Nenue@37 5 --- Bundles an object into the handler queue, returning the core object, and handlers for debug output and co-routine.
Nenue@37 6 -- Addon name is determined in order of: (string first arg, second arg :GetName(), invoking filename).
Nenue@37 7 -- Once an addon name/object is registered, subsequent calls will return a debugger that reports the file or plugin name.
Nenue@37 8 -- @usage addon, print, wrap = KT.register(name, table) or KT.register(addon) or KT.register(addon, plugin)
Nenue@37 9 -- @param name - name of addon, as found in global varargs
Nenue@37 10 -- @param table - addon table from global varargs
Nenue@37 11 --
Nenue@37 12 -- @param frame - frame object used by addon
Nenue@37 13 -- @param plugin - string name of plugin or an object table to check for lib handlers
Nenue@37 14 --
Nenue@37 15 -- Handlers:
Nenue@37 16 -- :init() run immediately after KT sets itself up
Nenue@37 17 -- :profile("Name-TruncatedRealm") called the first time SavedVars data becomes available
Nenue@37 18 -- :variables() called upon variables being available
Nenue@37 19 -- :event(event, ...) replaces the event callback
Nenue@37 20 -- :ui() called by /ui when activating
Nenue@37 21 --
Nenue@37 22 -- Embedded:
Nenue@37 23 -- NOTES:
Nenue@37 24 -- * `name' is passed as is into CreateFrame, so using nil produce an anonymous frame
Nenue@37 25 -- * `coord' is a 4 or 8 size table unpacked into Texture:SetTexCoords()
Nenue@37 26 --
Nenue@37 27 -- tab = frame:tab(name, tooltip, texture, coord)
Nenue@37 28 -- produces a serial button that changes display tabs
Nenue@37 29 --
Nenue@37 30 -- button = frame:button(name, text, tooltip, onClick)
Nenue@37 31 -- produces a button with OnClick script
Nenue@37 32 --
Nenue@37 33 -- uibutton = frame:uibutton(name, text, tooltip, onClick, texture, coord)
Nenue@37 34 -- produces a header button with desired onClick
Nenue@37 35 --
Nenue@37 36 --
Nenue@37 37 ]]--
Nenue@37 38
Nenue@37 39 local LIBKT_MAJOR, LIBKT_MINOR = "LibKraken", 2
Nenue@37 40 local KT = LibStub:NewLibrary(LIBKT_MAJOR, LIBKT_MINOR)
Nenue@37 41
Nenue@37 42 --GLOBALS: KTErrorFrame, LibKTError, SlashCmdList, SLASH_RL1, SLASH_UI1
Nenue@37 43 local CreateFrame, debugstack, tostring, select = CreateFrame, debugstack, tostring, select
Nenue@59 44 local max, unpack, tinsert, tremove = max, unpack, tinsert, tremove
Nenue@59 45 local ipairs, pairs, xpcall = ipairs, pairs, xpcall
Nenue@59 46 local type, assert = type, assert
Nenue@59 47 local IsLoggedIn = IsLoggedIn
Nenue@37 48 local db
Nenue@37 49
Nenue@59 50 local print = DEVIAN_WORKSPACE and function(...) _G.print('LKT', ...) end or function() end
Nenue@59 51 local noFunc = function() end
Nenue@37 52
Nenue@37 53 KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent)
Nenue@37 54 KT.addons = {}
Nenue@37 55 KT.initStack = {}
Nenue@59 56 local libInitialized = false
Nenue@37 57 local registeredHandles = {}
Nenue@59 58 local initialized = {}
Nenue@59 59 local enabled = {}
Nenue@37 60 local handlers = {}
Nenue@37 61
Nenue@37 62
Nenue@37 63
Nenue@59 64 local debuggers = {}
Nenue@59 65 local pending = {}
Nenue@59 66
Nenue@59 67
Nenue@59 68 local Embed = function (target, template)
Nenue@59 69 for k,v in pairs(template) do
Nenue@59 70 if not target[k] then
Nenue@59 71 target[k] = template[k]
Nenue@37 72 end
Nenue@37 73 end
Nenue@37 74 end
Nenue@37 75
Nenue@59 76 local LibKT_Error = function(msg)
Nenue@37 77 local dstack = debugstack(2)
Nenue@37 78 :gsub("Interface\\AddOns\\",'')
Nenue@37 79 :gsub("<(.-)>", function(a) return '|cFF00FFFF<'.. a ..'>|r' end)
Nenue@37 80
Nenue@37 81
Nenue@37 82
Nenue@37 83 KTErrorFrame.errmsg:SetText(msg)
Nenue@37 84 KTErrorFrame.debugstack:SetText(dstack)
Nenue@37 85 KTErrorFrame:SetHeight(KTErrorFrame.debugstack:GetStringHeight() + KTErrorFrame.errmsg:GetStringHeight() + 12)
Nenue@37 86 KTErrorFrame:Show()
Nenue@37 87 end
Nenue@37 88
Nenue@59 89 local LibKT_OnLoad = function(self)
Nenue@59 90 --- /rl
Nenue@59 91 -- ReloadUI shortcut
Nenue@59 92 SLASH_RL1 = "/rl"
Nenue@59 93 SlashCmdList.RL = function ()
Nenue@59 94 ReloadUI()
Nenue@59 95 end
Nenue@59 96 libInitialized = true
Nenue@59 97 end
Nenue@37 98
Nenue@59 99 local LibKT_OnEvent = function(self, event, arg1)
Nenue@59 100 print(event, arg1)
Nenue@59 101 if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
Nenue@59 102 if not libInitialized then
Nenue@59 103 LibKT_OnLoad(self)
Nenue@37 104 end
Nenue@37 105
Nenue@37 106
Nenue@59 107 -- run any init blocks left in the queue
Nenue@59 108 for i, addon in ipairs(KT.initStack) do
Nenue@59 109 if not initialized[addon] then
Nenue@59 110 print('|cFF0088FF'..tostring(addon)..'|r:init()')
Nenue@59 111 if addon.init then
Nenue@59 112 xpcall(addon.init, LibKT_Error)
Nenue@59 113 end
Nenue@37 114
Nenue@59 115 if addon.event then
Nenue@59 116 addon:SetScript('OnEvent', addon.event)
Nenue@59 117 end
Nenue@37 118
Nenue@59 119 initialized[addon] = true
Nenue@59 120 end
Nenue@37 121
Nenue@59 122 if #addon.modules >= 1 then
Nenue@59 123 for i, module in ipairs(addon.modules) do
Nenue@59 124 if not initialized[module] then
Nenue@59 125 print(i .. ' |cFF0088FF'..tostring(addon)..'|r.|cFF00FFFF'..tostring(module)..'|r:init()')
Nenue@59 126 if module.init then
Nenue@59 127 xpcall(module.init, LibKT_Error)
Nenue@59 128 end
Nenue@37 129
Nenue@59 130 if module.event then
Nenue@59 131 module:SetScript('OnEvent', module.event)
Nenue@59 132 end
Nenue@59 133 initialized[module] = true
Nenue@37 134 end
Nenue@37 135 end
Nenue@37 136 end
Nenue@59 137
Nenue@37 138 end
Nenue@37 139
Nenue@37 140 -- run any variables blocks if player variables are ready
Nenue@59 141 if IsLoggedIn() then
Nenue@59 142
Nenue@59 143 for i, addon in ipairs(KT.initStack) do
Nenue@59 144 print('|cFF88FF00'..tostring(addon)..'|r')
Nenue@59 145 if initialized[addon] then
Nenue@59 146 if not enabled[addon] then
Nenue@59 147 print('|cFF88FF00'..tostring(addon)..'|r:variables()')
Nenue@59 148 if addon.variables then
Nenue@59 149 xpcall(addon.variables, LibKT_Error)
Nenue@59 150 end
Nenue@59 151 enabled[addon] = true
Nenue@59 152 end
Nenue@59 153
Nenue@59 154 if addon.modules and enabled[addon] then
Nenue@59 155 for i, module in ipairs(addon.modules) do
Nenue@59 156 print(i .. ' |cFF88FF00'..tostring(module)..'|r')
Nenue@59 157 if not enabled[module] then
Nenue@59 158 if module.variables then
Nenue@59 159 print(i..' |cFF88FF00'..tostring(addon)..'|r.|cFF00FFFF'.. tostring(module)..'|r:variables()')
Nenue@59 160 xpcall(module.variables, LibKT_Error)
Nenue@59 161 end
Nenue@59 162 enabled[module] = true
Nenue@59 163 end
Nenue@37 164 end
Nenue@37 165 end
Nenue@37 166 end
Nenue@37 167 end
Nenue@37 168 end
Nenue@37 169 end
Nenue@37 170 end
Nenue@37 171
Nenue@59 172 --- GUI bits
Nenue@59 173 local defaultGUIAddon = {}
Nenue@59 174 do
Nenue@59 175 local GetButtonTemplate = function(name, parent, template, onClick)
Nenue@59 176 if _G[name] then
Nenue@59 177 return _G[name]
Nenue@59 178 end
Nenue@37 179
Nenue@59 180 local button = CreateFrame('Button', name, parent, template)
Nenue@59 181 button:RegisterForClicks('AnyUp')
Nenue@59 182 button:SetScript('OnClick', onClick)
Nenue@59 183 return button
Nenue@37 184 end
Nenue@37 185
Nenue@59 186 local SetButtonAnchor = function(self, collector, anchor, growth)
Nenue@59 187 if self:GetID() == 0 then
Nenue@59 188 self:SetID(#collector)
Nenue@59 189 print('registered TabButton #', self:GetID())
Nenue@59 190 end
Nenue@37 191
Nenue@59 192 if self:GetID() == 1 then
Nenue@59 193 self:SetPoint(unpack(anchor))
Nenue@59 194 else
Nenue@59 195 growth[2] = collector[self:GetID()-1]
Nenue@59 196 self:SetPoint(unpack(growth))
Nenue@59 197 end
Nenue@37 198 end
Nenue@37 199
Nenue@59 200 defaultGUIAddon.tab = function(self, name, tooltip, texture, coords)
Nenue@59 201 local button = GetButtonTemplate(name, self, 'KTTabButton', self.SelectTab)
Nenue@59 202 button.icon:SetTexture(texture)
Nenue@59 203 button.tooltip = tooltip
Nenue@59 204 button:SetSize(unpack(self.tabSize))
Nenue@59 205 if coords then
Nenue@59 206 button.icon:SetTexCoord(unpack(coords))
Nenue@59 207 end
Nenue@59 208 SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth)
Nenue@59 209 return button
Nenue@59 210 end
Nenue@59 211
Nenue@59 212 defaultGUIAddon.button = function(self, name, text, tooltip, onClick)
Nenue@59 213 local button = GetButtonTemplate(name, self, 'KTButton', onClick)
Nenue@59 214
Nenue@59 215 button.tooltip = tooltip
Nenue@59 216 button:SetText(text)
Nenue@59 217 button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12))
Nenue@59 218
Nenue@59 219 SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth)
Nenue@59 220 return button
Nenue@59 221 end
Nenue@59 222
Nenue@59 223 defaultGUIAddon.uibutton = function(self, name, text, tooltip, onClick, texture, coords)
Nenue@59 224 local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick)
Nenue@59 225
Nenue@59 226 button.tooltip = tooltip
Nenue@59 227 button:SetText(text)
Nenue@59 228
Nenue@59 229 if self.UIPanelIcon then
Nenue@59 230 local w, h, anchor, x, y = unpack(self.UIPanelIcon)
Nenue@59 231 button.icon:SetTexture(texture)
Nenue@59 232 button.icon:SetSize(w, h)
Nenue@59 233 button.icon:ClearAllPoints()
Nenue@59 234 button.icon:SetPoint(anchor, button, anchor, x, y)
Nenue@59 235 end
Nenue@59 236
Nenue@59 237 if not self.UIPanelSize then
Nenue@59 238 button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5)
Nenue@59 239 else
Nenue@59 240 button:SetSize(unpack(self.UIPanelSize))
Nenue@59 241 end
Nenue@59 242 if coords then
Nenue@59 243 button.icon:SetTexCoord(unpack(coords))
Nenue@59 244 end
Nenue@59 245 SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth)
Nenue@59 246 return button
Nenue@37 247 end
Nenue@37 248 end
Nenue@37 249
Nenue@37 250
Nenue@59 251 local defaultAddon = {}
Nenue@59 252 do
Nenue@59 253 defaultAddon.print = function(module, ...)
Nenue@59 254 local msg = '|cFF00FFFF'..module:GetName()..'|r:'
Nenue@59 255 for i = 1, select('#', ...) do
Nenue@59 256 msg = msg .. ' ' .. tostring(select(i, ...))
Nenue@59 257 end
Nenue@59 258 DEFAULT_CHAT_FRAME:AddMessage(msg)
Nenue@37 259 end
Nenue@37 260
Nenue@37 261
Nenue@37 262 local tickerQueue = {}
Nenue@37 263 local ticker
Nenue@59 264 defaultAddon.tick = function()
Nenue@37 265
Nenue@37 266 if #tickerQueue == 0 then
Nenue@37 267 ticker:Cancel()
Nenue@37 268 ticker = nil
Nenue@37 269 end
Nenue@37 270 local func = tremove(tickerQueue, 1)
Nenue@37 271 if func then
Nenue@37 272 --print('#', #tickerQueue)
Nenue@37 273 func()
Nenue@37 274 end
Nenue@37 275 end
Nenue@37 276
Nenue@59 277 defaultAddon.next = function(f)
Nenue@37 278 if not ticker then
Nenue@37 279 --print('create ticker')
Nenue@59 280 ticker = C_Timer.NewTicker(.001, defaultAddon.tick)
Nenue@37 281 end
Nenue@37 282 tinsert(tickerQueue, f)
Nenue@37 283
Nenue@37 284 return #tickerQueue
Nenue@59 285 end
Nenue@59 286
Nenue@59 287
Nenue@59 288 --- default OnEvent
Nenue@59 289
Nenue@59 290 local processing = false
Nenue@59 291 local isHandled = false
Nenue@59 292 local nodebug = false
Nenue@59 293 defaultAddon.event = function (addon, event, ...)
Nenue@59 294 if processing then
Nenue@59 295 local args = {...}
Nenue@60 296 C_Timer.After(0, function() LibKT_OnEvent(addon, event, unpack(args)) end)
Nenue@59 297 return
Nenue@59 298 else
Nenue@59 299
Nenue@59 300 end
Nenue@59 301 --- reset state
Nenue@59 302 processing = true
Nenue@59 303 isHandled = false
Nenue@59 304 nodebug = false
Nenue@59 305
Nenue@59 306
Nenue@59 307 if addon.event then
Nenue@59 308 nodebug = addon.event(addon, event, ...)
Nenue@59 309 elseif addon[event] then
Nenue@59 310 nodebug = addon[event](addon, event, ...) or nodebug
Nenue@59 311 addon.missed = 0
Nenue@59 312 addon.handled = addon.handled + 1
Nenue@59 313 isHandled = true
Nenue@59 314 else
Nenue@59 315 addon.firstEvent = false
Nenue@59 316 addon.unhandled = addon.unhandled + 1
Nenue@59 317 addon.missed = addon.missed + 1
Nenue@59 318 end
Nenue@59 319
Nenue@59 320 if addon.modules then
Nenue@59 321 for i, module in ipairs(addon.modules) do
Nenue@59 322 --print(i, module, event)
Nenue@59 323 if module.event then
Nenue@59 324 module.event(module, event, ...)
Nenue@59 325 elseif module[event] then
Nenue@59 326 nodebug = module[event](addon, event, ...) or nodebug
Nenue@59 327 addon.missed = 0
Nenue@59 328 addon.handled = addon.handled + 1
Nenue@59 329 isHandled = true
Nenue@59 330 else
Nenue@59 331 addon.firstEvent = false
Nenue@59 332 addon.unhandled = addon.unhandled + 1
Nenue@59 333 addon.missed = addon.missed + 1
Nenue@59 334 end
Nenue@59 335 end
Nenue@59 336 end
Nenue@59 337 --if nodebug then
Nenue@59 338 processing = false
Nenue@59 339 return
Nenue@59 340 --else
Nenue@59 341 -- KT.UpdateEventStatus(addon, event, ...)
Nenue@59 342 -- processing = false
Nenue@59 343 --end
Nenue@37 344
Nenue@37 345 end
Nenue@59 346 defaultAddon.wrap = function(addon, module, name)
Nenue@59 347 local moduleName = name or tostring(module)
Nenue@59 348 print(addon, module)
Nenue@59 349 print('|cFF0088FF'..tostring(addon)..'|r:wrap(|cFF00FFFF'.. moduleName .. '|r|cFFFFFF00)|r')
Nenue@59 350
Nenue@59 351 addon.modules = addon.modules or {}
Nenue@59 352 tinsert(addon.modules, module)
Nenue@59 353
Nenue@59 354
Nenue@59 355 if (module.DEVIAN_PNAME and DEVIAN_PNAME == module.DEVIAN_PNAME) or ((not module.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
Nenue@59 356 debuggers[module] = function(...) _G.print(moduleName, ...) end
Nenue@59 357 else
Nenue@59 358 debuggers[module] = noFunc
Nenue@59 359 end
Nenue@59 360 return debuggers[module]
Nenue@59 361 end
Nenue@59 362
Nenue@59 363 defaultAddon.GetName = function(self) return tostring(self) end
Nenue@59 364 end
Nenue@59 365
Nenue@59 366 --- Frame registration
Nenue@59 367 KT.register = function(addon, arg, noGUI)
Nenue@59 368 --print('register(', addon, arg, ')')
Nenue@59 369 local name, handler
Nenue@59 370 if type(addon) == 'string' and type(arg) == 'table' then
Nenue@59 371 name = addon
Nenue@59 372 -- it's a string, i.e. file vararg was passed
Nenue@59 373 if _G[addon] then
Nenue@59 374 -- check if it's the name of a frame and use that
Nenue@59 375 handler = _G[addon]
Nenue@59 376 else
Nenue@59 377 -- re-arrange
Nenue@59 378 handler = arg
Nenue@59 379 end
Nenue@59 380 else
Nenue@59 381 handler = addon
Nenue@59 382 assert(type(handler) == 'table', 'Usage: KT.register(name, table) or KT.register(table, plugin)')
Nenue@59 383 end
Nenue@59 384
Nenue@59 385
Nenue@59 386 local printName
Nenue@59 387 local isModule
Nenue@59 388
Nenue@59 389 local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua")
Nenue@59 390 if registeredHandles[handler] then
Nenue@59 391 -- addon is already register; treat second argument as plugin target
Nenue@59 392 isModule = true
Nenue@59 393 if type(arg) == 'table' then
Nenue@59 394 local mt = getmetatable(arg)
Nenue@59 395 setmetatable(arg, {__index = mt.__index, __tostring = function() return scriptName end})
Nenue@59 396 local debugger = handler:wrap(arg)
Nenue@59 397 return handler, debugger
Nenue@59 398 else
Nenue@59 399 print(' + "|cFF00FFFF'..scriptName..'|r"')
Nenue@59 400 end
Nenue@59 401 else
Nenue@59 402 -- new addon
Nenue@59 403 --local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua")
Nenue@59 404 local mt = getmetatable(handler)
Nenue@59 405 local nmt = {__index = mt.__index, __tostring = function() return scriptName end }
Nenue@59 406 handler = setmetatable(handler, nmt)
Nenue@59 407 Embed(handler, defaultAddon)
Nenue@59 408 name = tostring(handler)
Nenue@59 409 registeredHandles[handler] = name
Nenue@59 410 if handler.SetScript then
Nenue@59 411 handler:SetScript('OnEvent', handler.event)
Nenue@59 412 end
Nenue@59 413 handler.unhandled = 0
Nenue@59 414 handler.missed = 0
Nenue@59 415 handler.handled = 0
Nenue@59 416 handler.firstEvent = false
Nenue@59 417 handler.modules = {}
Nenue@59 418 tinsert(KT.initStack, handler)
Nenue@59 419
Nenue@59 420 if not noGUI then
Nenue@59 421 handler.UIPanelAnchor = {'TOPLEFT', handler, 'TOPLEFT', 12, -12 }
Nenue@59 422 handler.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
Nenue@59 423 Embed(handler, defaultGUIAddon)
Nenue@59 424 end
Nenue@59 425
Nenue@59 426 print('|cFF0088FF'..tostring(addon)..'|r')
Nenue@59 427 end
Nenue@59 428
Nenue@59 429 local debugFunc = noFunc
Nenue@59 430 --@debug@
Nenue@59 431 local debugID = isModule and name or handler
Nenue@59 432 if (handler.DEVIAN_PNAME and DEVIAN_PNAME == handler.DEVIAN_PNAME) or ((not handler.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
Nenue@59 433 debuggers[debugID] = debuggers[debugID] or function(...) _G.print(name, ...) end
Nenue@59 434 debugFunc = debuggers[debugID]
Nenue@59 435 end
Nenue@59 436 --@end-debug@
Nenue@59 437 return handler, debugFunc
Nenue@37 438 end
Nenue@37 439
Nenue@37 440 KT.handler:RegisterEvent('ADDON_LOADED')
Nenue@37 441 KT.handler:RegisterEvent('PLAYER_LOGIN')
Nenue@59 442 KT.handler:SetScript('OnEvent', LibKT_OnEvent)