view Init.lua @ 7:5301c68f28d8

TrackerBlock - use IsModifiedClick function to determine appropriate OnClick actions - handle 'CHATLINK' modifier - handle 'TOGGLEQUESTWATCH' modifier TrackerBlockObjectives - use a generic framework to manage frame creation for various criteria tracker types: - ProgressBar when Blizzard flag data indicates so - skip when Blizzard flag data indicates so - DynamicText otherwise - events related to the criteria are registered in the criteria frame, and unregistered when the frame is hidden, either by destruction of its parent or completion
author Nenue
date Fri, 01 Apr 2016 12:27:05 -0400
parents 3397aae1f44d
children 9455693fc290
line wrap: on
line source
--- Modulizer framework
-- OnInitialize
-- OnUpdate
-- OnEnable     -- run when GetSpecialization() returns true

local ADDON, A = ...
Veneer = Veneer or CreateFrame('Frame', 'Veneer', UIParent)
local B = Veneer
local wipe, min, max, random, tinsert, tremove = table.wipe, math.min, math.max, math.random, table.insert, table.remove
local pairs, ipairs, select, unpack, _G = pairs, ipairs, select, unpack, _G
local type, tostring, format = type, tostring, string.format
A.frame = B

--- Cache tables
local initOnced
local modules = {}
local queuedModules = {}
local checkForConfig = {}
local moduleStack = {
}

--- Various region categories
B.displays = {}
B.configLayers = {}
B.configLayersRef = {}

--@debug@
--- Generates a print handler pointing to a static channel signature
-- @usage func = B.print(sig)
-- @param sig channel name or number
local printfuncs = {}
B.print = function(pref, ...)
  if Devian and Devian.InWorkspace() then
      printfuncs[pref] = printfuncs[pref] or function(...) print(pref,  ...) end

    return printfuncs[pref]
  else
   return function () end
  end
end

local rgb = {}
local getcolor = function()
  local n, p = 0, 4
  for i = 1, 3 do
    rgb[i] = min(random(n,p) * 64, 255)
    if rgb[i] == 255 then
      p = 4
    elseif rgb[i] > 0 then
      n = 2
    end
  end
  return unpack(rgb)
end

local color = {}
local fprints = {}
B.fprint = function()
  if not (Devian and Devian.InWorkspace()) then
    return function() end
  end


  local sig = debugstack(2,1)
  if fprints[sig] then
    return fprints[sig]
  end

  local func = sig:match("%`(%a+)%'")
  if not func then
    func = sig:match("<(.-)>")
  end
  func = func:gsub('(%l+)(%u)', function(a, b) return a:sub(0,2) .. b  end, 1)
  func = func:gsub('^.+%\\', '')
  if not func then
    func = 'noname'
  end

  local r, g, b = getcolor()
  color[sig] = color[sig] or format('|cFF%02X%02X%02X%s|r', r, g, b, func)

  --print(color[func] .. ' ( ' .. table.concat(args, ', ')..' )' )
  func = B.print(func)
  fprints[sig] = func
  return func
end

--@end-debug@
--[=[@non-debug@
B.print = function() end
--@end-non-debug@]=]

-- for the Mikk script
-- GLOBALS: NUM_LE_RAID_BUFF_TYPES
-- GLOBALS: BUFF_FLASH_TIME_ON, BUFF_FLASH_TIME_OFF, BUFF_MIN_ALPHA, BUFF_WARNING_TIME, BUFF_DURATION_WARNING_TIME
-- GLOBALS: BUFFS_PER_ROW, BUFF_MAX_DISPLAY, BUFF_ACTUAL_DISPLAY, DEBUFF_MAX_DISPLAY, DEBUFF_ACTUAL_DISPLAY, BUFF_ROW_SPACING
-- GLOBALS: CONSOLIDATED_BUFFS_PER_ROW, CONSOLIDATED_BUFF_ROW_HEIGHT, NUM_TEMP_ENCHANT_FRAMES
-- GLOBALS: BUFF_BUTTON_HEIGHT, BUFF_FRAME_BASE_EXTENT, BUFF_HORIZ_SPACING

local print = B.print('Bfl')

--- Template for making perpendicular traversals of the displays structure; also makes sure the table is there
B.Abstract = function(dest, key, table)
  if table then
    for _, v in pairs(dest) do
      v[key] = {}
    end
  end
  B[key] = setmetatable({}, {
    __index = function(t, k)
      return dest[k][key]
    end,
    __newindex = function(_, k, v)
      print('abstract write ('..key..'):', k)
      dest[k][key] = v
    end,
    __tostring = function() return 'Abstract:'..key..'' end
  })


  return B[key]
