view Veneer.lua @ 95:43303398d1b9

- Pre-load veneer frames for buffs, set their triggers when the appropriate frames have spawned. - Add border frames into the PETBATTLES frame lock table so they are hidden properly. Normal triggers don't work here.
author Nenue
date Sun, 01 Jan 2017 19:56:35 -0500
parents df10cd0ae949
children 5476337198ec
line wrap: on
line source
-- Veneer Custom Interface Framework
-- 1. vn OnLoad
-- 2. OnEvent where IsLoggedIn() == true
-- 3. Setup() where (not self.initialized)
-- 4. Update()
-- 5. Reanchor()

SLASH_VENEER1 = "/veneer"
SLASH_VENEER2 = "/vn"
local VENEER_VERSION = 703
local type, strrep, ipairs, tinsert, tostring, select = type, string.rep, ipairs, tinsert, tostring, select
local pairs, tremove = pairs, tremove

SlashCmdList.VENEER = function(cmd)

  if Veneer.ConfigMode then
    Veneer.ConfigMode = false
  else
    Veneer.ConfigMode = true
  end
  Veneer:UpdateConfigLayers()
end

VeneerCore = {
  Frames = {},
  ConfigLayers = {},
  FrameClusters = {},
  parserDepth = 0,
  pendingCalls = {},
  AddOnCheck = {}
}

local print = DEVIAN_WORKSPACE and function(...) _G.print('Veneer', ...) end or nop
local wipe = table.wipe

local defaults = {
  enableAll = true,
  enableModule = {
    BuffFrame = true,
  },
  BuffFrame = {
    width = 48,
    height = 48,
  },
  ConfigMode = true
}

local configMode
local anonID = 0
local IsFrameHandle = IsFrameHandle
local GetAnonymousName = function(key)
  if not key then
    anonID = anonID + 1
    key = anonID
  end
  return 'VN' .. key
end
local GetTableName = function(table)
  return (IsFrameHandle(table) and table:GetName()) or tostring(table)
end

local OFFSET_PARALLELS = {
  TOP = {'LEFT', 'RIGHT', 'SetHeight'},
  BOTTOM = {'LEFT', 'RIGHT', 'SetHeight'},
  LEFT = {'TOP', 'BOTTOM', 'SetWidth'},
  RIGHT = {'TOP', 'BOTTOM', 'SetWidth'},
}
local ANCHOR_OFFSET_POINT = {
  TOP = 'BOTTOM',
  TOPLEFT = 'BOTTOMRIGHT',
  TOPRIGHT = 'BOTTOMLEFT',
  LEFT = 'RIGHT',
  RIGHT = 'LEFT',
  CENTER = 'CENTER',
  BOTTOM = 'TOP',
  BOTTOMRIGHT = 'TOPLEFT',
  BOTTOMLEFT = 'TOPRIGHT',
}
local ANCHOR_INSET_DELTA = {
  TOP = {0, -1},
  TOPLEFT = {1, -1},
  TOPRIGHT = {-1,-1},
  LEFT = {1, 0},
  BOTTOMLEFT = {1, 1},
  BOTTOM = {0, 1},
  BOTTOMRIGHT = {-1, 1},
  RIGHT = {-1, 0},
  CENTER = {0, 0},
}

function VeneerCore:print(...)
  local txt = '|cFFFFFF00Veneer|r:'
  for i = 1, select('#', ...) do
    txt = txt .. ' '.. tostring(select(i, ...))
  end

  DEFAULT_CHAT_FRAME:AddMessage(txt)
end

function VeneerCore:OnLoad()
  print('|cFFFFFF00Veneer!|r')
  self:RegisterEvent('ADDON_LOADED')
  self:RegisterEvent('PLAYER_LOGIN')

  self.DEVIAN_PNAME = 'Veneer'
  self:RegisterForDrag('LeftButton')


end

