Mercurial > wow > buffalo2
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 |