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() Nenue@84: Nenue@84: SLASH_VENEER1 = "/veneer" Nenue@84: SLASH_VENEER2 = "/vn" Nenue@84: Nenue@84: SlashCmdList.VENEER = function(cmd) Nenue@84: end Nenue@88: Nenue@84: VeneerCore = { Nenue@84: Frames = {}, Nenue@84: ConfigLayers = {}, Nenue@84: FrameClusters = {}, Nenue@84: parserDepth = 0, Nenue@84: pendingCalls = {}, Nenue@84: } Nenue@84: VeneerHandlerMixin = { Nenue@88: Nenue@87: anchorPoint = 'CENTER', -- indicates the initial cluster group point Nenue@87: --anchorPath = 'BOTTOM', -- indicates the point from which the frame is anchored in a cluster arrangement Nenue@88: OnHide = function() Nenue@88: Veneer:DynamicReanchor() Nenue@88: end, Nenue@88: OnShow = function(self) Nenue@88: self:Reanchor() Nenue@88: Veneer:StaticReanchor(self) Nenue@88: end Nenue@84: } Nenue@88: VeneerAnimationMixin = {} Nenue@84: local print = DEVIAN_WORKSPACE and function(...) print('Veneer', ...) end or nop Nenue@80: local wipe = table.wipe Nenue@0: Nenue@59: local defaults = { Nenue@59: enableAll = true, Nenue@59: enableModule = { Nenue@59: BuffFrame = true, Nenue@59: }, Nenue@59: BuffFrame = { Nenue@59: width = 48, Nenue@59: height = 48, Nenue@59: } Nenue@59: } Nenue@84: Nenue@71: local configMode Nenue@79: local anonID = 0 Nenue@79: local tostring = tostring 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: Nenue@84: function VeneerCore: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: Nenue@84: function VeneerCore:OnLoad() Nenue@84: print('|cFFFFFF00Veneer!|r') Nenue@84: self:RegisterEvent('ADDON_LOADED') Nenue@84: self:RegisterEvent('PLAYER_LOGIN') Nenue@84: Nenue@84: self.DEVIAN_PNAME = 'Veneer' Nenue@84: self:RegisterForDrag('LeftButton') Nenue@88: Nenue@88: Nenue@84: end Nenue@84: Nenue@84: function VeneerCore:OnEvent(event, ...) Nenue@84: if event == 'ADDON_LOADED' or event == 'PLAYER_LOGIN' then Nenue@84: if IsLoggedIn() and not self.intialized then Nenue@84: self:Setup() Nenue@87: self:UnregisterEvent('ADDON_LOADED') Nenue@87: self:UnregisterEvent('PLAYER_LOGIN') Nenue@87: self:Reanchor() Nenue@87: self:Update() Nenue@84: end Nenue@84: end Nenue@84: end Nenue@84: Nenue@84: function VeneerCore:OnDragStart() Nenue@84: self:StartMoving() Nenue@84: end Nenue@84: Nenue@84: function VeneerCore:OnDragStop() Nenue@84: self:StopMovingOrSizing() Nenue@84: end Nenue@84: Nenue@84: function VeneerCore:Setup () Nenue@87: self.initialized = true Nenue@84: if (not VeneerData) or (not VeneerData.version) then Nenue@84: VeneerData = defaults Nenue@84: end Nenue@84: self.data = VeneerData Nenue@87: self:ExecuteOnClusters(nil, 'Setup') Nenue@87: end Nenue@84: Nenue@84: Nenue@87: function VeneerCore:GetClusterFromArgs (...) Nenue@87: local primaryAnchor Nenue@87: local insertPosition Nenue@87: local clusterTable = self.FrameClusters Nenue@87: for i = 1, select('#', ...) do Nenue@87: local arg = select(i, ...) Nenue@87: local argType = type(arg) Nenue@87: if argType == 'string' then Nenue@87: if not primaryAnchor then Nenue@87: primaryAnchor = arg Nenue@87: end Nenue@87: clusterTable[arg] = clusterTable[arg] or {} Nenue@87: clusterTable = clusterTable[arg] Nenue@87: print(string.rep(' ', i)..'anchor cluster', i, arg) Nenue@87: elseif argType == 'boolean' then Nenue@87: insertPosition = 1 Nenue@87: end Nenue@87: end Nenue@87: if not primaryAnchor then Nenue@87: primaryAnchor = 'TOPLEFT' Nenue@87: end Nenue@87: if not insertPosition then Nenue@87: insertPosition = #clusterTable + 1 Nenue@87: end Nenue@87: return primaryAnchor, clusterTable, insertPosition Nenue@84: end Nenue@84: Nenue@84: function VeneerCore:AddHandler(handler, ...) Nenue@84: print('*** Adding handler:', handler.moduleName or handler:GetName()) Nenue@87: Nenue@87: local anchorGroup, clusterTable, clusterIndex = self:GetClusterFromArgs(...) Nenue@87: if clusterIndex == 1 then Nenue@87: for i, frame in ipairs(clusterTable) do Nenue@87: frame.clusterIndex = i + 1 Nenue@87: end Nenue@84: end Nenue@87: tinsert(clusterTable, clusterIndex, handler) Nenue@87: print('cluster', anchorGroup, 'table', clusterTable, 'position', clusterIndex) Nenue@87: Nenue@87: handler.anchorCluster = clusterTable Nenue@87: handler.anchorIndex = clusterIndex Nenue@84: for k,v in pairs(VeneerHandlerMixin) do Nenue@84: if not handler[k] then Nenue@87: print(' * from mixin:', k) Nenue@84: handler[k] = v Nenue@84: end Nenue@84: end Nenue@87: if self.initialized then Nenue@87: print(' -- doing initialization') Nenue@87: if handler.Setup and not handler.initialized then Nenue@87: handler:Setup() Nenue@87: handler.initialized = true Nenue@87: end Nenue@87: self:InternalReanchor(handler) Nenue@87: end Nenue@87: end Nenue@87: Nenue@87: function VeneerCore:Reanchor() Nenue@87: self:ExecuteOnClusters(nil, 'Reanchor') Nenue@88: self:DynamicReanchor(self) Nenue@87: end Nenue@87: Nenue@87: function VeneerCore:Update() Nenue@87: self:ExecuteOnClusters(nil, 'Update') Nenue@88: self:Reanchor() Nenue@87: end Nenue@87: Nenue@87: -- updates anchor relations to and from the target handler Nenue@87: function VeneerCore:GetAnchor(...) Nenue@87: Nenue@87: end Nenue@87: Nenue@88: -- Evaluates frames visibility and chains them accordingly Nenue@88: Nenue@88: function VeneerCore:DynamicReanchor(parent) Nenue@88: parent = parent or self Nenue@88: print('|cFF88FF00DynamicReanchor()') Nenue@88: for anchorPoint, cluster in pairs(parent.FrameClusters) do Nenue@88: local lastFrame Nenue@88: for index, frame in ipairs(cluster) do Nenue@88: print(' |cFF00FF00'..index, frame:GetName(), frame:IsVisible()) Nenue@88: if frame:IsVisible() then Nenue@88: anchorPoint = frame.anchorPoint Nenue@88: frame:ClearAllPoints() Nenue@88: if lastFrame then Nenue@88: frame:SetPoint(anchorPoint, lastFrame, ANCHOR_OFFSET_POINT[anchorPoint], 0, 0) Nenue@88: else Nenue@88: frame:SetPoint(anchorPoint, UIParent, anchorPoint, 0, 0) Nenue@88: end Nenue@88: lastFrame = frame Nenue@88: end Nenue@88: Nenue@88: end Nenue@88: end Nenue@88: end Nenue@88: Nenue@88: -- Evaluates the current visibility state and re-anchors adjacent blocks accordingly Nenue@87: function VeneerCore:InternalReanchor(handler, printFunc) Nenue@87: print('|cFF00FFFFVeneer:InternalReanchor('..handler:GetName()..')') 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()) Nenue@88: if frame:IsVisible() 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: Nenue@88: if handler:IsVisible() 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: Nenue@88: function VeneerCore:SlideBlock(frame, ...) 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@88: Nenue@84: function VeneerCore:ExecuteOnClusters(layer, method) 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@87: print('|cFF00FF00Veneer:ExecuteOnClusters|r('..tostring(layer)..', '..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@84: if 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: local VeneerButton_OnDragStart = function(self) Nenue@72: self.startingLeft = self:GetLeft() Nenue@72: self.startingBottom = self:GetBottom() Nenue@72: self.anchors = self.anchors or {} Nenue@72: table.wipe(self.anchors) Nenue@72: Nenue@72: local frame = self:GetParent() Nenue@72: local n = frame:GetNumPoints() Nenue@72: for i = 1, n do Nenue@72: local anchor, parent, relative, x, y = frame:GetPoint(i) Nenue@72: self.anchors[i] = { Nenue@72: anchor = anchor, Nenue@72: parent = parent, Nenue@72: relative = relative, Nenue@72: x = x, Nenue@72: y = y Nenue@72: } Nenue@72: end Nenue@72: Nenue@72: print(self:GetName(), 'start moving', self.startingLeft, self.startingBottom) Nenue@72: self:StartMoving() Nenue@72: end Nenue@72: Nenue@72: local VeneerButton_OnDragStop = function(self) Nenue@72: self:StopMovingOrSizing() Nenue@72: if self.OnDragStop then Nenue@72: self.OnDragStop(self) Nenue@72: else Nenue@72: local frame = self:GetParent() Nenue@72: local dx = self:GetLeft() - self.startingLeft Nenue@72: local dy = self:GetBottom() - self.startingBottom Nenue@72: Nenue@72: frame:ClearAllPoints() Nenue@72: for i, point in ipairs(self.anchors) do Nenue@72: frame:SetPoint(point.anchor, point.parent, point.relative, point.x + dx, point.y + dy) Nenue@72: print('adjusting anchor', point.anchor, point.parent, point.relative, point.x + dx, point.y + dy) Nenue@72: end Nenue@72: end Nenue@72: end Nenue@72: Nenue@72: local Veneer_FixMovers = function() Nenue@72: for frame, veneer in pairs(veneers) do Nenue@72: if veneer:IsMoving() then Nenue@72: VeneerButton_OnDragStop(veneer) Nenue@72: end Nenue@72: end Nenue@72: end Nenue@71: Nenue@71: local VeneerButton_Update = function(self) Nenue@71: if configMode then Nenue@72: self:SetScript('OnDragStart', VeneerButton_OnDragStart) Nenue@72: self:SetScript('OnDragStop', VeneerButton_OnDragStop) Nenue@72: self:SetMovable(true) Nenue@72: self:EnableMouse(true) Nenue@71: self:RegisterForDrag('LeftButton') Nenue@71: Nenue@71: self.bg:SetColorTexture(0,1,0,0.5) Nenue@72: for i, region in ipairs(self.configLayers) do Nenue@72: region:Show() Nenue@72: end Nenue@72: self:Show() Nenue@71: else Nenue@71: Nenue@71: self:SetScript('OnDragStart', self.StartMoving) Nenue@71: self:SetScript('OnDragStop', self.StopMovingOrSizing) Nenue@71: self:SetMovable(false) Nenue@71: self:EnableMouse(false) Nenue@71: Nenue@71: self.bg:SetColorTexture(0,1,0,0) Nenue@72: for i, region in ipairs(self.configLayers) do Nenue@72: region:Hide() Nenue@72: end Nenue@72: if self.isHidden then Nenue@72: self:Hide() Nenue@72: end Nenue@72: Nenue@71: end Nenue@71: end Nenue@71: Nenue@71: local ToggleVeneerConfig = function() Nenue@71: if configMode then Nenue@71: configMode = false Nenue@84: Veneer:print('Config mode off.') Nenue@71: else Nenue@71: configMode = true Nenue@84: Veneer:print('Config mode on.') Nenue@71: end Nenue@71: Nenue@71: for frame, veneer in pairs(veneers) do Nenue@71: VeneerButton_Update(veneer) Nenue@71: end Nenue@71: end Nenue@71: Nenue@71: local VeneerButton_OnShow = function(self) Nenue@71: VeneerButton_Update(self) Nenue@71: end Nenue@71: Nenue@88: -- Takes frame handle and assigns a block to it Nenue@84: function VeneerCore:Acquire (frame, template) 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@84: local name = type(frame) == 'table' and GetTableName(frame) or GetAnonymousName() Nenue@84: veneer = CreateFrame('Frame', name, frame, template or 'VeneerTemplate') Nenue@84: print('+veneer', name) 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: Nenue@84: veneer:SetScript('OnShow', VeneerButton_OnShow) Nenue@84: 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@71: Nenue@84: return veneer Nenue@71: end Nenue@0: Nenue@88: function VeneerHandlerMixin:Reanchor (anchorAll) Nenue@88: if not anchorAll then Nenue@88: Veneer:InternalReanchor(self) Nenue@88: end Nenue@80: Nenue@88: end Nenue@80: Nenue@88: function VeneerAnimationMixin:OnPlay() Nenue@88: PlaySoundKitID(229) Nenue@89: self.animating = true Nenue@88: print('|cFF00FF00Anim:OnPlay|r @', unpack(self.sourcePoint)) Nenue@88: end Nenue@88: function VeneerAnimationMixin:OnStop() Nenue@88: PlaySoundKitID(229) Nenue@89: self.animating = nil Nenue@89: print('|cFF00FF00Anim:OnFinish|r @', unpack(self.destPoint)) Nenue@88: end Nenue@88: function VeneerAnimationMixin:OnFinished() Nenue@88: PlaySoundKitID(229) Nenue@89: self.animating = nil Nenue@88: print('|cFF00FF00Anim:OnFinish|r @', unpack(self.destPoint)) Nenue@88: end