annotate LibKraken/LibKraken.lua @ 37:ae012c9d8dc5

more logjam cleanup
author Nenue
date Tue, 16 Aug 2016 10:28:37 -0400
parents
children 9eebce04e69b
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@37 44 local max, unpack, tinsert = max, unpack, tinsert
Nenue@37 45 local ipairs, xpcall, next, safecall = ipairs, xpcall, next, safecall
Nenue@37 46 local UI_TOGGLE = false
Nenue@37 47 local db
Nenue@37 48
Nenue@37 49
Nenue@37 50 KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent)
Nenue@37 51 KT.addons = {}
Nenue@37 52 KT.initStack = {}
Nenue@37 53 KT.varsStack = {}
Nenue@37 54 local print = DEVIAN_WORKSPACE and function(...) _G.print('LKT', ...) end or function() end
Nenue@37 55 local registeredHandles = {}
Nenue@37 56 local handlers = {}
Nenue@37 57
Nenue@37 58
Nenue@37 59 --- /rl
Nenue@37 60 -- ReloadUI shortcut
Nenue@37 61 SLASH_RL1 = "/rl"
Nenue@37 62 SlashCmdList.RL = function ()
Nenue@37 63 ReloadUI()
Nenue@37 64 end
Nenue@37 65
Nenue@37 66 --- /ui
Nenue@37 67 -- Run any addon:ui() methods
Nenue@37 68 SLASH_UI1 = "/ui"
Nenue@37 69 SlashCmdList.UI = function ()
Nenue@37 70 if UI_TOGGLE then
Nenue@37 71 UI_TOGGLE = false
Nenue@37 72 else
Nenue@37 73 UI_TOGGLE = true
Nenue@37 74 end
Nenue@37 75 for i, frame in pairs(KT.frames) do
Nenue@37 76 if UI_TOGGLE then
Nenue@37 77 if frame.close then
Nenue@37 78 frame.close()
Nenue@37 79 else
Nenue@37 80 frame:Hide()
Nenue@37 81 end
Nenue@37 82 else
Nenue@37 83 if frame.ui then
Nenue@37 84 frame.ui()
Nenue@37 85 end
Nenue@37 86 frame:Show()
Nenue@37 87 end
Nenue@37 88 end
Nenue@37 89 end
Nenue@37 90
Nenue@37 91 LibKTError = function(msg)
Nenue@37 92 local dstack = debugstack(2)
Nenue@37 93 :gsub("Interface\\AddOns\\",'')
Nenue@37 94 :gsub("<(.-)>", function(a) return '|cFF00FFFF<'.. a ..'>|r' end)
Nenue@37 95
Nenue@37 96
Nenue@37 97
Nenue@37 98 KTErrorFrame.errmsg:SetText(msg)
Nenue@37 99 KTErrorFrame.debugstack:SetText(dstack)
Nenue@37 100 KTErrorFrame:SetHeight(KTErrorFrame.debugstack:GetStringHeight() + KTErrorFrame.errmsg:GetStringHeight() + 12)
Nenue@37 101 KTErrorFrame:Show()
Nenue@37 102 end
Nenue@37 103
Nenue@37 104 local debuggers = {}
Nenue@37 105 local pending = {}
Nenue@37 106 local processing = false
Nenue@37 107 local isHandled = false
Nenue@37 108 local nodebug = false
Nenue@37 109 function KT.OnEvent (addon, event, ...)
Nenue@37 110 if processing then
Nenue@37 111 local args = {...}
Nenue@37 112 C_Timer.After(0, function() KT.OnEvent(addon, event, unpack(args)) end)
Nenue@37 113 return
Nenue@37 114 else
Nenue@37 115
Nenue@37 116 end
Nenue@37 117 --- reset state
Nenue@37 118 processing = true
Nenue@37 119 isHandled = false
Nenue@37 120 nodebug = false
Nenue@37 121
Nenue@37 122
Nenue@37 123 if addon.event then
Nenue@37 124 nodebug = addon.event(addon, event, ...)
Nenue@37 125 end
Nenue@37 126
Nenue@37 127 if addon[event] then
Nenue@37 128 nodebug = addon[event](addon, event, ...) or nodebug
Nenue@37 129 addon.missed = 0
Nenue@37 130 addon.handled = addon.handled + 1
Nenue@37 131 isHandled = true
Nenue@37 132 else
Nenue@37 133 addon.firstEvent = false
Nenue@37 134 addon.unhandled = addon.unhandled + 1
Nenue@37 135 addon.missed = addon.missed + 1
Nenue@37 136 end
Nenue@37 137
Nenue@37 138 for i, module in ipairs(addon.modules) do
Nenue@37 139 --print(i, module)
Nenue@37 140 if module[event] then
Nenue@37 141 nodebug = module[event](addon, event, ...) or nodebug
Nenue@37 142 addon.missed = 0
Nenue@37 143 addon.handled = addon.handled + 1
Nenue@37 144 isHandled = true
Nenue@37 145 else
Nenue@37 146 addon.firstEvent = false
Nenue@37 147 addon.unhandled = addon.unhandled + 1
Nenue@37 148 addon.missed = addon.missed + 1
Nenue@37 149 end
Nenue@37 150
Nenue@37 151 end
Nenue@37 152 --if nodebug then
Nenue@37 153 processing = false
Nenue@37 154 return
Nenue@37 155 --else
Nenue@37 156 -- KT.UpdateEventStatus(addon, event, ...)
Nenue@37 157 -- processing = false
Nenue@37 158 --end
Nenue@37 159
Nenue@37 160 end
Nenue@37 161
Nenue@37 162 KT.UpdateEventStatus = function(addon, event, ...)
Nenue@37 163 if (addon.DEVIAN_PNAME and addon.DEVIAN_PNAME == DEVIAN_PNAME) or ((not addon.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
Nenue@37 164 print(addon:GetName(), event, ...)
Nenue@37 165 end
Nenue@37 166
Nenue@37 167 -- debug outputs
Nenue@37 168 if addon.status then
Nenue@37 169 addon.status:SetText(event .. '\n|cFF00FF00' .. addon.handled .. '|r |cFFFF8800' .. addon.missed .. '|r |cFFFF4400' .. addon.unhandled .. '|r')
Nenue@37 170 if isHandled then
Nenue@37 171 addon.status:SetTextColor(0,1,0)
Nenue@37 172 if addon.log then
Nenue@37 173 local logtext = event
Nenue@37 174 for i = 1, select('#',...) do
Nenue@37 175 logtext = logtext .. '\n' .. i .. ':' .. tostring(select(i,...))
Nenue@37 176 end
Nenue@37 177 addon.log:SetText('|cFFFFFF00last|r\n' .. logtext)
Nenue@37 178 local newWidth = addon.log:GetStringWidth()
Nenue@37 179
Nenue@37 180 if addon.logfirst then
Nenue@37 181 if not addon.firstEvent then
Nenue@37 182 addon.firstEvent = event
Nenue@37 183 addon.logfirst:SetText('|cFF00FF88first|r\n' .. logtext)
Nenue@37 184 end
Nenue@37 185
Nenue@37 186 newWidth = newWidth + addon.logfirst:GetStringWidth()
Nenue@37 187 end
Nenue@37 188 if addon.logdiff then
Nenue@37 189 if not event ~= addon.firstEvent then
Nenue@37 190 addon.firstEvent = event
Nenue@37 191 addon.logdiff:SetText('|cFF0088FFdiff|r\n' .. logtext)
Nenue@37 192 end
Nenue@37 193 newWidth = newWidth + addon.logdiff:GetStringWidth()
Nenue@37 194 end
Nenue@37 195 --addon:SetWidth(newWidth)
Nenue@37 196 end
Nenue@37 197 else
Nenue@37 198 addon.status:SetTextColor(1,0,0)
Nenue@37 199 end
Nenue@37 200 end
Nenue@37 201 end
Nenue@37 202
Nenue@37 203 KT.map = function(addon, addonTable)
Nenue@37 204 setmetatable(addonTable, {
Nenue@37 205 __index = function(_,k)
Nenue@37 206 return addon[k]
Nenue@37 207 end,
Nenue@37 208 __newindex = function(_, k, v)
Nenue@37 209 addon[k] = v
Nenue@37 210 end
Nenue@37 211 })
Nenue@37 212 end
Nenue@37 213
Nenue@37 214 local emptyFunc = function() end
Nenue@37 215
Nenue@37 216 KT.register = function(addon, arg, noGUI)
Nenue@37 217
Nenue@37 218 local name, handler
Nenue@37 219 if type(addon) == 'string' and type(arg) == 'table' then
Nenue@37 220 -- it's a string, i.e. file vararg was passed
Nenue@37 221 if _G[addon] then
Nenue@37 222 -- check if it's the name of a frame and use that
Nenue@37 223 handler = _G[addon]
Nenue@37 224 else
Nenue@37 225 -- re-arrange
Nenue@37 226 name = addon
Nenue@37 227 handler = arg
Nenue@37 228 end
Nenue@37 229 else
Nenue@37 230 handler = addon
Nenue@37 231 assert(type(handler) == 'table', 'Unable to parse ('..tostring(type(addon))..', '..tostring(type(arg))..')')
Nenue@37 232 end
Nenue@37 233
Nenue@37 234
Nenue@37 235 local loadedName
Nenue@37 236 local printName
Nenue@37 237 local isModule
Nenue@37 238
Nenue@37 239 -- if exists, then second argument is probably a module name
Nenue@37 240 if registeredHandles[handler] then
Nenue@37 241 if type(arg) == 'table' then
Nenue@37 242 tinsert(handler.modules, arg)
Nenue@37 243 else
Nenue@37 244 name = arg or debugstack(2,1,0):match(".+\\(%S+)%.lua")
Nenue@37 245 end
Nenue@37 246 isModule = true
Nenue@37 247
Nenue@37 248 if not name then
Nenue@37 249 name = debugstack(2,1,0):match(".+\\(%S+)%.lua")
Nenue@37 250 end
Nenue@37 251 local handlerName = handler:GetName()
Nenue@37 252 loadedName = '|cFF00FF88'.. tostring(handlerName) .. '|r:|cFFFFFF00'.. tostring(name)
Nenue@37 253 else
Nenue@37 254 if handler.GetName then
Nenue@37 255 name = handler:GetName()
Nenue@37 256 elseif not name then
Nenue@37 257 name = debugstack(2,1,0):match(".+\\(%S+)%.lua")
Nenue@37 258 end
Nenue@37 259
Nenue@37 260 loadedName = '|cFF00FFFF'.. tostring(name) .. '|r'
Nenue@37 261
Nenue@37 262 registeredHandles[handler] = name
Nenue@37 263 if handler.SetScript then
Nenue@37 264 handler:SetScript('OnEvent', KT.OnEvent)
Nenue@37 265 end
Nenue@37 266 handler.unhandled = 0
Nenue@37 267 handler.missed = 0
Nenue@37 268 handler.handled = 0
Nenue@37 269 handler.firstEvent = false
Nenue@37 270 handler.modules = {}
Nenue@37 271 tinsert(KT.initStack, handler)
Nenue@37 272 tinsert(KT.varsStack, handler)
Nenue@37 273
Nenue@37 274 if handler.GetName and (not noGUI) then
Nenue@37 275 handler.UIPanelAnchor = {'TOPLEFT', handler, 'TOPLEFT', 12, -12 }
Nenue@37 276 handler.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
Nenue@37 277 handler.button = KT.button
Nenue@37 278 handler.uibutton = KT.uibutton
Nenue@37 279 handler.tab = KT.tab
Nenue@37 280 handler.print = KT.print
Nenue@37 281 end
Nenue@37 282 end
Nenue@37 283
Nenue@37 284 local debugID = isModule and name or handler
Nenue@37 285 local debugFunc = emptyFunc
Nenue@37 286 if (handler.DEVIAN_PNAME and DEVIAN_PNAME == handler.DEVIAN_PNAME) or ((not handler.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
Nenue@37 287 debuggers[debugID] = debuggers[debugID] or function(...) _G.print(name, ...) end
Nenue@37 288 debugFunc = debuggers[debugID]
Nenue@37 289 end
Nenue@37 290
Nenue@37 291 print(loadedName)
Nenue@37 292
Nenue@37 293 return handler, debugFunc, KT.wrap
Nenue@37 294 end
Nenue@37 295
Nenue@37 296
Nenue@37 297
Nenue@37 298 local onEvent = function(self, event, arg1)
Nenue@37 299 if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
Nenue@37 300 -- run any init blocks left in the queue
Nenue@37 301 while #KT.initStack >= 1 do
Nenue@37 302 local addon = tremove(KT.initStack, 1)
Nenue@37 303 print('KT', addon:GetName(), 'init')
Nenue@37 304 if addon.init then
Nenue@37 305 xpcall(addon.init, LibKTError)
Nenue@37 306 for i, module in ipairs(addon.modules) do
Nenue@37 307 if module.init then
Nenue@37 308 xpcall(module.init, LibKTError)
Nenue@37 309 end
Nenue@37 310 end
Nenue@37 311 end
Nenue@37 312 end
Nenue@37 313
Nenue@37 314 -- run any variables blocks if player variables are ready
Nenue@37 315 if IsLoggedIn() and #KT.varsStack >= 1 then
Nenue@37 316 while #KT.varsStack >= 1 do
Nenue@37 317 local addon = tremove(KT.varsStack, 1)
Nenue@37 318 print(addon:GetName())
Nenue@37 319 if addon.variables then
Nenue@37 320 xpcall(addon.variables, LibKTError)
Nenue@37 321 for i, module in ipairs(addon.modules) do
Nenue@37 322 if module.variables then
Nenue@37 323 xpcall(module.variables, LibKTError)
Nenue@37 324 end
Nenue@37 325 end
Nenue@37 326 end
Nenue@37 327 end
Nenue@37 328 end
Nenue@37 329 end
Nenue@37 330 end
Nenue@37 331
Nenue@37 332 KT.print = function(module, ...)
Nenue@37 333 local msg = '|cFF00FFFF'..module:GetName()..'|r:'
Nenue@37 334 for i = 1, select('#', ...) do
Nenue@37 335 msg = msg .. ' ' .. tostring(select(i, ...))
Nenue@37 336 end
Nenue@37 337 DEFAULT_CHAT_FRAME:AddMessage(msg)
Nenue@37 338 end
Nenue@37 339
Nenue@37 340 --- Button generators
Nenue@37 341
Nenue@37 342 local GetButtonTemplate = function(name, parent, template, onClick)
Nenue@37 343 if _G[name] then
Nenue@37 344 return _G[name]
Nenue@37 345 end
Nenue@37 346
Nenue@37 347 local button = CreateFrame('Button', name, parent, template)
Nenue@37 348 button:RegisterForClicks('AnyUp')
Nenue@37 349 button:SetScript('OnClick', onClick)
Nenue@37 350 return button
Nenue@37 351 end
Nenue@37 352
Nenue@37 353 local SetButtonAnchor = function(self, collector, anchor, growth)
Nenue@37 354 if self:GetID() == 0 then
Nenue@37 355 self:SetID(#collector)
Nenue@37 356 print('registered TabButton #', self:GetID())
Nenue@37 357 end
Nenue@37 358
Nenue@37 359 if self:GetID() == 1 then
Nenue@37 360 self:SetPoint(unpack(anchor))
Nenue@37 361 else
Nenue@37 362 growth[2] = collector[self:GetID()-1]
Nenue@37 363 self:SetPoint(unpack(growth))
Nenue@37 364 end
Nenue@37 365 end
Nenue@37 366
Nenue@37 367 KT.tab = function(self, name, tooltip, texture, coords)
Nenue@37 368 local button = GetButtonTemplate(name, self, 'KTTabButton', self.SelectTab)
Nenue@37 369 button.icon:SetTexture(texture)
Nenue@37 370 button.tooltip = tooltip
Nenue@37 371 button:SetSize(unpack(self.tabSize))
Nenue@37 372 if coords then
Nenue@37 373 button.icon:SetTexCoord(unpack(coords))
Nenue@37 374 end
Nenue@37 375 SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth)
Nenue@37 376 return button
Nenue@37 377 end
Nenue@37 378
Nenue@37 379 KT.button = function(self, name, text, tooltip, onClick)
Nenue@37 380 local button = GetButtonTemplate(name, self, 'KTButton', onClick)
Nenue@37 381
Nenue@37 382 button.tooltip = tooltip
Nenue@37 383 button:SetText(text)
Nenue@37 384 button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12))
Nenue@37 385
Nenue@37 386 SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth)
Nenue@37 387 return button
Nenue@37 388 end
Nenue@37 389
Nenue@37 390 KT.uibutton = function(self, name, text, tooltip, onClick, texture, coords)
Nenue@37 391 local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick)
Nenue@37 392
Nenue@37 393 button.tooltip = tooltip
Nenue@37 394 button:SetText(text)
Nenue@37 395
Nenue@37 396 if self.UIPanelIcon then
Nenue@37 397 local w, h, anchor, x, y = unpack(self.UIPanelIcon)
Nenue@37 398 button.icon:SetTexture(texture)
Nenue@37 399 button.icon:SetSize(w, h)
Nenue@37 400 button.icon:ClearAllPoints()
Nenue@37 401 button.icon:SetPoint(anchor, button, anchor, x, y)
Nenue@37 402 end
Nenue@37 403
Nenue@37 404 if not self.UIPanelSize then
Nenue@37 405 button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5)
Nenue@37 406 else
Nenue@37 407 button:SetSize(unpack(self.UIPanelSize))
Nenue@37 408 end
Nenue@37 409 if coords then
Nenue@37 410 button.icon:SetTexCoord(unpack(coords))
Nenue@37 411 end
Nenue@37 412 SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth)
Nenue@37 413 return button
Nenue@37 414 end
Nenue@37 415
Nenue@37 416 --- Co-routine Handler kajigger
Nenue@37 417 do
Nenue@37 418 local tickerQueue = {}
Nenue@37 419 local ticker
Nenue@37 420 local instant = false
Nenue@37 421 KT.tick = function()
Nenue@37 422
Nenue@37 423 if #tickerQueue == 0 then
Nenue@37 424 ticker:Cancel()
Nenue@37 425 ticker = nil
Nenue@37 426 end
Nenue@37 427 local func = tremove(tickerQueue, 1)
Nenue@37 428 if func then
Nenue@37 429 --print('#', #tickerQueue)
Nenue@37 430 func()
Nenue@37 431 end
Nenue@37 432 end
Nenue@37 433
Nenue@37 434 KT.wrap = function(f)
Nenue@37 435 if not ticker then
Nenue@37 436 --print('create ticker')
Nenue@37 437 ticker = C_Timer.NewTicker(.001, KT.tick)
Nenue@37 438 end
Nenue@37 439 tinsert(tickerQueue, f)
Nenue@37 440
Nenue@37 441 return #tickerQueue
Nenue@37 442
Nenue@37 443 end
Nenue@37 444 end
Nenue@37 445
Nenue@37 446 KT.handler:RegisterEvent('ADDON_LOADED')
Nenue@37 447 KT.handler:RegisterEvent('PLAYER_LOGIN')
Nenue@37 448 KT.handler:SetScript('OnEvent', onEvent)