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@7
|
193 KT.register = function(addon, nameOrModule, noGUI)
|
Nenue@7
|
194 local name
|
Nenue@5
|
195 if registeredHandles[addon] then
|
Nenue@7
|
196 if type(nameOrModule) == 'table' then
|
Nenue@7
|
197 tinsert(addon.modules, nameOrModule)
|
Nenue@7
|
198 name = debugstack(2,1,0):match(".+\\(%S+)%.lua")
|
Nenue@7
|
199
|
Nenue@7
|
200 print('auto-resolved module name', name, tostring(nameOrModule))
|
Nenue@7
|
201 else
|
Nenue@7
|
202 name = nameOrModule
|
Nenue@7
|
203 end
|
Nenue@5
|
204 else
|
Nenue@7
|
205 if not nameOrModule then
|
Nenue@5
|
206 assert(type(addon) == 'table', 'Need a valid table.')
|
Nenue@5
|
207 if addon.GetName then
|
Nenue@5
|
208 name = addon:GetName()
|
Nenue@5
|
209 else
|
Nenue@7
|
210 name = debugstack(2,1,0):match(".+AddOns\\(%S+)\\")
|
Nenue@7
|
211 print('auto-resolved addon name', name, tostring(nameOrModule))
|
Nenue@5
|
212 end
|
Nenue@5
|
213 assert(type(name) == 'string', 'Unable to resolve a valid stub name.')
|
Nenue@5
|
214 end
|
Nenue@5
|
215 -- if calling again, assume name is a file handle
|
Nenue@5
|
216
|
Nenue@5
|
217 registeredHandles[addon] = name
|
Nenue@5
|
218 KT.addons[name] = addon
|
Nenue@5
|
219 if addon.SetScript then
|
Nenue@5
|
220 addon:SetScript('OnEvent', KT.OnEvent)
|
Nenue@5
|
221 end
|
Nenue@5
|
222 addon.unhandled = 0
|
Nenue@5
|
223 addon.missed = 0
|
Nenue@5
|
224 addon.handled = 0
|
Nenue@5
|
225 addon.firstEvent = false
|
Nenue@5
|
226 addon.modules = {}
|
Nenue@5
|
227 tinsert(KT.initStack, addon)
|
Nenue@5
|
228 tinsert(KT.varsStack, addon)
|
Nenue@5
|
229
|
Nenue@5
|
230 if addon.GetName and (not noGUI) then
|
Nenue@5
|
231 addon.UIPanelAnchor = {'TOPLEFT', addon, 'TOPLEFT', 12, -12 }
|
Nenue@5
|
232 addon.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
|
Nenue@5
|
233 addon.button = KT.button
|
Nenue@5
|
234 addon.uibutton = KT.uibutton
|
Nenue@5
|
235 addon.tab = KT.tab
|
Nenue@5
|
236 addon.print = KT.print
|
Nenue@5
|
237 end
|
Nenue@5
|
238 end
|
Nenue@5
|
239
|
Nenue@5
|
240 return addon, (DEVIAN_WORKSPACE and function(...) _G.print(name, ...) end or function() end)
|
Nenue@5
|
241 end
|
Nenue@5
|
242
|
Nenue@5
|
243
|
Nenue@5
|
244
|
Nenue@5
|
245 local onEvent = function(self, event, arg1)
|
Nenue@5
|
246 if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
|
Nenue@5
|
247 -- run any init blocks left in the queue
|
Nenue@5
|
248 while #KT.initStack >= 1 do
|
Nenue@5
|
249 local addon = tremove(KT.initStack, 1)
|
Nenue@5
|
250 print('KT', addon:GetName(), 'init')
|
Nenue@5
|
251 if addon.init then
|
Nenue@5
|
252 xpcall(addon.init, LibKTError)
|
Nenue@5
|
253 for i, module in ipairs(addon.modules) do
|
Nenue@5
|
254 if module.init then
|
Nenue@5
|
255 xpcall(module.init, LibKTError)
|
Nenue@5
|
256 end
|
Nenue@5
|
257 end
|
Nenue@5
|
258 end
|
Nenue@5
|
259 end
|
Nenue@5
|
260
|
Nenue@5
|
261 -- run any variables blocks if player variables are ready
|
Nenue@5
|
262 if IsLoggedIn() and #KT.varsStack >= 1 then
|
Nenue@5
|
263 while #KT.varsStack >= 1 do
|
Nenue@5
|
264 local addon = tremove(KT.varsStack, 1)
|
Nenue@5
|
265 print(addon:GetName())
|
Nenue@5
|
266 if addon.variables then
|
Nenue@5
|
267 xpcall(addon.variables, LibKTError)
|
Nenue@5
|
268 for i, module in ipairs(addon.modules) do
|
Nenue@5
|
269 if module.variables then
|
Nenue@5
|
270 xpcall(module.variables, LibKTError)
|
Nenue@5
|
271 end
|
Nenue@5
|
272 end
|
Nenue@5
|
273 end
|
Nenue@5
|
274 end
|
Nenue@5
|
275 end
|
Nenue@5
|
276 end
|
Nenue@5
|
277 end
|
Nenue@5
|
278
|
Nenue@5
|
279 KT.print = function(module, ...)
|
Nenue@5
|
280 local msg = '|cFF00FFFF'..module:GetName()..'|r:'
|
Nenue@5
|
281 for i = 1, select('#', ...) do
|
Nenue@5
|
282 msg = msg .. ' ' .. tostring(select(i, ...))
|
Nenue@5
|
283 end
|
Nenue@5
|
284 DEFAULT_CHAT_FRAME:AddMessage(msg)
|
Nenue@5
|
285 end
|
Nenue@5
|
286
|
Nenue@5
|
287 --- Button generators
|
Nenue@5
|
288
|
Nenue@5
|
289 local GetButtonTemplate = function(name, parent, template, onClick)
|
Nenue@5
|
290 if _G[name] then
|
Nenue@5
|
291 return _G[name]
|
Nenue@5
|
292 end
|
Nenue@5
|
293
|
Nenue@5
|
294 local button = CreateFrame('Button', name, parent, template)
|
Nenue@5
|
295 button:RegisterForClicks('AnyUp')
|
Nenue@5
|
296 button:SetScript('OnClick', onClick)
|
Nenue@5
|
297 return button
|
Nenue@5
|
298 end
|
Nenue@5
|
299
|
Nenue@5
|
300 local SetButtonAnchor = function(self, collector, anchor, growth)
|
Nenue@5
|
301 if self:GetID() == 0 then
|
Nenue@5
|
302 self:SetID(#collector)
|
Nenue@5
|
303 print('registered TabButton #', self:GetID())
|
Nenue@5
|
304 end
|
Nenue@5
|
305
|
Nenue@5
|
306 if self:GetID() == 1 then
|
Nenue@5
|
307 self:SetPoint(unpack(anchor))
|
Nenue@5
|
308 else
|
Nenue@5
|
309 growth[2] = collector[self:GetID()-1]
|
Nenue@5
|
310 self:SetPoint(unpack(growth))
|
Nenue@5
|
311 end
|
Nenue@5
|
312 end
|
Nenue@5
|
313
|
Nenue@5
|
314 KT.tab = function(self, name, tooltip, texture, coords)
|
Nenue@5
|
315 local button = GetButtonTemplate(name, self, 'KTTabButton', self.SelectTab)
|
Nenue@5
|
316 button.icon:SetTexture(texture)
|
Nenue@5
|
317 button.tooltip = tooltip
|
Nenue@5
|
318 button:SetSize(unpack(self.tabSize))
|
Nenue@5
|
319 if coords then
|
Nenue@5
|
320 button.icon:SetTexCoord(unpack(coords))
|
Nenue@5
|
321 end
|
Nenue@5
|
322 SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth)
|
Nenue@5
|
323 return button
|
Nenue@5
|
324 end
|
Nenue@5
|
325
|
Nenue@5
|
326 KT.button = function(self, name, text, tooltip, onClick)
|
Nenue@5
|
327 local button = GetButtonTemplate(name, self, 'KTButton', onClick)
|
Nenue@5
|
328
|
Nenue@5
|
329 button.tooltip = tooltip
|
Nenue@5
|
330 button:SetText(text)
|
Nenue@5
|
331 button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12))
|
Nenue@5
|
332
|
Nenue@5
|
333 SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth)
|
Nenue@5
|
334 return button
|
Nenue@5
|
335 end
|
Nenue@5
|
336
|
Nenue@5
|
337 KT.uibutton = function(self, name, text, tooltip, onClick, texture, coords)
|
Nenue@5
|
338 local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick)
|
Nenue@5
|
339
|
Nenue@5
|
340 button.tooltip = tooltip
|
Nenue@5
|
341 button:SetText(text)
|
Nenue@5
|
342
|
Nenue@5
|
343 if self.UIPanelIcon then
|
Nenue@5
|
344 local w, h, anchor, x, y = unpack(self.UIPanelIcon)
|
Nenue@5
|
345 button.icon:SetTexture(texture)
|
Nenue@5
|
346 button.icon:SetSize(w, h)
|
Nenue@5
|
347 button.icon:ClearAllPoints()
|
Nenue@5
|
348 button.icon:SetPoint(anchor, button, anchor, x, y)
|
Nenue@5
|
349 end
|
Nenue@5
|
350
|
Nenue@5
|
351 if not self.UIPanelSize then
|
Nenue@5
|
352 button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5)
|
Nenue@5
|
353 else
|
Nenue@5
|
354 button:SetSize(unpack(self.UIPanelSize))
|
Nenue@5
|
355 end
|
Nenue@5
|
356 if coords then
|
Nenue@5
|
357 button.icon:SetTexCoord(unpack(coords))
|
Nenue@5
|
358 end
|
Nenue@5
|
359 SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth)
|
Nenue@5
|
360 return button
|
Nenue@5
|
361 end
|
Nenue@5
|
362
|
Nenue@5
|
363
|
Nenue@5
|
364 KT.handler:RegisterEvent('ADDON_LOADED')
|
Nenue@5
|
365 KT.handler:RegisterEvent('PLAYER_LOGIN')
|
Nenue@5
|
366 KT.handler:SetScript('OnEvent', onEvent) |