annotate LibKraken/LibKraken-1.0.lua @ 5:9ac29fe77455

- dynamic profession spell mapping - dynamic talent spell mapping - protection of dynamic slots that aren't in use - plugin abstractors for accessing state data - a lot of fixes related to the 7.0.3 API
author Nenue
date Tue, 26 Jul 2016 19:29:44 -0400
parents
children a2fc77fa4c73
rev   line source
Nenue@5 1 --[[
Nenue@5 2 -- KrakynTools
Nenue@5 3 -- AddOn prototyping library.
Nenue@5 4 --
Nenue@5 5 -- Implements
Nenue@5 6 -- KT.register(frame) to hook the following (all optional):
Nenue@5 7 -- frame:init() run immediately after KT sets itself up
Nenue@5 8 -- frame:profile("Name-TruncatedRealm") called the first time SavedVars data becomes available
Nenue@5 9 -- frame:variables() called upon variables being available
Nenue@5 10 -- frame:event(event, ...) replaces the event callback
Nenue@5 11 -- frame:ui() called by /ui when activating
Nenue@5 12 --
Nenue@5 13 -- frame:tab(name, tooltip, texture, coord)
Nenue@5 14 -- produces a serial button that changes display tabs
Nenue@5 15 --
Nenue@5 16 -- frame:button(name, text, tooltip, onClick)
Nenue@5 17 -- produces a button with OnClick script
Nenue@5 18 --
Nenue@5 19 -- frame:uibutton(name, text, tooltip, onClick, texture, coord)
Nenue@5 20 -- produces a header button with desired onClick
Nenue@5 21 --
Nenue@5 22 ]]--
Nenue@5 23
Nenue@5 24 local LIBKT_MAJOR, LIBKT_MINOR = "LibKraken", 1
Nenue@5 25 local KT = LibStub:NewLibrary(LIBKT_MAJOR, LIBKT_MINOR)
Nenue@5 26
Nenue@5 27 --GLOBALS: LibKT, KrakTool, KTErrorFrame, LibKTError, SlashCmdList, SLASH_RL1, SLASH_UI1
Nenue@5 28 local CreateFrame, debugstack, tostring, select = CreateFrame, debugstack, tostring, select
Nenue@5 29 local print, max, unpack, tinsert = print, max, unpack, tinsert
Nenue@5 30 local ipairs, xpcall, next, safecall = ipairs, xpcall, next, safecall
Nenue@5 31 local UI_TOGGLE = false
Nenue@5 32 local db
Nenue@5 33
Nenue@5 34
Nenue@5 35 KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent)
Nenue@5 36 KT.addons = {}
Nenue@5 37 KT.initStack = {}
Nenue@5 38 KT.varsStack = {}
Nenue@5 39 local print = DEVIAN_WORKSPACE and function(...) print('LKT', ...) end or function() end
Nenue@5 40 local registeredHandles = {}
Nenue@5 41
Nenue@5 42 --- /rl
Nenue@5 43 -- ReloadUI shortcut
Nenue@5 44 SLASH_RL1 = "/rl"
Nenue@5 45 SlashCmdList.RL = function ()
Nenue@5 46 ReloadUI()
Nenue@5 47 end
Nenue@5 48
Nenue@5 49 --- /kv addon ...
Nenue@5 50 -- Dumps table values from "addon", using the arguments as index values.
Nenue@5 51 -- Numerics are converted, and names that end with "()" will be called as such
Nenue@5 52 SLASH_KV1 = "/kv"
Nenue@5 53 SlashCmdList.KV = function (editbox, input)
Nenue@5 54
Nenue@5 55 end
Nenue@5 56
Nenue@5 57
Nenue@5 58 --- /ui
Nenue@5 59 -- Run any addon:ui() methods
Nenue@5 60 SLASH_UI1 = "/ui"
Nenue@5 61 SlashCmdList.UI = function ()
Nenue@5 62 if UI_TOGGLE then
Nenue@5 63 UI_TOGGLE = false
Nenue@5 64 else
Nenue@5 65 UI_TOGGLE = true
Nenue@5 66 end
Nenue@5 67 for i, frame in pairs(KT.frames) do
Nenue@5 68 if UI_TOGGLE then
Nenue@5 69 if frame.close then
Nenue@5 70 frame.close()
Nenue@5 71 else
Nenue@5 72 frame:Hide()
Nenue@5 73 end
Nenue@5 74 else
Nenue@5 75 if frame.ui then
Nenue@5 76 frame.ui()
Nenue@5 77 end
Nenue@5 78 frame:Show()
Nenue@5 79 end
Nenue@5 80 end
Nenue@5 81 end
Nenue@5 82
Nenue@5 83 LibKTError = function(msg)
Nenue@5 84 local dstack = debugstack(2)
Nenue@5 85 :gsub("Interface\\AddOns\\",'')
Nenue@5 86 :gsub("<(.-)>", function(a) return '|cFF00FFFF<'.. a ..'>|r' end)
Nenue@5 87
Nenue@5 88
Nenue@5 89
Nenue@5 90 KTErrorFrame.errmsg:SetText(msg)
Nenue@5 91 KTErrorFrame.debugstack:SetText(dstack)
Nenue@5 92 KTErrorFrame:SetHeight(KTErrorFrame.debugstack:GetStringHeight() + KTErrorFrame.errmsg:GetStringHeight() + 12)
Nenue@5 93 KTErrorFrame:Show()
Nenue@5 94 end
Nenue@5 95
Nenue@5 96
Nenue@5 97 local pending = {}
Nenue@5 98 local processing = false
Nenue@5 99 local isHandled = false
Nenue@5 100 local nodebug = false
Nenue@5 101 function KT.OnEvent (addon, event, ...)
Nenue@5 102 if processing then
Nenue@5 103 local args = {...}
Nenue@5 104 C_Timer.After(0, function() KT.OnEvent(addon, event, unpack(args)) end)
Nenue@5 105 return
Nenue@5 106 else
Nenue@5 107
Nenue@5 108 end
Nenue@5 109 --- reset state
Nenue@5 110 processing = true
Nenue@5 111 isHandled = false
Nenue@5 112 nodebug = false
Nenue@5 113
Nenue@5 114
Nenue@5 115 if addon.event then
Nenue@5 116 nodebug = addon.event(addon, event, ...)
Nenue@5 117 end
Nenue@5 118
Nenue@5 119 if addon[event] then
Nenue@5 120 nodebug = addon[event](addon, event, ...) or nodebug
Nenue@5 121 addon.missed = 0
Nenue@5 122 addon.handled = addon.handled + 1
Nenue@5 123 isHandled = true
Nenue@5 124 else
Nenue@5 125 addon.firstEvent = false
Nenue@5 126 addon.unhandled = addon.unhandled + 1
Nenue@5 127 addon.missed = addon.missed + 1
Nenue@5 128 end
Nenue@5 129
Nenue@5 130 for i, module in ipairs(addon.modules) do
Nenue@5 131 --print(i, module)
Nenue@5 132 if module[event] then
Nenue@5 133 nodebug = module[event](addon, event, ...) or nodebug
Nenue@5 134 addon.missed = 0
Nenue@5 135 addon.handled = addon.handled + 1
Nenue@5 136 isHandled = true
Nenue@5 137 else
Nenue@5 138 addon.firstEvent = false
Nenue@5 139 addon.unhandled = addon.unhandled + 1
Nenue@5 140 addon.missed = addon.missed + 1
Nenue@5 141 end
Nenue@5 142
Nenue@5 143 end
Nenue@5 144 if nodebug then
Nenue@5 145 processing = false
Nenue@5 146 return
Nenue@5 147 else
Nenue@5 148 KT.UpdateEventStatus(addon, event, ...)
Nenue@5 149 processing = false
Nenue@5 150 end
Nenue@5 151
Nenue@5 152 end
Nenue@5 153
Nenue@5 154 KT.UpdateEventStatus = function(addon, event, ...)
Nenue@5 155 print(addon:GetName(), event, ...)
Nenue@5 156
Nenue@5 157 -- debug outputs
Nenue@5 158 if addon.status then
Nenue@5 159 addon.status:SetText(event .. '\n|cFF00FF00' .. addon.handled .. '|r |cFFFF8800' .. addon.missed .. '|r |cFFFF4400' .. addon.unhandled .. '|r')
Nenue@5 160 if isHandled then
Nenue@5 161 addon.status:SetTextColor(0,1,0)
Nenue@5 162 if addon.log then
Nenue@5 163 local logtext = event
Nenue@5 164 for i = 1, select('#',...) do
Nenue@5 165 logtext = logtext .. '\n' .. i .. ':' .. tostring(select(i,...))
Nenue@5 166 end
Nenue@5 167 addon.log:SetText('|cFFFFFF00last|r\n' .. logtext)
Nenue@5 168 local newWidth = addon.log:GetStringWidth()
Nenue@5 169
Nenue@5 170 if addon.logfirst then
Nenue@5 171 if not addon.firstEvent then
Nenue@5 172 addon.firstEvent = event
Nenue@5 173 addon.logfirst:SetText('|cFF00FF88first|r\n' .. logtext)
Nenue@5 174 end
Nenue@5 175
Nenue@5 176 newWidth = newWidth + addon.logfirst:GetStringWidth()
Nenue@5 177 end
Nenue@5 178 if addon.logdiff then
Nenue@5 179 if not event ~= addon.firstEvent then
Nenue@5 180 addon.firstEvent = event
Nenue@5 181 addon.logdiff:SetText('|cFF0088FFdiff|r\n' .. logtext)
Nenue@5 182 end
Nenue@5 183 newWidth = newWidth + addon.logdiff:GetStringWidth()
Nenue@5 184 end
Nenue@5 185 --addon:SetWidth(newWidth)
Nenue@5 186 end
Nenue@5 187 else
Nenue@5 188 addon.status:SetTextColor(1,0,0)
Nenue@5 189 end
Nenue@5 190 end
Nenue@5 191 end
Nenue@5 192
Nenue@5 193 KT.register = function(addon, name, noGUI)
Nenue@5 194 if registeredHandles[addon] then
Nenue@5 195 name = name or debugstack(2,1,0):gsub("\\n.+", ""):gsub("^Interface\\AddOns\\", ""):gsub("%s+$", "")
Nenue@5 196 else
Nenue@5 197 if not name then
Nenue@5 198 assert(type(addon) == 'table', 'Need a valid table.')
Nenue@5 199 if addon.GetName then
Nenue@5 200 name = addon:GetName()
Nenue@5 201 else
Nenue@5 202 name = debugstack(2,1,0):gsub("\\n.+", ""):gsub("^Interface\\AddOns\\", ""):gsub("%s+$", "")
Nenue@5 203 end
Nenue@5 204 assert(type(name) == 'string', 'Unable to resolve a valid stub name.')
Nenue@5 205 end
Nenue@5 206 -- if calling again, assume name is a file handle
Nenue@5 207
Nenue@5 208 registeredHandles[addon] = name
Nenue@5 209 KT.addons[name] = addon
Nenue@5 210 if addon.SetScript then
Nenue@5 211 addon:SetScript('OnEvent', KT.OnEvent)
Nenue@5 212 end
Nenue@5 213 addon.unhandled = 0
Nenue@5 214 addon.missed = 0
Nenue@5 215 addon.handled = 0
Nenue@5 216 addon.firstEvent = false
Nenue@5 217 addon.modules = {}
Nenue@5 218 tinsert(KT.initStack, addon)
Nenue@5 219 tinsert(KT.varsStack, addon)
Nenue@5 220
Nenue@5 221 if addon.GetName and (not noGUI) then
Nenue@5 222 addon.UIPanelAnchor = {'TOPLEFT', addon, 'TOPLEFT', 12, -12 }
Nenue@5 223 addon.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
Nenue@5 224 addon.button = KT.button
Nenue@5 225 addon.uibutton = KT.uibutton
Nenue@5 226 addon.tab = KT.tab
Nenue@5 227 addon.print = KT.print
Nenue@5 228 end
Nenue@5 229 end
Nenue@5 230
Nenue@5 231 return addon, (DEVIAN_WORKSPACE and function(...) _G.print(name, ...) end or function() end)
Nenue@5 232 end
Nenue@5 233
Nenue@5 234
Nenue@5 235
Nenue@5 236 local onEvent = function(self, event, arg1)
Nenue@5 237 if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
Nenue@5 238 -- run any init blocks left in the queue
Nenue@5 239 while #KT.initStack >= 1 do
Nenue@5 240 local addon = tremove(KT.initStack, 1)
Nenue@5 241 print('KT', addon:GetName(), 'init')
Nenue@5 242 if addon.init then
Nenue@5 243 xpcall(addon.init, LibKTError)
Nenue@5 244 for i, module in ipairs(addon.modules) do
Nenue@5 245 if module.init then
Nenue@5 246 xpcall(module.init, LibKTError)
Nenue@5 247 end
Nenue@5 248 end
Nenue@5 249 end
Nenue@5 250 end
Nenue@5 251
Nenue@5 252 -- run any variables blocks if player variables are ready
Nenue@5 253 if IsLoggedIn() and #KT.varsStack >= 1 then
Nenue@5 254 while #KT.varsStack >= 1 do
Nenue@5 255 local addon = tremove(KT.varsStack, 1)
Nenue@5 256 print(addon:GetName())
Nenue@5 257 if addon.variables then
Nenue@5 258 xpcall(addon.variables, LibKTError)
Nenue@5 259 for i, module in ipairs(addon.modules) do
Nenue@5 260 if module.variables then
Nenue@5 261 xpcall(module.variables, LibKTError)
Nenue@5 262 end
Nenue@5 263 end
Nenue@5 264 end
Nenue@5 265 end
Nenue@5 266 end
Nenue@5 267 end
Nenue@5 268 end
Nenue@5 269
Nenue@5 270 KT.print = function(module, ...)
Nenue@5 271 local msg = '|cFF00FFFF'..module:GetName()..'|r:'
Nenue@5 272 for i = 1, select('#', ...) do
Nenue@5 273 msg = msg .. ' ' .. tostring(select(i, ...))
Nenue@5 274 end
Nenue@5 275 DEFAULT_CHAT_FRAME:AddMessage(msg)
Nenue@5 276 end
Nenue@5 277
Nenue@5 278 --- Button generators
Nenue@5 279
Nenue@5 280 local GetButtonTemplate = function(name, parent, template, onClick)
Nenue@5 281 if _G[name] then
Nenue@5 282 return _G[name]
Nenue@5 283 end
Nenue@5 284
Nenue@5 285 local button = CreateFrame('Button', name, parent, template)
Nenue@5 286 button:RegisterForClicks('AnyUp')
Nenue@5 287 button:SetScript('OnClick', onClick)
Nenue@5 288 return button
Nenue@5 289 end
Nenue@5 290
Nenue@5 291 local SetButtonAnchor = function(self, collector, anchor, growth)
Nenue@5 292 if self:GetID() == 0 then
Nenue@5 293 self:SetID(#collector)
Nenue@5 294 print('registered TabButton #', self:GetID())
Nenue@5 295 end
Nenue@5 296
Nenue@5 297 if self:GetID() == 1 then
Nenue@5 298 self:SetPoint(unpack(anchor))
Nenue@5 299 else
Nenue@5 300 growth[2] = collector[self:GetID()-1]
Nenue@5 301 self:SetPoint(unpack(growth))
Nenue@5 302 end
Nenue@5 303 end
Nenue@5 304
Nenue@5 305 KT.tab = function(self, name, tooltip, texture, coords)
Nenue@5 306 local button = GetButtonTemplate(name, self, 'KTTabButton', self.SelectTab)
Nenue@5 307 button.icon:SetTexture(texture)
Nenue@5 308 button.tooltip = tooltip
Nenue@5 309 button:SetSize(unpack(self.tabSize))
Nenue@5 310 if coords then
Nenue@5 311 button.icon:SetTexCoord(unpack(coords))
Nenue@5 312 end
Nenue@5 313 SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth)
Nenue@5 314 return button
Nenue@5 315 end
Nenue@5 316
Nenue@5 317 KT.button = function(self, name, text, tooltip, onClick)
Nenue@5 318 local button = GetButtonTemplate(name, self, 'KTButton', onClick)
Nenue@5 319
Nenue@5 320 button.tooltip = tooltip
Nenue@5 321 button:SetText(text)
Nenue@5 322 button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12))
Nenue@5 323
Nenue@5 324 SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth)
Nenue@5 325 return button
Nenue@5 326 end
Nenue@5 327
Nenue@5 328 KT.uibutton = function(self, name, text, tooltip, onClick, texture, coords)
Nenue@5 329 local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick)
Nenue@5 330
Nenue@5 331 button.tooltip = tooltip
Nenue@5 332 button:SetText(text)
Nenue@5 333
Nenue@5 334 if self.UIPanelIcon then
Nenue@5 335 local w, h, anchor, x, y = unpack(self.UIPanelIcon)
Nenue@5 336 button.icon:SetTexture(texture)
Nenue@5 337 button.icon:SetSize(w, h)
Nenue@5 338 button.icon:ClearAllPoints()
Nenue@5 339 button.icon:SetPoint(anchor, button, anchor, x, y)
Nenue@5 340 end
Nenue@5 341
Nenue@5 342 if not self.UIPanelSize then
Nenue@5 343 button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5)
Nenue@5 344 else
Nenue@5 345 button:SetSize(unpack(self.UIPanelSize))
Nenue@5 346 end
Nenue@5 347 if coords then
Nenue@5 348 button.icon:SetTexCoord(unpack(coords))
Nenue@5 349 end
Nenue@5 350 SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth)
Nenue@5 351 return button
Nenue@5 352 end
Nenue@5 353
Nenue@5 354
Nenue@5 355 KT.handler:RegisterEvent('ADDON_LOADED')
Nenue@5 356 KT.handler:RegisterEvent('PLAYER_LOGIN')
Nenue@5 357 KT.handler:SetScript('OnEvent', onEvent)