local select, IsAddOnLoaded, IsLoggedIn = select, IsAddOnLoaded, IsLoggedIn

function VeneerCore:OnEvent(event, ...)
  print(event, ...)
  if event == 'ADDON_LOADED' or event == 'PLAYER_LOGIN' then
    print(IsLoggedIn(), self.initialized)
    if IsLoggedIn() and not self.intialized then
      self:Setup()
      self.intialized = true
      print('popping init sequence', self.intialized)
    end


    if self.intialized then
      local addon  = ...
      if self.AddOnCheck[addon] then
        print('  - setting up '..addon..' dependent modules:')
        local keepChecking = false
        for index, handler in ipairs(self.AddOnCheck[addon]) do
          print('  -', handler:GetName(), (not handler.initialized) and (handler.addonFrame and not _G[handler.addonFrame]))
          if not handler.initialized then
            print('  '..handler:GetName()..':Setup()')
            handler:Setup()
            handler.initialized = true
          end
        end
        if not keepChecking then
          self.AddOnCheck[addon] = nil
        end
      end
    end
  end
end

function VeneerCore:OnDragStart()
  self:StartMoving()
end

function VeneerCore:OnDragStop()
  self:StopMovingOrSizing()
end

local VeneerModule_Setup = function(frame)
  if (not frame.addonTrigger) or select(2,IsAddOnLoaded(frame.addonTrigger)) then
    if not frame.initialized then
      frame:Setup()
      frame.initialized = true
    end
  end
end

function VeneerCore:Setup ()
  local resetConfig = (not VeneerData)
  if (not VeneerData) then
    VeneerData = defaults
    VeneerData.version = VENEER_VERSION
  end
  self.data = VeneerData
  self:ExecuteOnClusters(nil, VeneerModule_Setup)

  self.ConfigMode = VeneerData.ConfigMode
  self:UpdateConfigLayers()
  self:Reanchor()
  self:Update()
end

function VeneerCore:UpdateConfigLayers()
  if VeneerData then

    VeneerData.ConfigMode = self.ConfigMode
  end

  self:print('Config mode '..(self.ConfigMode and '|cFF00FF00ON|r' or '|cFFFF0000OFF|r')..'.')
  self:ExecuteOnClusters(nil, function(frame)
    if frame.UpdateConfigLayers then
      frame:UpdateConfigLayers(self.ConfigMode)
    end


    if type(frame.ConfigLayer) == 'table' then
      for index, region in ipairs(frame.ConfigLayer) do
        print('setting', frame:GetName() .. '['.. index..']', 'to', self.ConfigMode)

        region:SetShown(self.ConfigMode)
      end
    end

    self.ConfigLayers[frame] = frame:IsShown()
    if self.ConfigMode then
      print(frame:GetName(), self.ConfigLayers[frame])
      frame:SetShown(self.ConfigMode)
    else
      frame:SetShown(self.ConfigLayers[frame])
    end
  end)
end


function VeneerCore:GetClusterFromArgs (...)
  local primaryAnchor
  local insertPosition



  local clusterTable = self.FrameClusters
  for i = 1, select('#', ...) do
    local arg = select(i, ...)
    local argType = type(arg)
    if argType == 'string' then
      if not primaryAnchor then
        primaryAnchor = arg
      end
      clusterTable[arg] = clusterTable[arg] or {}
      clusterTable = clusterTable[arg]
      print(strrep(' ', i)..'anchor cluster', i, arg)
    elseif argType == 'boolean' then
      insertPosition = 1
    end
  end
  if not primaryAnchor then
    primaryAnchor = 'TOPLEFT'
  end
  if not insertPosition then
    insertPosition = #clusterTable + 1
  end
  return primaryAnchor, clusterTable, insertPosition
end

