Nenue@88: -- Veneer Custom Interface Framework Nenue@88: -- 1. vn OnLoad Nenue@88: -- 2. OnEvent where IsLoggedIn() == true Nenue@88: -- 3. Setup() where (not self.initialized) Nenue@88: -- 4. Update() Nenue@88: -- 5. Reanchor() Nick@109: local ADDON, Veneer = ... Nenue@90: local VENEER_VERSION = 703 Nenue@103: local LE_FREE_FRAMES_GROUP = 1 Nenue@93: local type, strrep, ipairs, tinsert, tostring, select = type, string.rep, ipairs, tinsert, tostring, select Nenue@93: local pairs, tremove = pairs, tremove Nick@109: local print = DEVIAN_WORKSPACE and function(...) _G.print('Veneer', ...) end or nop Nick@113: local eprint = DEVIAN_WORKSPACE and function(...) _G.print('VeneerEvent', ...) end or nop Nick@109: local wipe = table.wipe Nenue@84: Nick@109: SLASH_VENEER1 = "/veneer" Nick@109: SLASH_VENEER2 = "/vn" Nenue@84: SlashCmdList.VENEER = function(cmd) Nenue@90: if Veneer.ConfigMode then Nenue@90: Veneer.ConfigMode = false Nenue@90: else Nenue@90: Veneer.ConfigMode = true Nenue@90: end Nenue@90: Veneer:UpdateConfigLayers() Nenue@84: end Nenue@88: Nenue@115: Veneer.modules = {} Nick@109: Veneer.Frames = {} Nick@109: Veneer.ConfigLayers = {} Nick@109: Veneer.FrameClusters = { Nick@109: [LE_FREE_FRAMES_GROUP] = {} Nick@109: } Nick@109: Veneer.parserDepth = 0 Nick@109: Veneer.pendingCalls = {} Nick@109: Veneer.AddOnCheck = {} Nenue@0: Nenue@59: local defaults = { Nenue@59: enableAll = true, Nenue@90: ConfigMode = true Nenue@59: } Nenue@84: Nenue@71: local configMode Nenue@79: local anonID = 0 Nenue@79: local IsFrameHandle = IsFrameHandle Nenue@79: local GetAnonymousName = function(key) Nenue@79: if not key then Nenue@71: anonID = anonID + 1 Nenue@79: key = anonID Nenue@71: end Nenue@79: return 'VN' .. key Nenue@71: end Nenue@79: local GetTableName = function(table) Nenue@79: return (IsFrameHandle(table) and table:GetName()) or tostring(table) Nenue@79: end Nenue@79: Nenue@87: local OFFSET_PARALLELS = { Nenue@87: TOP = {'LEFT', 'RIGHT', 'SetHeight'}, Nenue@87: BOTTOM = {'LEFT', 'RIGHT', 'SetHeight'}, Nenue@87: LEFT = {'TOP', 'BOTTOM', 'SetWidth'}, Nenue@87: RIGHT = {'TOP', 'BOTTOM', 'SetWidth'}, Nenue@87: } Nenue@87: local ANCHOR_OFFSET_POINT = { Nenue@87: TOP = 'BOTTOM', Nenue@87: TOPLEFT = 'BOTTOMRIGHT', Nenue@87: TOPRIGHT = 'BOTTOMLEFT', Nenue@87: LEFT = 'RIGHT', Nenue@87: RIGHT = 'LEFT', Nenue@87: CENTER = 'CENTER', Nenue@87: BOTTOM = 'TOP', Nenue@87: BOTTOMRIGHT = 'TOPLEFT', Nenue@87: BOTTOMLEFT = 'TOPRIGHT', Nenue@87: } Nenue@87: local ANCHOR_INSET_DELTA = { Nenue@87: TOP = {0, -1}, Nenue@87: TOPLEFT = {1, -1}, Nenue@87: TOPRIGHT = {-1,-1}, Nenue@87: LEFT = {1, 0}, Nenue@87: BOTTOMLEFT = {1, 1}, Nenue@87: BOTTOM = {0, 1}, Nenue@87: BOTTOMRIGHT = {-1, 1}, Nenue@87: RIGHT = {-1, 0}, Nenue@87: CENTER = {0, 0}, Nenue@72: } Nenue@72: Nick@109: function Veneer:print(...) Nenue@84: local txt = '|cFFFFFF00Veneer|r:' Nenue@84: for i = 1, select('#', ...) do Nenue@84: txt = txt .. ' '.. tostring(select(i, ...)) Nenue@84: end Nenue@84: Nenue@84: DEFAULT_CHAT_FRAME:AddMessage(txt) Nenue@84: end Nenue@84: Nick@109: function Veneer:OnLoad() Nenue@84: print('|cFFFFFF00Veneer!|r') Nenue@84: self:RegisterEvent('ADDON_LOADED') Nenue@84: self:RegisterEvent('PLAYER_LOGIN') Nenue@115: self:RegisterEvent('PLAYER_REGEN_ENABLED') Nenue@115: self:RegisterEvent('PLAYER_REGEN_DISABLED') Nenue@84: Nenue@84: self.DEVIAN_PNAME = 'Veneer' Nenue@84: self:RegisterForDrag('LeftButton') Nenue@88: Nenue@88: Nenue@84: end Nenue@84: Nenue@90: local select, IsAddOnLoaded, IsLoggedIn = select, IsAddOnLoaded, IsLoggedIn Nenue@90: Nick@109: function Veneer:OnEvent(event, ...) Nick@113: local print = eprint Nenue@97: print('|cFFFF0088OnEvent()|r',event, ...) Nenue@98: if (event == 'PLAYER_LOGIN') or (event == 'ADDON_LOADED') then Nenue@90: print(IsLoggedIn(), self.initialized) Nenue@84: if IsLoggedIn() and not self.intialized then Nenue@84: self:Setup() Nenue@90: self.intialized = true Nenue@90: print('popping init sequence', self.intialized) Nenue@90: end Nenue@90: Nenue@90: Nenue@90: if self.intialized then Nenue@90: local addon = ... Nenue@90: if self.AddOnCheck[addon] then Nenue@90: print(' - setting up '..addon..' dependent modules:') Nenue@90: local keepChecking = false Nenue@90: for index, handler in ipairs(self.AddOnCheck[addon]) do Nenue@90: print(' -', handler:GetName(), (not handler.initialized) and (handler.addonFrame and not _G[handler.addonFrame])) Nenue@90: if not handler.initialized then Nenue@90: print(' '..handler:GetName()..':Setup()') Nenue@90: handler:Setup() Nenue@90: handler.initialized = true Nenue@90: end Nenue@90: end Nenue@90: if not keepChecking then Nenue@90: self.AddOnCheck[addon] = nil Nenue@90: end Nenue@90: end Nenue@84: end Nenue@115: elseif event == 'PLAYER_REGEN_ENABLED' then Nenue@115: for _, module in pairs(self.modules) do Nenue@115: if module:IsShown() and module.hideCombat then Nenue@115: module:Hide() Nenue@115: end Nenue@115: end Nenue@115: Nenue@115: elseif event == 'PLAYER_REGEN_DISABLED' then Nenue@115: for _, module in pairs(self.modules) do Nenue@115: if module:IsShown() and module.hideCombat then Nenue@115: module:Show() Nenue@115: end Nenue@115: end Nenue@84: end Nenue@84: end Nenue@84: Nick@109: function Veneer:OnDragStart() Nenue@84: self:StartMoving() Nenue@84: end Nenue@84: Nick@109: function Veneer:OnDragStop() Nenue@84: self:StopMovingOrSizing() Nenue@84: end Nenue@84: Nenue@93: local VeneerModule_Setup = function(frame) Nenue@97: if not frame.initialized then Nenue@97: local doSetup = (not frame.addonTrigger) or select(2, IsAddOnLoaded(frame.addonTrigger)) Nenue@97: print(' '..frame:GetName()..'.doSetup =', doSetup) Nenue@97: if doSetup then Nenue@93: frame:Setup() Nenue@93: frame.initialized = true Nenue@98: else Nick@109: frame:RegisterEvent('ADDON_LOADED') Nenue@93: end Nenue@97: Nenue@93: end Nenue@93: end Nenue@93: Nick@109: function Veneer:Setup () Nenue@97: print('|cFFFF0088Setup()|r') Nenue@90: local resetConfig = (not VeneerData) Nenue@90: if (not VeneerData) then Nenue@84: VeneerData = defaults Nenue@90: VeneerData.version = VENEER_VERSION Nenue@84: end Nenue@84: self.data = VeneerData Nenue@93: self:ExecuteOnClusters(nil, VeneerModule_Setup) Nenue@90: Nenue@90: self.ConfigMode = VeneerData.ConfigMode Nenue@90: self:UpdateConfigLayers() Nenue@90: self:Reanchor() Nenue@90: self:Update() Nenue@87: end Nenue@84: Nick@109: function Veneer:UpdateConfigLayers() Nenue@90: if VeneerData then Nenue@90: Nenue@90: VeneerData.ConfigMode = self.ConfigMode Nenue@90: end Nenue@90: Nenue@90: self:print('Config mode '..(self.ConfigMode and '|cFF00FF00ON|r' or '|cFFFF0000OFF|r')..'.') Nenue@90: self:ExecuteOnClusters(nil, function(frame) Nenue@90: if frame.UpdateConfigLayers then Nenue@90: frame:UpdateConfigLayers(self.ConfigMode) Nenue@90: end Nenue@90: Nenue@90: Nenue@90: if type(frame.ConfigLayer) == 'table' then Nenue@90: for index, region in ipairs(frame.ConfigLayer) do Nenue@90: print('setting', frame:GetName() .. '['.. index..']', 'to', self.ConfigMode) Nenue@90: Nenue@90: region:SetShown(self.ConfigMode) Nenue@90: end Nenue@90: end Nenue@90: Nenue@90: self.ConfigLayers[frame] = frame:IsShown() Nenue@90: if self.ConfigMode then Nenue@90: print(frame:GetName(), self.ConfigLayers[frame]) Nenue@90: frame:SetShown(self.ConfigMode) Nenue@90: else Nenue@90: frame:SetShown(self.ConfigLayers[frame]) Nenue@90: end Nenue@90: end) Nenue@90: end Nenue@84: Nenue@93: Nick@109: function Veneer:GetClusterFromArgs (...) Nenue@87: return primaryAnchor, clusterTable, insertPosition Nenue@84: end Nenue@84: Nick@108: -- args: frame object, list of anchor groups, true for forced top, number for priority layer Nenue@115: local mixins = {} Nick@109: function Veneer:AddHandler(handler, ...) Nenue@115: print('|cFFFFFF00'..handler:GetName()..':|r', handler.moduleName, ...) Nenue@87: Nenue@115: wipe(mixins) Nick@108: for k,v in pairs(VeneerHandlerMixin) do Nick@108: if not handler[k] then Nenue@115: tinsert(mixins, k) Nick@108: handler[k] = v Nick@108: end Nick@108: end Nenue@115: if #mixins >= 1 then Nenue@115: print('* Mixins:|cFF00FF88', table.concat(mixins, ', ')) Nenue@115: end Nenue@115: Nenue@115: self.modules[handler] = handler Nick@108: Nenue@102: if not handler.anchorFrame then Nick@113: Nenue@115: local primaryAnchor = handler.anchorPoint or 'CENTER' Nenue@115: local clusterPriority = handler.anchorPriority Nenue@115: self.FrameClusters[primaryAnchor] = self.FrameClusters[primaryAnchor] or {} Nenue@115: local clusterTable = self.FrameClusters[primaryAnchor] Nick@113: local clusterIndex Nick@113: Nick@113: if clusterPriority then Nick@113: for i = 1, #clusterTable do Nenue@115: Nick@113: if clusterTable[i].anchorPriority and (clusterTable[i].anchorPriority > clusterPriority) then Nenue@115: clusterIndex = i Nick@113: print('|cFF00BB00insert position:', clusterPriority, clusterIndex) Nick@113: break Nick@113: else Nenue@115: print('pass', clusterTable[i]) Nenue@115: clusterIndex = i+1 Nick@113: end Nick@113: end Nick@113: else Nick@113: print('|cFF00BB00inserting at front') Nenue@115: clusterIndex = #clusterTable + 1 Nick@113: end Nick@113: Nick@113: if not clusterIndex then Nick@113: clusterIndex = #clusterTable + 1 Nick@113: end Nick@113: Nick@113: Nenue@90: tinsert(clusterTable, clusterIndex, handler) Nenue@115: print(' cluster', (clusterDepth or 1) .. '.' .. primaryAnchor, clusterTable, 'priority', clusterPriority, 'position', clusterIndex) Nenue@90: Nenue@102: handler.anchorCluster = clusterTable Nenue@102: handler.anchorIndex = clusterIndex Nenue@102: else Nenue@103: local clusterTable = self.FrameClusters[LE_FREE_FRAMES_GROUP] Nenue@103: handler.anchorCluster = clusterTable Nenue@103: handler.anchorIndex = #clusterTable+1 Nenue@103: tinsert(clusterTable, handler.anchorIndex, handler) Nenue@102: print(' free frame') Nenue@102: end Nenue@87: Nenue@90: if handler.addonTrigger and not IsAddOnLoaded(handler.addonTrigger) then Nenue@90: print('|cFFFF4400 -- dependency:', handler.addonTrigger) Nenue@90: self.AddOnCheck[handler.addonTrigger] = self.AddOnCheck[handler.addonTrigger] or {} Nenue@90: tinsert(self.AddOnCheck[handler.addonTrigger], handler) Nenue@90: end Nenue@90: Nenue@115: Nenue@87: if self.initialized then Nenue@90: print(' -- initialization check') Nenue@90: if handler.Setup then Nenue@90: local doInit = (not handler.initialized) Nenue@90: if handler.addonTrigger and not IsAddOnLoaded(handler.addonTrigger) then Nenue@90: doInit = false Nenue@90: end Nenue@90: -- room to add other checks Nenue@90: Nenue@90: if doInit then Nenue@90: handler:Setup() Nenue@90: handler.initialized = true Nenue@90: self:InternalReanchor(handler) Nenue@90: end Nenue@87: end Nenue@87: end Nenue@87: end Nenue@87: Nick@109: function Veneer:Reanchor() Nenue@87: self:ExecuteOnClusters(nil, 'Reanchor') Nenue@88: self:DynamicReanchor(self) Nenue@87: end Nenue@87: Nick@109: function Veneer:Update() Nenue@90: self:ExecuteOnClusters(nil, function(frame) Nenue@90: if frame.initialized and frame.Update then Nenue@90: frame:Update() Nenue@90: end Nenue@90: end) Nenue@88: self:Reanchor() Nenue@87: end Nenue@87: Nenue@87: -- updates anchor relations to and from the target handler Nick@109: function Veneer:GetAnchor(...) Nenue@87: Nenue@87: end Nenue@87: Nick@113: -- Recursives updates frame group anchors Nick@113: function Veneer:EvaluateAnchors(parent) Nenue@88: parent = parent or self Nick@113: local print = eprint Nenue@88: print('|cFF88FF00DynamicReanchor()') Nenue@88: for anchorPoint, cluster in pairs(parent.FrameClusters) do Nenue@103: if anchorPoint ~= LE_FREE_FRAMES_GROUP then Nenue@103: local lastFrame Nenue@103: for index, frame in ipairs(cluster) do Nick@108: print(' |cFF00FF00'..index, frame:GetName(), frame:IsShown(), (lastFrame and ('|cFFFFFF00'..lastFrame:GetName()..'|r') or '|cFF00FFFFUIParent')) Nick@108: if frame:IsShown() then Nenue@90: Nenue@103: if frame.anchorFrame then Nenue@103: print(frame.anchorPoint) Nenue@103: frame:SetPoint(frame.anchorPoint, frame.anchorFrame, frame.anchorFrom, frame.anchorX, frame.anchorY) Nick@112: print('anchored to', frame.anchorFrame,frame:GetTop(), frame:GetRight()) Nenue@90: else Nenue@103: anchorPoint = frame.anchorPoint or anchorPoint Nenue@103: frame:ClearAllPoints() Nenue@103: if lastFrame then Nenue@103: frame:SetPoint(anchorPoint, lastFrame, ANCHOR_OFFSET_POINT[anchorPoint], 0, 0) Nick@112: Nick@112: print('moved after', lastFrame,frame:GetTop(), frame:GetRight()) Nenue@103: else Nenue@103: frame:SetPoint(anchorPoint, UIParent, anchorPoint, frame.anchorX, frame.anchorY) Nick@112: print('stub', anchorPoint, frame.anchorX, frame.anchorY) Nenue@103: end Nenue@103: lastFrame = frame Nenue@90: end Nenue@103: Nenue@88: end Nenue@90: Nenue@88: end Nenue@103: end Nenue@88: Nenue@88: end Nenue@88: end Nenue@88: Nick@113: Veneer.DynamicReanchor = Veneer.EvaluateAnchors Nick@113: Nenue@88: -- Evaluates the current visibility state and re-anchors adjacent blocks accordingly Nick@109: function Veneer:InternalReanchor(handler, printFunc) Nick@113: local print = eprint Nenue@87: print('|cFF00FFFFVeneer:InternalReanchor('..handler:GetName()..')') Nenue@90: if handler.anchorFrame then Nenue@90: handler:SetPoint(handler.anchorPoint, handler.anchorFrame, handler.anchorFrom, handler.anchorX, handler.anchorY) Nenue@90: return Nenue@90: end Nenue@90: Nenue@90: Nenue@87: local anchorPoint = handler.anchorPath or handler.anchorPoint Nenue@87: local anchorParent, anchorTo = UIParent, anchorPoint Nenue@88: local subPoint, subTo Nenue@88: local nextFrame Nenue@88: for index, frame in ipairs(handler.anchorCluster) do Nenue@88: print(' |cFF00FF00'..index, frame:GetName(), frame:IsVisible()) Nick@108: if frame:IsShown() then Nenue@88: if frame ~= handler then Nenue@88: anchorParent = frame Nenue@88: anchorTo = ANCHOR_OFFSET_POINT[anchorPoint] Nenue@87: Nenue@88: else Nenue@88: nextFrame = handler.anchorCluster[index+1] Nenue@88: if nextFrame then Nenue@88: Nenue@88: subPoint = nextFrame.anchorPath or nextFrame.anchorPoint Nenue@88: subTo = ANCHOR_OFFSET_POINT[subPoint] Nenue@88: nextFrame:ClearAllPoints() Nenue@88: nextFrame:SetPoint(subPoint, handler, subTo, 0, 0) Nenue@88: print(' -- pushing '..nextFrame:GetName()..' down the anchor chain', subPoint, subTo) Nenue@88: end Nenue@88: break Nenue@87: end Nenue@87: end Nenue@87: end Nenue@87: Nick@108: if handler:IsShown() then Nenue@88: handler:SetPoint(anchorPoint, anchorParent, anchorTo, 0, 0) Nenue@88: else Nenue@88: if anchorParent and nextFrame then Nenue@88: nextFrame:SetPoint(subPoint, handler, subTo, 0, 0) Nenue@88: end Nenue@88: end Nenue@88: Nenue@87: Nenue@87: print(handler.anchorPoint, anchorParent, anchorTo) Nenue@87: if printFunc then Nenue@87: printFunc('|cFF88FF00'..handler:GetName()..':SetPoint(', handler.anchorPoint, anchorParent, anchorTo) Nenue@87: end Nenue@88: end Nenue@87: Nick@109: function Veneer:SlideBlock(frame, ...) Nick@113: local print = eprint Nenue@89: local aX, aY = frame:GetLeft(), frame:GetTop() Nenue@88: Nenue@89: frame:SetPoint('TOPLEFT', frame, 'BOTTOMLEFT', aX, aY) Nenue@89: frame.animation = frame.animation or {} Nenue@89: frame.animation.startX = aX Nenue@89: frame.animation.startY = aY Nenue@88: Nenue@89: local targetPoint, targetParent, targetAnchor, offsetX, offsetY = ... Nenue@89: frame.BlockSlide:SetScript('OnFinished', function() Nenue@89: frame:SetPoint(targetPoint, targetParent, targetAnchor, offsetX, offsetY) Nenue@89: VeneerAnimationMixin.OnFinished(frame) Nenue@89: end) Nenue@88: Nenue@84: end Nenue@84: Nenue@115: -- execute a function on all clusters in display order Nick@109: function Veneer:ExecuteOnClusters(layer, method) Nick@113: local print = eprint Nenue@84: self.parserDepth = self.parserDepth + 1 Nenue@84: if not layer then Nenue@87: if self.parserDepth > 1 then Nenue@84: tinsert(self.pendingCalls, method) Nenue@84: print('delaying walk for', method) Nenue@84: return Nenue@84: end Nenue@97: print('|cFF00FF00ExecuteOnClusters|r('..tostring(layer)..', '..tostring(method)..')') Nenue@84: else Nenue@87: print(' Level '..self.parserDepth) Nenue@84: end Nenue@87: Nenue@87: layer = layer or self.FrameClusters Nenue@84: for anchor, cluster in pairs(layer) do Nenue@84: for index, frame in ipairs(cluster) do Nenue@87: print(' '..anchor..'.'..index..' = '..frame:GetName()) Nenue@90: if type(method) == 'function' then Nenue@90: method(frame, true) Nenue@90: elseif frame[method] then Nenue@87: print(' |cFF00FF00'..frame:GetName()) Nenue@87: frame[method](frame, true) Nenue@84: end Nenue@84: end Nenue@84: if cluster.FrameClusters then Nenue@84: self:ExecuteOnClusters(cluster.FrameClusters, method) Nenue@84: end Nenue@84: end Nenue@84: self.parserDepth = self.parserDepth - 1 Nenue@84: Nenue@84: if (self.parserDepth == 0) and (#self.pendingCalls >= 1) then Nenue@84: local delayedMethod = tremove(self.pendingCalls, 1) Nenue@84: print('starting delayed walk for', delayedMethod) Nenue@84: self:ExecuteOnClusters(nil, delayedMethod) Nenue@84: end Nenue@84: end Nenue@84: Nenue@72: Nenue@71: Nenue@88: -- Takes frame handle and assigns a block to it Nick@109: function Veneer:Acquire (frame, template) Nick@113: local print = eprint Nenue@71: if not frame then Nenue@71: print('|cFFFF4400Unable to acquire frame...|r') Nenue@71: return Nenue@71: end Nenue@84: local veneer = self.Frames[frame] Nenue@84: if not veneer then Nenue@94: local name = GetAnonymousName() Nenue@94: veneer = CreateFrame('Frame', name, frame, template) Nenue@90: print(self:GetName()..':Acquire()', frame:GetName(), template) Nenue@71: Nenue@84: veneer:SetAllPoints(frame) Nenue@84: veneer:SetParent(frame) Nenue@84: veneer.label:SetText(name) Nenue@84: veneer.bg:SetColorTexture(0,0,0,0) Nenue@84: veneer:Hide() Nenue@84: veneer:EnableMouse(false) Nenue@84: -- find current X/Y Nenue@84: veneer.currentLeft = frame:GetLeft() Nenue@84: veneer.currentTop = frame:GetTop() Nenue@84: self.Frames[frame] = veneer Nenue@71: end Nenue@84: return veneer Nick@109: end Nick@109: Nick@109: VeneerCore = Veneer