end


--- localize for speed
local layers, refs, displays = B.configLayers, B.configLayersRef, B.displays

local ModulesCall = function(func)

  local n = 0
  for i = 1, #moduleStack do
    print('calling level '..i)
    local stackset = moduleStack[i]

    for name, module in pairs(stackset) do
      n = n + 1
      print(n..'  '..name..'.'..func..'()')


      if module[func] then
        module[func](module)
      end
    end
  end
end


local Enable = function()
end

--- The things that happen repeatedly
local Init = function ()
end


--- Things that happen immediately upon entering world
local InitOnce = function()
  print('entering world first time')
  local defaults = B.ConfDefaults
  print('|cFFFFFF00Veneer|r')
  if not VeneerData then
    VeneerData = {}
    for k,v in pairs(defaults) do
      VeneerData[k] = v
    end
    print('Veneer defaults being used.')
  end

  B.Conf = setmetatable(VeneerData, {__index = function(_, k) return defaults[k] end})

  -- suffix tables
  for name, display in pairs(displays) do
    display.conf = setmetatable({}, {
      __index = function(_, k)
        --print('config check '.. name .. k)
        return B.Conf[name .. k] or B.Conf['BuffButton' .. k]
      end,
      __newindex = function(_, k , v)
        B.Conf[name..k] = v
      end,
    })
  end

  -- To ensure that modules are run in controlled order, walk the dependency list; if the dep shows up
  -- in the loaded manifest, remove the value. If the dep list isn't empty, move that module to the next
  -- layer.
  local loaded = {}
  local stackLevels = #moduleStack
  local i = 1
  moduleStack[1] = modules
  repeat
    print('setting init level '.. i)
    local queue = moduleStack[i]
    for name, module in pairs(queue) do

      if queuedModules[name] and #queuedModules[name] > 0 then
        local p = #queuedModules[name]
        for j = 1, p do
          local dep = queuedModules[name][j]

          if loaded[dep] then
            print( '    ' .. dep .. ' OK')
            queuedModules[name][j] = nil
            for k = j, p do
              print('   shift ' .. (k+1)  .. ' ('..tostring(queuedModules[name][k+1])..') to ' .. k ..'')
              queuedModules[name][k] = queuedModules[name][k+1]
            end
          end
        end

        if #queuedModules[name] == 0 then
          queuedModules[name] = nil
          print('  |cFF00FFFF'.. name ..'|r deps OK')
          loaded[name] = true
        else

          print('  |cFFFF8800' .. name ..'|r pending')
          local next = i+1
          if not  moduleStack[next] then
            moduleStack[next] = {}
          end
          stackLevels = next
          moduleStack[next][name] = module
          queue[name] = nil
        end

      else
        print('  |cFF00FF00'.. name ..'|r no deps')
        loaded[name] = true
      end
   end
    i = i + 1
  until i > stackLevels


  for level, batch in ipairs(moduleStack) do
    print('config level', level)
    for name, module in pairs(batch) do
      print('integrity check', name)

      --[===[@non-debug@
      if module.defaults and not VeneerData[name] then
      --@end-non-debug@]===]
        print('Adding defaults from module ', name)
        VeneerData[name] = module.default
      --[===[@non-debug@
      end
      --@end-non-debug@]===]
    end
  end


  if #checkForConfig >= 1 then
    local queuedFrame = tremove(checkForConfig)
    while queuedFrame do
      B.SetConfigLayers(queuedFrame)
      B.InitXMLFrame(queuedFrame)
      queuedFrame = tremove(checkForConfig)
    end
  end
  -- remove from existing
end

--- Fires an update to all modules
local lastUpdate
function B.UpdateAll(...)
  lastUpdate = GetTime()
  ModulesCall('OnUpdate', lastUpdate)
end

B:RegisterEvent('PLAYER_ENTERING_WORLD')
B:SetScript('OnEvent', function(self, event)
  if event == 'PLAYER_ENTERING_WORLD' then
    if not initOnced then
      InitOnce()
      ModulesCall('OnInitialize')
      initOnced = true
      C_Timer.After(1, function()
        if GetSpecialization() then
          print(GetSpecialization(), 'enabling')
          ModulesCall('OnEnable')
          B:SetScript('OnUpdate', nil)
        end

      end)
    end

  end

  B.UpdateAll()
end)