function VeneerCore:AddHandler(handler, ...)
  print('*** Adding handler:', handler.moduleName or handler:GetName())


    local anchorGroup, clusterTable, clusterIndex = self:GetClusterFromArgs(...)
    if clusterIndex == 1 then
      for i, frame in ipairs(clusterTable) do
        frame.clusterIndex = i + 1
      end
    end
    tinsert(clusterTable, clusterIndex, handler)

  print('cluster', anchorGroup, 'table', clusterTable, 'position', clusterIndex)

  handler.anchorCluster = clusterTable
  handler.anchorIndex = clusterIndex
  for k,v in pairs(VeneerHandlerMixin) do
    if not handler[k] then
      print(' * from mixin:', k)
      handler[k] = v
    end
  end

  if handler.addonTrigger and not IsAddOnLoaded(handler.addonTrigger) then
    print('|cFFFF4400  -- dependency:', handler.addonTrigger)
    self.AddOnCheck[handler.addonTrigger] = self.AddOnCheck[handler.addonTrigger] or {}
    tinsert(self.AddOnCheck[handler.addonTrigger], handler)
  end

  if self.initialized then
    print('  -- initialization check')
    if handler.Setup then
      local doInit = (not handler.initialized)
      if handler.addonTrigger and not IsAddOnLoaded(handler.addonTrigger) then
        doInit = false
      end
      -- room to add other checks

      if doInit then
        handler:Setup()
        handler.initialized = true
        self:InternalReanchor(handler)
      end
    end
  end
end

function VeneerCore:Reanchor()
  self:ExecuteOnClusters(nil, 'Reanchor')
  self:DynamicReanchor(self)
end

function VeneerCore:Update()
  self:ExecuteOnClusters(nil, function(frame)
    if frame.initialized and frame.Update then
      frame:Update()
    end
  end)
  self:Reanchor()
end

-- updates anchor relations to and from the target handler
function VeneerCore:GetAnchor(...)

end

-- Evaluates frames visibility and chains them accordingly

function VeneerCore:DynamicReanchor(parent)
  parent = parent or self
  print('|cFF88FF00DynamicReanchor()')
  for anchorPoint, cluster in pairs(parent.FrameClusters) do
    local lastFrame
    for index, frame in ipairs(cluster) do
      print('  |cFF00FF00'..index, frame:GetName(), frame:IsVisible(), (lastFrame and ('|cFFFFFF00'..lastFrame:GetName()..'|r') or '|cFF00FFFFUIParent'))
      if frame:IsVisible() then

        if frame.anchorFrame then
          frame:SetPoint(frame.anchorPoint, frame.anchorFrame, frame.anchorFrom, frame.anchorX, frame.anchorY)
          print(frame:GetTop(), frame:GetRight())
        else
          anchorPoint = frame.anchorPoint
          frame:ClearAllPoints()
          if lastFrame then
            frame:SetPoint(anchorPoint, lastFrame, ANCHOR_OFFSET_POINT[anchorPoint], 0, 0)
          else
            frame:SetPoint(anchorPoint, UIParent, anchorPoint, frame.anchorX, frame.anchorY)
          end
          print(frame:GetTop(), frame:GetRight())
          lastFrame = frame
        end

      end

    end
  end
end

