Nenue@54: -------------------------------------------- Nenue@54: -- Veneer Nenue@54: -- Core Nenue@54: -- author: Krakyn Nenue@54: -- @project-revision@ @project-hash@ Nenue@54: -- @file-revision@ @file-hash@ Nenue@54: -- Created: 4/27/2016 1:02 AM Nenue@54: -------------------------------------------- Nenue@54: --- Implemented methods Nenue@0: -- OnInitialize Nenue@0: -- OnUpdate Nenue@54: -- OnEnable -- runs as soon as GetSpecialization() returns valid data Nenue@0: Nenue@0: local ADDON, A = ... Nenue@3: local wipe, min, max, random, tinsert, tremove = table.wipe, math.min, math.max, math.random, table.insert, table.remove Nenue@0: local pairs, ipairs, select, unpack, _G = pairs, ipairs, select, unpack, _G Nenue@0: local type, tostring, format = type, tostring, string.format Nenue@0: Nenue@54: --- Establish presence Nenue@54: Veneer = Veneer or CreateFrame('Frame', 'Veneer', UIParent) Nenue@54: local V = Veneer Nenue@54: A.frame = V Nenue@54: Nenue@54: --- Work variables Nenue@54: local modules = {} -- module collector Nenue@54: local queuedModules = {} -- indicates modules that were encountered out of dependency order Nenue@54: local checkForConfig = {} -- indicates frames created from XML that use their own namespace for position control Nenue@54: local moduleStack = {} -- dictates the order in which module methods are fired Nenue@54: local initOnced -- internal check for doing bottom-up SV retrieval Nenue@0: Nenue@37: --- Utilities Nenue@54: V.wipeall = function (...) Nenue@37: for i = 1, select('#', ...) do Nenue@37: wipe(select(i, ...)) Nenue@37: end Nenue@37: end Nenue@37: Nenue@0: --- Various region categories Nenue@54: V.displays = {} Nenue@54: V.configLayers = {} Nenue@54: V.configLayersRef = {} Nenue@0: Nenue@37: Nenue@54: --- Returns a debug hook for adding generic module information to each message Nenue@54: -- @usage func = V.print(sig) Nenue@0: -- @param sig channel name or number Nenue@54: local debugstack = _G.debugstack Nenue@54: local Devian = _G.Devian Nenue@0: local printfuncs = {} Nenue@54: V.print = function(pref, ...) Nenue@0: if Devian and Devian.InWorkspace() then Nenue@0: printfuncs[pref] = printfuncs[pref] or function(...) print(pref, ...) end Nenue@0: Nenue@0: return printfuncs[pref] Nenue@0: else Nenue@0: return function () end Nenue@0: end Nenue@0: end Nenue@0: Nenue@54: --@debug@ Nenue@0: local rgb = {} Nenue@0: local getcolor = function() Nenue@0: local n, p = 0, 4 Nenue@0: for i = 1, 3 do Nenue@0: rgb[i] = min(random(n,p) * 64, 255) Nenue@0: if rgb[i] == 255 then Nenue@0: p = 4 Nenue@0: elseif rgb[i] > 0 then Nenue@0: n = 2 Nenue@0: end Nenue@0: end Nenue@0: return unpack(rgb) Nenue@0: end Nenue@0: Nenue@0: local color = {} Nenue@0: local fprints = {} Nenue@54: --- Attempts to generate a debug printer based on the local scope. Results vary by where the originator was invoked. Nenue@54: V.fprint = function() Nenue@0: if not (Devian and Devian.InWorkspace()) then Nenue@0: return function() end Nenue@0: end Nenue@0: Nenue@0: local sig = debugstack(2,1) Nenue@0: if fprints[sig] then Nenue@0: return fprints[sig] Nenue@0: end Nenue@0: Nenue@0: local func = sig:match("%`(%a+)%'") Nenue@0: if not func then Nenue@0: func = sig:match("<(.-)>") Nenue@0: end Nenue@0: func = func:gsub('(%l+)(%u)', function(a, b) return a:sub(0,2) .. b end, 1) Nenue@0: func = func:gsub('^.+%\\', '') Nenue@0: if not func then Nenue@0: func = 'noname' Nenue@0: end Nenue@0: Nenue@0: local r, g, b = getcolor() Nenue@0: color[sig] = color[sig] or format('|cFF%02X%02X%02X%s|r', r, g, b, func) Nenue@0: Nenue@0: --print(color[func] .. ' ( ' .. table.concat(args, ', ')..' )' ) Nenue@54: func = V.print(func) Nenue@0: fprints[sig] = func Nenue@0: return func Nenue@0: end Nenue@0: Nenue@0: --@end-debug@ Nenue@0: --[=[@non-debug@ Nenue@54: V.print = function() end Nenue@0: --@end-non-debug@]=] Nenue@0: Nenue@0: -- for the Mikk script Nenue@0: -- GLOBALS: NUM_LE_RAID_BUFF_TYPES Nenue@0: -- GLOBALS: BUFF_FLASH_TIME_ON, BUFF_FLASH_TIME_OFF, BUFF_MIN_ALPHA, BUFF_WARNING_TIME, BUFF_DURATION_WARNING_TIME Nenue@0: -- GLOBALS: BUFFS_PER_ROW, BUFF_MAX_DISPLAY, BUFF_ACTUAL_DISPLAY, DEBUFF_MAX_DISPLAY, DEBUFF_ACTUAL_DISPLAY, BUFF_ROW_SPACING Nenue@0: -- GLOBALS: CONSOLIDATED_BUFFS_PER_ROW, CONSOLIDATED_BUFF_ROW_HEIGHT, NUM_TEMP_ENCHANT_FRAMES Nenue@0: -- GLOBALS: BUFF_BUTTON_HEIGHT, BUFF_FRAME_BASE_EXTENT, BUFF_HORIZ_SPACING Nenue@0: Nenue@54: local print = V.print('Bfl') Nenue@0: Nenue@0: --- Template for making perpendicular traversals of the displays structure; also makes sure the table is there Nenue@54: local setmetatable = setmetatable Nenue@54: V.Abstract = function(dest, key, table) Nenue@0: if table then Nenue@0: for _, v in pairs(dest) do Nenue@0: v[key] = {} Nenue@0: end Nenue@0: end Nenue@54: V[key] = setmetatable({}, { Nenue@0: __index = function(t, k) Nenue@0: return dest[k][key] Nenue@0: end, Nenue@0: __newindex = function(_, k, v) Nenue@0: print('abstract write ('..key..'):', k) Nenue@0: dest[k][key] = v Nenue@0: end, Nenue@0: __tostring = function() return 'Abstract:'..key..'' end Nenue@0: }) Nenue@0: Nenue@0: Nenue@54: return V[key] Nenue@0: end Nenue@0: Nenue@0: Nenue@54: --- internal Nenue@24: local ModulesCall = function(func, flag) Nenue@0: local n = 0 Nenue@0: for i = 1, #moduleStack do Nenue@0: print('calling level '..i) Nenue@0: local stackset = moduleStack[i] Nenue@0: for name, module in pairs(stackset) do Nenue@0: n = n + 1 Nenue@0: if module[func] then Nenue@24: -- nil = pass Nenue@54: if not flag or (module.Conf and module.Conf[flag]) then Nenue@24: if (flag) then Nenue@24: print(' check', flag, '=', module.Conf[flag]) Nenue@24: end Nenue@24: Nenue@24: print(' ',n..' '..name..'.'..func..'()') Nenue@24: module[func](module, module.Conf) Nenue@24: end Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: Nenue@0: local Enable = function() Nenue@0: end Nenue@0: Nenue@0: --- The things that happen repeatedly Nenue@0: local Init = function () Nenue@0: end Nenue@0: Nenue@0: Nenue@54: local layers, refs, displays = V.configLayers, V.configLayersRef, V.displays Nenue@0: --- Things that happen immediately upon entering world Nenue@0: local InitOnce = function() Nenue@0: print('entering world first time') Nenue@48: local defaults = {} Nenue@0: print('|cFFFFFF00Veneer|r') Nenue@0: if not VeneerData then Nenue@0: VeneerData = {} Nenue@0: for k,v in pairs(defaults) do Nenue@0: VeneerData[k] = v Nenue@0: end Nenue@0: print('Veneer defaults being used.') Nenue@0: end Nenue@54: V.Conf = setmetatable(VeneerData, {__index = function(_, k) return defaults[k] end}) Nenue@0: Nenue@0: -- To ensure that modules are run in controlled order, walk the dependency list; if the dep shows up Nenue@0: -- in the loaded manifest, remove the value. If the dep list isn't empty, move that module to the next Nenue@0: -- layer. Nenue@0: local loaded = {} Nenue@0: local stackLevels = #moduleStack Nenue@0: local i = 1 Nenue@0: moduleStack[1] = modules Nenue@0: repeat Nenue@0: print('setting init level '.. i) Nenue@0: local queue = moduleStack[i] Nenue@0: for name, module in pairs(queue) do Nenue@0: Nenue@0: if queuedModules[name] and #queuedModules[name] > 0 then Nenue@0: local p = #queuedModules[name] Nenue@0: for j = 1, p do Nenue@0: local dep = queuedModules[name][j] Nenue@0: Nenue@0: if loaded[dep] then Nenue@0: print( ' ' .. dep .. ' OK') Nenue@0: queuedModules[name][j] = nil Nenue@0: for k = j, p do Nenue@0: print(' shift ' .. (k+1) .. ' ('..tostring(queuedModules[name][k+1])..') to ' .. k ..'') Nenue@0: queuedModules[name][k] = queuedModules[name][k+1] Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: if #queuedModules[name] == 0 then Nenue@0: queuedModules[name] = nil Nenue@0: print(' |cFF00FFFF'.. name ..'|r deps OK') Nenue@0: loaded[name] = true Nenue@0: else Nenue@0: Nenue@0: print(' |cFFFF8800' .. name ..'|r pending') Nenue@0: local next = i+1 Nenue@0: if not moduleStack[next] then Nenue@0: moduleStack[next] = {} Nenue@0: end Nenue@0: stackLevels = next Nenue@0: moduleStack[next][name] = module Nenue@0: queue[name] = nil Nenue@0: end Nenue@0: Nenue@0: else Nenue@0: print(' |cFF00FF00'.. name ..'|r no deps') Nenue@0: loaded[name] = true Nenue@0: end Nenue@0: end Nenue@0: i = i + 1 Nenue@0: until i > stackLevels Nenue@0: Nenue@0: for level, batch in ipairs(moduleStack) do Nenue@0: print('config level', level) Nenue@0: for name, module in pairs(batch) do Nenue@24: if not VeneerData[name] then Nenue@24: VeneerData[name] = {} Nenue@24: end Nenue@24: Nenue@14: if module.defaults then Nenue@14: print('setting defaults for module', name) Nenue@14: --[===[@non-debug@ Nenue@14: if not VeneerData[name] then Nenue@14: --@end-non-debug@]===] Nenue@14: VeneerData[name] = {} Nenue@14: --[===[@non-debug@ Nenue@14: end Nenue@14: --@end-non-debug@]===] Nenue@14: for k,v in pairs(module.defaults) do Nenue@14: VeneerData[name][k] = v Nenue@14: end Nenue@14: module.Conf = VeneerData[name] Nenue@14: end Nenue@0: Nenue@24: if VeneerData[name].enabled == nil then Nenue@24: VeneerData[name].enabled = true Nenue@24: end Nenue@24: Nenue@0: end Nenue@0: end Nenue@3: Nenue@48: --- Pull in any XML templates Nenue@3: if #checkForConfig >= 1 then Nenue@3: local queuedFrame = tremove(checkForConfig) Nenue@3: while queuedFrame do Nenue@54: V.SetConfigLayers(queuedFrame) Nenue@54: V.UpdateXMLFrame(queuedFrame) Nenue@3: queuedFrame = tremove(checkForConfig) Nenue@3: end Nenue@3: end Nenue@0: end Nenue@0: Nenue@0: --- Fires an update to all modules Nenue@0: local lastUpdate Nenue@54: function V.UpdateAll(...) Nenue@0: lastUpdate = GetTime() Nenue@24: ModulesCall('OnUpdate') Nenue@0: end Nenue@0: Nenue@54: V:RegisterEvent('PLAYER_ENTERING_WORLD') Nenue@54: V:SetScript('OnEvent', function(self, event) Nenue@0: if event == 'PLAYER_ENTERING_WORLD' then Nenue@0: if not initOnced then Nenue@0: InitOnce() Nenue@0: ModulesCall('OnInitialize') Nenue@0: initOnced = true Nenue@0: C_Timer.After(1, function() Nenue@0: if GetSpecialization() then Nenue@0: print(GetSpecialization(), 'enabling') Nenue@24: Nenue@24: ModulesCall('OnEnable', 'enabled') Nenue@54: V:SetScript('OnUpdate', nil) Nenue@0: end Nenue@0: end) Nenue@0: end Nenue@0: end Nenue@0: Nenue@54: V.UpdateAll() Nenue@24: Nenue@24: if event == 'PLAYER_ENTERING_WORLD' then Nenue@54: V.UpdateConfigLayers() Nenue@24: end Nenue@24: Nenue@0: end) Nenue@0: Nenue@0: --- Modulizer method Nenue@0: -- Nenue@54: function V:RegisterModule (name, module, ...) Nenue@0: if modules[name] then Nenue@0: print('pulling modules[|cFFFF8800'.. tostring(name) ..'|r]') Nenue@0: return modules[name] Nenue@0: end Nenue@0: Nenue@0: print('new module |cFF00BBFF'.. tostring(name) ..'|r') Nenue@0: if module then Nenue@0: if modules[name] then Nenue@0: error("Module table for '"..tostring(name).."' already exists.") Nenue@0: end Nenue@0: else Nenue@54: module = CreateFrame('Frame', 'Veneer' .. tostring(name) .. 'Handler', V, 'VeneerHandlerTemplate') Nenue@0: end Nenue@0: modules[name] = module Nenue@54: V[name] = module Nenue@0: if select('#', ...) >= 1 then Nenue@0: local numDeps = select('#', ...) Nenue@0: print(' '..numDeps..' deps detected') Nenue@0: for i = 1, numDeps do Nenue@0: local dep = select(i, ...) Nenue@0: -- means that init/enable funcs are ordered to run after deps do their things Nenue@0: queuedModules[name] = queuedModules[name] or {} Nenue@0: tinsert(queuedModules[name], dep) Nenue@0: print(' needs '..dep) Nenue@0: end Nenue@0: end Nenue@0: return module Nenue@0: end Nenue@0: Nenue@0: Nenue@54: V.SetConfigLayers = function(frame) Nenue@54: local print = V.fprint() Nenue@0: if not frame.config then Nenue@24: --print(frame:GetName(), 'has no config layers') Nenue@0: return Nenue@0: end Nenue@24: --print('Registering config layers from', frame:GetName()) Nenue@0: Nenue@0: for i, subframe in ipairs(frame.config) do Nenue@0: -- make sure there are no duplicates Nenue@0: if not refs[subframe] then Nenue@0: local key = #layers+1 Nenue@0: layers[key] = subframe Nenue@0: refs[subframe] = key Nenue@0: end Nenue@24: --print(' ', i, subframe:GetName()) Nenue@0: end Nenue@0: end Nenue@0: Nenue@54: V.RemoveConfigLayers = function(frame) Nenue@3: Nenue@54: local print = V.fprint() Nenue@0: print('|cFFFF0000RemoveConfigLayers', frame:GetName()) Nenue@0: for i, subframe in pairs(layers) do Nenue@0: if subframe:GetParent() == frame then Nenue@0: print('|cFFFF8800 ', subframe:GetParent():GetName(), '|cFFFFFF00', subframe:GetName()) Nenue@0: layers[i]:Hide() Nenue@0: layers[i] = nil Nenue@0: refs[subframe] = nil Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@54: V.ToggleGuideLayers = function() Nenue@54: local print = V.fprint() Nenue@54: local func = V.Conf.GuidesMode and 'Show' or 'Hide' Nenue@0: local numAnchors = 0 Nenue@0: Nenue@0: for id, region in pairs(layers) do Nenue@44: --print(id, region:GetName(), func) Nenue@0: region[func](region) Nenue@0: end Nenue@0: Nenue@44: --print('['..func..'] updated', #layers, 'regions,', numAnchors, 'frames') Nenue@3: end Nenue@54: V.UpdateConfigLayers = function() Nenue@48: print('|cFFFF0000', debugstack()) Nenue@54: V.ToggleGuideLayers() Nenue@48: end Nenue@3: Nenue@38: local XMLFrame_Enable = function(self, value) Nenue@24: local name = self:GetName() Nenue@54: local print = V.print('XML') Nenue@24: Nenue@54: if not V.Conf[name] then Nenue@54: V.Conf[name] = { Nenue@24: enabled = true Nenue@24: } Nenue@24: end Nenue@24: Nenue@24: print() Nenue@24: local enabled Nenue@24: if value == nil then Nenue@54: if V.Conf[name].enabled == nil then Nenue@24: print('toggle based on visibility') Nenue@24: enabled = (not self:IsVisible()) and true or false Nenue@24: else Nenue@54: print('toggle a config value =', V.Conf[name].enabled) Nenue@54: enabled = V.Conf[name].enabled Nenue@24: end Nenue@24: Nenue@24: enabled = (enabled ~= true) and true or false Nenue@24: else Nenue@24: print('use argument value', value) Nenue@24: enabled = value Nenue@24: end Nenue@24: Nenue@54: print('arg =', value, 'conf =', V.Conf[name].enabled, 'result=', enabled) Nenue@24: Nenue@54: V.Conf[name].enabled = enabled Nenue@24: Nenue@24: local stateFunc = enabled and 'Show' or 'Hide' Nenue@24: local eventFunc = enabled and 'OnToggle' or 'OnToggle' Nenue@47: --- taggled layers Nenue@47: if self.toggled then Nenue@47: for i, region in pairs(self.toggled) do Nenue@47: region[stateFunc](region) Nenue@47: end Nenue@24: end Nenue@47: --- toggle action Nenue@24: if self.OnToggle then Nenue@54: self:OnToggle(V.Conf[name].enabled) Nenue@24: end Nenue@47: --- do enable Nenue@54: if V.Conf[name].enabled then Nenue@24: if self.OnEnable then Nenue@24: self:OnEnable() Nenue@24: end Nenue@24: else Nenue@24: if self.OnDisable then Nenue@24: self:OnDisable() Nenue@24: end Nenue@24: end Nenue@24: end Nenue@3: --- Generic handlers for keeping track of XML-defined frames Nenue@54: local print = V.print('XML') Nenue@54: local prototypes = {} Nenue@54: prototypes.OnDragStart = function(self) Nenue@3: self.xA = self:GetLeft() Nenue@3: self.yA = self:GetBottom() Nenue@3: self.anchorTo, self.relativeTo, self.relativePoint, self.x, self.y = self:GetPoint(1) Nenue@3: print('acquire anchor', self:GetPoint(1)) Nenue@3: print(self:GetName(), 'start moving ('..self.x..', '..self.y..')') Nenue@3: self:StartMoving() Nenue@3: end Nenue@3: Nenue@54: prototypes.OnDragStop = function(self) Nenue@24: local name = self:GetName() Nenue@24: print(name, 'stop moving ('..self:GetLeft()..', '..self:GetBottom()..')') Nenue@3: local xB = self:GetLeft() - self.xA Nenue@3: local yB = self:GetBottom() - self.yA Nenue@3: print('storing anchor point', self.anchorTo, self.relativePoint, self.x + xB, self.y + yB) Nenue@3: self:StopMovingOrSizing() Nenue@54: V.Conf[name].position = {self.anchorTo, self.relativePoint, self.x + xB, self.y + yB} Nenue@54: V.UpdateXMLFrame(self) Nenue@38: end Nenue@38: Nenue@47: Nenue@54: V.RegisterModuleFrame = function(self, moduleName) Nenue@47: local name = self:GetName() Nenue@38: tinsert(checkForConfig, self) Nenue@38: self.Enable = XMLFrame_Enable Nenue@38: self.moduleName = moduleName Nenue@44: print('|cFF00FF00XML stuff related to '.. tostring(moduleName) .. ':', self:GetName()) Nenue@47: ------------------------------------------------------------------------------------------ Nenue@54: if not V[name] then Nenue@47: return Nenue@47: end Nenue@47: Nenue@47: local scriptTypes = {'OnUpdate', 'OnEvent', 'OnDragStart', 'OnDragStop'} Nenue@47: for script in next(scriptTypes) do Nenue@54: if V[name][script] then Nenue@54: self:SetScript(script, V[name][script]) Nenue@47: end Nenue@47: end Nenue@47: Nenue@38: end Nenue@49: local XMLFrame_OnDragStart = function() end Nenue@49: local XMLFrame_OnDragStop = function() end Nenue@38: Nenue@54: V.UpdateXMLFrame = function(self) Nenue@54: local print = V.print('XML') Nenue@38: Nenue@38: local name = self:GetName() Nenue@38: Nenue@38: Nenue@58: Nenue@38: if self.drag then Nenue@38: self:RegisterForDrag('LeftButton') Nenue@54: self:SetScript('OnDragStart', prototypes.OnDragStart) Nenue@38: if self.OnDragStop then Nenue@38: self:SetScript('OnDragStop', function(self, ...) Nenue@55: print('|cFFFF0088end of dragging'). Nenue@38: self:OnDragStop(self, ...) Nenue@54: prototypes.OnDragStop(self, ...) Nenue@38: end) Nenue@38: else Nenue@54: self:SetScript('OnDragStop', prototypes.OnDragStop) Nenue@38: end Nenue@38: else Nenue@38: self:EnableMouse(false) Nenue@38: end Nenue@38: Nenue@55: -- establish internal storage Nenue@54: if not V.Conf[name] then Nenue@54: V.Conf[name] = { Nenue@38: enabled = self.enabled, Nenue@38: } Nenue@38: end Nenue@54: local c = V.Conf[name] Nenue@38: Nenue@55: -- establish position data; if undefined, round the API values Nenue@38: if not c.position then Nenue@47: local anchor, _, point, x, y = self:GetPoint(1) Nenue@55: x = floor(x+.5) Nenue@55: y = floor(y+.5) Nenue@55: print('obtained frame position', name, anchor, point, x, y) Nenue@47: c.position = {anchor, point, x, y} Nenue@38: else Nenue@55: print('restoring frame position', name, unpack(c.position)) Nenue@38: self:ClearAllPoints() Nenue@38: local anchorTo, relativePoint, x, y = unpack(c.position) Nenue@38: self:SetPoint(anchorTo, UIParent, relativePoint, x, y) Nenue@38: end Nenue@38: self:Enable(c.enabled) Nenue@38: Nenue@38: Nenue@38: end