--- Modulizer method
--
function B:RegisterModule (name, module, ...)
  if modules[name] then
    print('pulling modules[|cFFFF8800'.. tostring(name) ..'|r]')
    return modules[name]
  end

  print('new module |cFF00BBFF'.. tostring(name) ..'|r')
  if module then
    if modules[name] then
      error("Module table for '"..tostring(name).."' already exists.")
    end
  else
    module = CreateFrame('Frame', 'Veneer' .. tostring(name) .. 'Handler', B, 'VeneerHandlerTemplate')
  end
  modules[name] = module
  B[name] = module
  if select('#', ...) >= 1 then
    local numDeps = select('#', ...)
    print('  '..numDeps..' deps detected')
    for i = 1, numDeps do
      local dep = select(i, ...)
        -- means that init/enable funcs are ordered to run after deps do their things
      queuedModules[name] = queuedModules[name]  or {}
      tinsert(queuedModules[name], dep)
      print('  needs '..dep)
    end
  end
  return module
end


B.SetConfigLayers =  function(frame)
  local print = B.fprint()
  if not frame.config then
    print(frame:GetName(), 'has no config layers')
    return
  end
  print('Registering config layers from', frame:GetName())

  for i, subframe in ipairs(frame.config) do
    -- make sure there are no duplicates
    if not refs[subframe] then
      local key = #layers+1
      layers[key] = subframe
      refs[subframe] = key
    end
    print(' ', i, subframe:GetName())
  end
end

B.RemoveConfigLayers = function(frame)

  local print = B.fprint()
  print('|cFFFF0000RemoveConfigLayers', frame:GetName())
  for i, subframe in pairs(layers) do
    if subframe:GetParent() == frame then
      print('|cFFFF8800  ', subframe:GetParent():GetName(), '|cFFFFFF00', subframe:GetName())
      layers[i]:Hide()
      layers[i] = nil
      refs[subframe] = nil
    end
  end
end

B.UpdateConfigLayers = function()
  local print = B.fprint()
  local func = B.Conf.GuidesMode and 'Show' or 'Hide'
  local numAnchors = 0
  for name, display in pairs(displays) do
    numAnchors = numAnchors + 1
    display.anchor:EnableMouse(B.Conf.GuidesMode)
    if B.Conf.GuidesMode then
      display.anchor:SetScript('OnUpdate', display.anchor.OnUpdate)
    else
      display.anchor:SetScript('OnUpdate', nil)

      for i, anchorButton in ipairs(display.anchor.anchorButton) do
        anchorButton:Hide()
      end

    end

  end
  for id, region in pairs(layers) do
    print(id, region:GetName(), func)
    region[func](region)
  end

  print('['..func..'] updated', #layers, 'regions,', numAnchors, 'frames')
end

--- Generic handlers for keeping track of XML-defined frames
B.OnLoad = function(self)
  tinsert(checkForConfig, self)
end

B.InitXMLFrame = function(self)
  print('|cFF00FF00hello from '..self:GetName())

  self:RegisterForDrag('LeftButton')
  if not B.Conf.FramePosition then
    B.Conf.FramePosition = {}
  end
  if B.Conf.FramePosition[self:GetName()] then
    print('restoring frame position', unpack(B.Conf.FramePosition[self:GetName()]))
    self:ClearAllPoints()
    local anchorTo, relativePoint, x, y = unpack(B.Conf.FramePosition[self:GetName()])
    self:SetPoint(anchorTo, UIParent, relativePoint, x, y)
  end
end

B.OnDragStart = function(self)
  self.xA = self:GetLeft()
  self.yA = self:GetBottom()
  self.anchorTo, self.relativeTo, self.relativePoint, self.x, self.y = self:GetPoint(1)
  print('acquire anchor', self:GetPoint(1))
  print(self:GetName(), 'start moving ('..self.x..', '..self.y..')')
  self:StartMoving()
end

B.OnDragStop = function(self)
  print(self:GetName(), 'stop moving ('..self:GetLeft()..', '..self:GetBottom()..')')
  local xB = self:GetLeft() - self.xA
  local yB = self:GetBottom() - self.yA
  print('storing anchor point', self.anchorTo, self.relativePoint, self.x + xB, self.y + yB)

  self:StopMovingOrSizing()
  B.Conf.FramePosition[self:GetName()] = {self.anchorTo, self.relativePoint, self.x + xB, self.y + yB}
  B.InitXMLFrame(self)

end