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