-- Evaluates the current visibility state and re-anchors adjacent blocks accordingly
function VeneerCore:InternalReanchor(handler, printFunc)
  print('|cFF00FFFFVeneer:InternalReanchor('..handler:GetName()..')')
  if handler.anchorFrame then
    handler:SetPoint(handler.anchorPoint, handler.anchorFrame, handler.anchorFrom, handler.anchorX, handler.anchorY)
    return
  end


  local anchorPoint = handler.anchorPath or handler.anchorPoint
  local anchorParent, anchorTo = UIParent, anchorPoint
  local subPoint, subTo
  local nextFrame
  for index, frame in ipairs(handler.anchorCluster) do
    print('  |cFF00FF00'..index, frame:GetName(), frame:IsVisible())
    if frame:IsVisible() then
      if frame ~= handler then
        anchorParent = frame
        anchorTo = ANCHOR_OFFSET_POINT[anchorPoint]

      else
        nextFrame = handler.anchorCluster[index+1]
        if nextFrame then

          subPoint = nextFrame.anchorPath or nextFrame.anchorPoint
          subTo = ANCHOR_OFFSET_POINT[subPoint]
          nextFrame:ClearAllPoints()
          nextFrame:SetPoint(subPoint, handler, subTo, 0, 0)
          print(' -- pushing '..nextFrame:GetName()..' down the anchor chain', subPoint, subTo)
        end
        break
      end
    end
  end

  if handler:IsVisible() then
    handler:SetPoint(anchorPoint, anchorParent, anchorTo, 0, 0)
  else
    if anchorParent and nextFrame then
      nextFrame:SetPoint(subPoint, handler, subTo, 0, 0)
    end
  end


  print(handler.anchorPoint, anchorParent, anchorTo)
  if printFunc then
    printFunc('|cFF88FF00'..handler:GetName()..':SetPoint(', handler.anchorPoint, anchorParent, anchorTo)
  end
end

function VeneerCore:SlideBlock(frame, ...)
  local aX, aY = frame:GetLeft(), frame:GetTop()

  frame:SetPoint('TOPLEFT', frame, 'BOTTOMLEFT', aX, aY)
  frame.animation = frame.animation or {}
  frame.animation.startX = aX
  frame.animation.startY = aY

  local targetPoint, targetParent, targetAnchor, offsetX, offsetY = ...
  frame.BlockSlide:SetScript('OnFinished', function()
    frame:SetPoint(targetPoint, targetParent, targetAnchor, offsetX, offsetY)
    VeneerAnimationMixin.OnFinished(frame)
  end)

end


function VeneerCore:ExecuteOnClusters(layer, method)
  self.parserDepth = self.parserDepth + 1
  if not layer then
    if self.parserDepth > 1 then
      tinsert(self.pendingCalls, method)
      print('delaying walk for', method)
      return
    end
    print('|cFF00FF00Veneer:ExecuteOnClusters|r('..tostring(layer)..', '..tostring(method)..')')
  else
    print(' Level '..self.parserDepth)
  end

  layer = layer or self.FrameClusters
  for anchor, cluster in pairs(layer) do
    for index, frame in ipairs(cluster) do
      print(' '..anchor..'.'..index..' = '..frame:GetName())
      if type(method) == 'function' then
        method(frame, true)
      elseif frame[method] then
        print('  |cFF00FF00'..frame:GetName())
        frame[method](frame, true)
      end
    end
    if cluster.FrameClusters then
      self:ExecuteOnClusters(cluster.FrameClusters, method)
    end
  end
  self.parserDepth = self.parserDepth - 1

  if (self.parserDepth == 0) and (#self.pendingCalls >= 1) then
    local delayedMethod = tremove(self.pendingCalls, 1)
    print('starting delayed walk for', delayedMethod)
    self:ExecuteOnClusters(nil, delayedMethod)
  end
end



-- Takes frame handle and assigns a block to it
function VeneerCore:Acquire (frame, template)
  if not frame then
    print('|cFFFF4400Unable to acquire frame...|r')
    return
  end
  local veneer = self.Frames[frame]
  if not veneer then
    local name = GetAnonymousName()
    veneer = CreateFrame('Frame', name, frame, template)
    print(self:GetName()..':Acquire()', frame:GetName(), template)

    veneer:SetAllPoints(frame)
    veneer:SetParent(frame)
    veneer.label:SetText(name)
    veneer.bg:SetColorTexture(0,0,0,0)
    veneer:Hide()
    veneer:EnableMouse(false)
    -- find current X/Y
    veneer.currentLeft = frame:GetLeft()
    veneer.currentTop = frame:GetTop()
    self.Frames[frame] = veneer
  end
  return veneer
end