comparison Veneer.lua @ 59:07ef62fe201f

Re-write of BuffFrame module: - uses secure hooks on blizzard BuffFrame.lua functions to determine needed action - make use of built-in table behavior to reduce unnecessary frame updates
author Nenue
date Thu, 28 Jul 2016 18:27:56 -0400
parents f253baf6022d
children 3f083d389c18
comparison
equal deleted inserted replaced
58:f253baf6022d 59:07ef62fe201f
1 --------------------------------------------
2 -- Veneer 1 -- Veneer
3 -- Core 2 -- Customization tool for the small bits and pieces
4 -- author: Krakyn
5 -- @project-revision@ @project-hash@
6 -- @file-revision@ @file-hash@
7 -- Created: 4/27/2016 1:02 AM
8 --------------------------------------------
9 --- Implemented methods
10 -- OnInitialize
11 -- OnUpdate
12 -- OnEnable -- runs as soon as GetSpecialization() returns valid data
13 3
14 local ADDON, A = ... 4 -- BuffFrame
15 local wipe, min, max, random, tinsert, tremove = table.wipe, math.min, math.max, math.random, table.insert, table.remove 5 -- Provides mechanisms for positioning and alter buff button parameters.
16 local pairs, ipairs, select, unpack, _G = pairs, ipairs, select, unpack, _G 6 -- Mostly re-configures the blizzard UI frames due to limitations of SecureTemplate.
17 local type, tostring, format = type, tostring, string.format
18 7
19 --- Establish presence 8 local vn, print = LibStub("LibKraken").register(VeneerController)
20 Veneer = Veneer or CreateFrame('Frame', 'Veneer', UIParent)
21 local V = Veneer
22 A.frame = V
23
24 --- Work variables
25 local modules = {} -- module collector
26 local queuedModules = {} -- indicates modules that were encountered out of dependency order
27 local checkForConfig = {} -- indicates frames created from XML that use their own namespace for position control
28 local moduleStack = {} -- dictates the order in which module methods are fired
29 local initOnced -- internal check for doing bottom-up SV retrieval
30
31 --- Utilities
32 V.wipeall = function (...)
33 for i = 1, select('#', ...) do
34 wipe(select(i, ...))
35 end
36 end
37
38 --- Various region categories
39 V.displays = {}
40 V.configLayers = {}
41 V.configLayersRef = {}
42 9
43 10
44 --- Returns a debug hook for adding generic module information to each message 11 local defaults = {
45 -- @usage func = V.print(sig) 12 enableAll = true,
46 -- @param sig channel name or number 13 enableModule = {
47 local debugstack = _G.debugstack 14 BuffFrame = true,
48 local Devian = _G.Devian 15 },
49 local printfuncs = {} 16 BuffFrame = {
50 V.print = function(pref, ...) 17 width = 48,
51 if Devian and Devian.InWorkspace() then 18 height = 48,
52 printfuncs[pref] = printfuncs[pref] or function(...) print(pref, ...) end
53 19
54 return printfuncs[pref] 20 }
55 else 21 }
56 return function () end 22
23 vn.init = function()
24 if (not VeneerData) or (not VeneerData.version) then
25 VeneerData = defaults
57 end 26 end
58 end
59
60 --@debug@
61 local rgb = {}
62 local getcolor = function()
63 local n, p = 0, 4
64 for i = 1, 3 do
65 rgb[i] = min(random(n,p) * 64, 255)
66 if rgb[i] == 255 then
67 p = 4
68 elseif rgb[i] > 0 then
69 n = 2
70 end
71 end
72 return unpack(rgb)
73 end
74
75 local color = {}
76 local fprints = {}
77 --- Attempts to generate a debug printer based on the local scope. Results vary by where the originator was invoked.
78 V.fprint = function()
79 if not (Devian and Devian.InWorkspace()) then
80 return function() end
81 end
82
83 local sig = debugstack(2,1)
84 if fprints[sig] then
85 return fprints[sig]
86 end
87
88 local func = sig:match("%`(%a+)%'")
89 if not func then
90 func = sig:match("<(.-)>")
91 end
92 func = func:gsub('(%l+)(%u)', function(a, b) return a:sub(0,2) .. b end, 1)
93 func = func:gsub('^.+%\\', '')
94 if not func then
95 func = 'noname'
96 end
97
98 local r, g, b = getcolor()
99 color[sig] = color[sig] or format('|cFF%02X%02X%02X%s|r', r, g, b, func)
100
101 --print(color[func] .. ' ( ' .. table.concat(args, ', ')..' )' )
102 func = V.print(func)
103 fprints[sig] = func
104 return func
105 end
106
107 --@end-debug@
108 --[=[@non-debug@
109 V.print = function() end
110 --@end-non-debug@]=]
111
112 -- for the Mikk script
113 -- GLOBALS: NUM_LE_RAID_BUFF_TYPES
114 -- GLOBALS: BUFF_FLASH_TIME_ON, BUFF_FLASH_TIME_OFF, BUFF_MIN_ALPHA, BUFF_WARNING_TIME, BUFF_DURATION_WARNING_TIME
115 -- GLOBALS: BUFFS_PER_ROW, BUFF_MAX_DISPLAY, BUFF_ACTUAL_DISPLAY, DEBUFF_MAX_DISPLAY, DEBUFF_ACTUAL_DISPLAY, BUFF_ROW_SPACING
116 -- GLOBALS: CONSOLIDATED_BUFFS_PER_ROW, CONSOLIDATED_BUFF_ROW_HEIGHT, NUM_TEMP_ENCHANT_FRAMES
117 -- GLOBALS: BUFF_BUTTON_HEIGHT, BUFF_FRAME_BASE_EXTENT, BUFF_HORIZ_SPACING
118
119 local print = V.print('Bfl')
120
121 --- Template for making perpendicular traversals of the displays structure; also makes sure the table is there
122 local setmetatable = setmetatable
123 V.Abstract = function(dest, key, table)
124 if table then
125 for _, v in pairs(dest) do
126 v[key] = {}
127 end
128 end
129 V[key] = setmetatable({}, {
130 __index = function(t, k)
131 return dest[k][key]
132 end,
133 __newindex = function(_, k, v)
134 print('abstract write ('..key..'):', k)
135 dest[k][key] = v
136 end,
137 __tostring = function() return 'Abstract:'..key..'' end
138 })
139
140
141 return V[key]
142 end
143
144
145 --- internal
146 local ModulesCall = function(func, flag)
147 local n = 0
148 for i = 1, #moduleStack do
149 print('calling level '..i)
150 local stackset = moduleStack[i]
151 for name, module in pairs(stackset) do
152 n = n + 1
153 if module[func] then
154 -- nil = pass
155 if not flag or (module.Conf and module.Conf[flag]) then
156 if (flag) then
157 print(' check', flag, '=', module.Conf[flag])
158 end
159
160 print(' ',n..' '..name..'.'..func..'()')
161 module[func](module, module.Conf)
162 end
163 end
164 end
165 end
166 end
167
168
169 local Enable = function()
170 end
171
172 --- The things that happen repeatedly
173 local Init = function ()
174 end
175
176
177 local layers, refs, displays = V.configLayers, V.configLayersRef, V.displays
178 --- Things that happen immediately upon entering world
179 local InitOnce = function()
180 print('entering world first time')
181 local defaults = {}
182 print('|cFFFFFF00Veneer|r')
183 if not VeneerData then
184 VeneerData = {}
185 for k,v in pairs(defaults) do
186 VeneerData[k] = v
187 end
188 print('Veneer defaults being used.')
189 end
190 V.Conf = setmetatable(VeneerData, {__index = function(_, k) return defaults[k] end})
191
192 -- To ensure that modules are run in controlled order, walk the dependency list; if the dep shows up
193 -- in the loaded manifest, remove the value. If the dep list isn't empty, move that module to the next
194 -- layer.
195 local loaded = {}
196 local stackLevels = #moduleStack
197 local i = 1
198 moduleStack[1] = modules
199 repeat
200 print('setting init level '.. i)
201 local queue = moduleStack[i]
202 for name, module in pairs(queue) do
203
204 if queuedModules[name] and #queuedModules[name] > 0 then
205 local p = #queuedModules[name]
206 for j = 1, p do
207 local dep = queuedModules[name][j]
208
209 if loaded[dep] then
210 print( ' ' .. dep .. ' OK')
211 queuedModules[name][j] = nil
212 for k = j, p do
213 print(' shift ' .. (k+1) .. ' ('..tostring(queuedModules[name][k+1])..') to ' .. k ..'')
214 queuedModules[name][k] = queuedModules[name][k+1]
215 end
216 end
217 end
218
219 if #queuedModules[name] == 0 then
220 queuedModules[name] = nil
221 print(' |cFF00FFFF'.. name ..'|r deps OK')
222 loaded[name] = true
223 else
224
225 print(' |cFFFF8800' .. name ..'|r pending')
226 local next = i+1
227 if not moduleStack[next] then
228 moduleStack[next] = {}
229 end
230 stackLevels = next
231 moduleStack[next][name] = module
232 queue[name] = nil
233 end
234
235 else
236 print(' |cFF00FF00'.. name ..'|r no deps')
237 loaded[name] = true
238 end
239 end
240 i = i + 1
241 until i > stackLevels
242
243 for level, batch in ipairs(moduleStack) do
244 print('config level', level)
245 for name, module in pairs(batch) do
246 if not VeneerData[name] then
247 VeneerData[name] = {}
248 end
249
250 if module.defaults then
251 print('setting defaults for module', name)
252 --[===[@non-debug@
253 if not VeneerData[name] then
254 --@end-non-debug@]===]
255 VeneerData[name] = {}
256 --[===[@non-debug@
257 end
258 --@end-non-debug@]===]
259 for k,v in pairs(module.defaults) do
260 VeneerData[name][k] = v
261 end
262 module.Conf = VeneerData[name]
263 end
264
265 if VeneerData[name].enabled == nil then
266 VeneerData[name].enabled = true
267 end
268
269 end
270 end
271
272 --- Pull in any XML templates
273 if #checkForConfig >= 1 then
274 local queuedFrame = tremove(checkForConfig)
275 while queuedFrame do
276 V.SetConfigLayers(queuedFrame)
277 V.UpdateXMLFrame(queuedFrame)
278 queuedFrame = tremove(checkForConfig)
279 end
280 end
281 end
282
283 --- Fires an update to all modules
284 local lastUpdate
285 function V.UpdateAll(...)
286 lastUpdate = GetTime()
287 ModulesCall('OnUpdate')
288 end
289
290 V:RegisterEvent('PLAYER_ENTERING_WORLD')
291 V:SetScript('OnEvent', function(self, event)
292 if event == 'PLAYER_ENTERING_WORLD' then
293 if not initOnced then
294 InitOnce()
295 ModulesCall('OnInitialize')
296 initOnced = true
297 C_Timer.After(1, function()
298 if GetSpecialization() then
299 print(GetSpecialization(), 'enabling')
300
301 ModulesCall('OnEnable', 'enabled')
302 V:SetScript('OnUpdate', nil)
303 end
304 end)
305 end
306 end
307
308 V.UpdateAll()
309
310 if event == 'PLAYER_ENTERING_WORLD' then
311 V.UpdateConfigLayers()
312 end
313
314 end)
315
316 --- Modulizer method
317 --
318 function V:RegisterModule (name, module, ...)
319 if modules[name] then
320 print('pulling modules[|cFFFF8800'.. tostring(name) ..'|r]')
321 return modules[name]
322 end
323
324 print('new module |cFF00BBFF'.. tostring(name) ..'|r')
325 if module then
326 if modules[name] then
327 error("Module table for '"..tostring(name).."' already exists.")
328 end
329 else
330 module = CreateFrame('Frame', 'Veneer' .. tostring(name) .. 'Handler', V, 'VeneerHandlerTemplate')
331 end
332 modules[name] = module
333 V[name] = module
334 if select('#', ...) >= 1 then
335 local numDeps = select('#', ...)
336 print(' '..numDeps..' deps detected')
337 for i = 1, numDeps do
338 local dep = select(i, ...)
339 -- means that init/enable funcs are ordered to run after deps do their things
340 queuedModules[name] = queuedModules[name] or {}
341 tinsert(queuedModules[name], dep)
342 print(' needs '..dep)
343 end
344 end
345 return module
346 end
347
348
349 V.SetConfigLayers = function(frame)
350 local print = V.fprint()
351 if not frame.config then
352 --print(frame:GetName(), 'has no config layers')
353 return
354 end
355 --print('Registering config layers from', frame:GetName())
356
357 for i, subframe in ipairs(frame.config) do
358 -- make sure there are no duplicates
359 if not refs[subframe] then
360 local key = #layers+1
361 layers[key] = subframe
362 refs[subframe] = key
363 end
364 --print(' ', i, subframe:GetName())
365 end
366 end
367
368 V.RemoveConfigLayers = function(frame)
369
370 local print = V.fprint()
371 print('|cFFFF0000RemoveConfigLayers', frame:GetName())
372 for i, subframe in pairs(layers) do
373 if subframe:GetParent() == frame then
374 print('|cFFFF8800 ', subframe:GetParent():GetName(), '|cFFFFFF00', subframe:GetName())
375 layers[i]:Hide()
376 layers[i] = nil
377 refs[subframe] = nil
378 end
379 end
380 end
381
382 V.ToggleGuideLayers = function()
383 local print = V.fprint()
384 local func = V.Conf.GuidesMode and 'Show' or 'Hide'
385 local numAnchors = 0
386
387 for id, region in pairs(layers) do
388 --print(id, region:GetName(), func)
389 region[func](region)
390 end
391
392 --print('['..func..'] updated', #layers, 'regions,', numAnchors, 'frames')
393 end
394 V.UpdateConfigLayers = function()
395 print('|cFFFF0000', debugstack())
396 V.ToggleGuideLayers()
397 end
398
399 local XMLFrame_Enable = function(self, value)
400 local name = self:GetName()
401 local print = V.print('XML')
402
403 if not V.Conf[name] then
404 V.Conf[name] = {
405 enabled = true
406 }
407 end
408
409 print()
410 local enabled
411 if value == nil then
412 if V.Conf[name].enabled == nil then
413 print('toggle based on visibility')
414 enabled = (not self:IsVisible()) and true or false
415 else
416 print('toggle a config value =', V.Conf[name].enabled)
417 enabled = V.Conf[name].enabled
418 end
419
420 enabled = (enabled ~= true) and true or false
421 else
422 print('use argument value', value)
423 enabled = value
424 end
425
426 print('arg =', value, 'conf =', V.Conf[name].enabled, 'result=', enabled)
427
428 V.Conf[name].enabled = enabled
429
430 local stateFunc = enabled and 'Show' or 'Hide'
431 local eventFunc = enabled and 'OnToggle' or 'OnToggle'
432 --- taggled layers
433 if self.toggled then
434 for i, region in pairs(self.toggled) do
435 region[stateFunc](region)
436 end
437 end
438 --- toggle action
439 if self.OnToggle then
440 self:OnToggle(V.Conf[name].enabled)
441 end
442 --- do enable
443 if V.Conf[name].enabled then
444 if self.OnEnable then
445 self:OnEnable()
446 end
447 else
448 if self.OnDisable then
449 self:OnDisable()
450 end
451 end
452 end
453 --- Generic handlers for keeping track of XML-defined frames
454 local print = V.print('XML')
455 local prototypes = {}
456 prototypes.OnDragStart = function(self)
457 self.xA = self:GetLeft()
458 self.yA = self:GetBottom()
459 self.anchorTo, self.relativeTo, self.relativePoint, self.x, self.y = self:GetPoint(1)
460 print('acquire anchor', self:GetPoint(1))
461 print(self:GetName(), 'start moving ('..self.x..', '..self.y..')')
462 self:StartMoving()
463 end
464
465 prototypes.OnDragStop = function(self)
466 local name = self:GetName()
467 print(name, 'stop moving ('..self:GetLeft()..', '..self:GetBottom()..')')
468 local xB = self:GetLeft() - self.xA
469 local yB = self:GetBottom() - self.yA
470 print('storing anchor point', self.anchorTo, self.relativePoint, self.x + xB, self.y + yB)
471 self:StopMovingOrSizing()
472 V.Conf[name].position = {self.anchorTo, self.relativePoint, self.x + xB, self.y + yB}
473 V.UpdateXMLFrame(self)
474 end
475
476
477 V.RegisterModuleFrame = function(self, moduleName)
478 local name = self:GetName()
479 tinsert(checkForConfig, self)
480 self.Enable = XMLFrame_Enable
481 self.moduleName = moduleName
482 print('|cFF00FF00XML stuff related to '.. tostring(moduleName) .. ':', self:GetName())
483 ------------------------------------------------------------------------------------------
484 if not V[name] then
485 return
486 end
487
488 local scriptTypes = {'OnUpdate', 'OnEvent', 'OnDragStart', 'OnDragStop'}
489 for script in next(scriptTypes) do
490 if V[name][script] then
491 self:SetScript(script, V[name][script])
492 end
493 end
494
495 end
496 local XMLFrame_OnDragStart = function() end
497 local XMLFrame_OnDragStop = function() end
498
499 V.UpdateXMLFrame = function(self)
500 local print = V.print('XML')
501
502 local name = self:GetName()
503 27
504 28
505 29
506 if self.drag then
507 self:RegisterForDrag('LeftButton')
508 self:SetScript('OnDragStart', prototypes.OnDragStart)
509 if self.OnDragStop then
510 self:SetScript('OnDragStop', function(self, ...)
511 print('|cFFFF0088end of dragging').
512 self:OnDragStop(self, ...)
513 prototypes.OnDragStop(self, ...)
514 end)
515 else
516 self:SetScript('OnDragStop', prototypes.OnDragStop)
517 end
518 else
519 self:EnableMouse(false)
520 end
521 30
522 -- establish internal storage 31 vn.db = VeneerData
523 if not V.Conf[name] then
524 V.Conf[name] = {
525 enabled = self.enabled,
526 }
527 end
528 local c = V.Conf[name]
529
530 -- establish position data; if undefined, round the API values
531 if not c.position then
532 local anchor, _, point, x, y = self:GetPoint(1)
533 x = floor(x+.5)
534 y = floor(y+.5)
535 print('obtained frame position', name, anchor, point, x, y)
536 c.position = {anchor, point, x, y}
537 else
538 print('restoring frame position', name, unpack(c.position))
539 self:ClearAllPoints()
540 local anchorTo, relativePoint, x, y = unpack(c.position)
541 self:SetPoint(anchorTo, UIParent, relativePoint, x, y)
542 end
543 self:Enable(c.enabled)
544
545
546 end 32 end