view LibKraken/LibKraken.lua @ 60:256585077cdd

missed a function rename
author Nenue
date Sat, 27 Aug 2016 11:01:25 -0400
parents d8bb2629fea8
children 04c23ceaf9e0
line wrap: on
line source
--[[
-- KrakynTools
-- AddOn prototyping library.
--
--- Bundles an object into the handler queue, returning the core object, and handlers for debug output and co-routine.
-- Addon name is determined in order of: (string first arg, second arg :GetName(), invoking filename).
-- Once an addon name/object is registered, subsequent calls will return a debugger that reports the file or plugin name.
-- @usage addon, print, wrap = KT.register(name, table) or KT.register(addon) or KT.register(addon, plugin)
-- @param name - name of addon, as found in global varargs
-- @param table - addon table from global varargs
--
-- @param frame - frame object used by addon
-- @param plugin - string name of plugin or an object table to check for lib handlers
--
-- Handlers:
--    :init()                            run immediately after KT sets itself up
--    :profile("Name-TruncatedRealm")    called the first time SavedVars data becomes available
--    :variables()                       called upon variables being available
--    :event(event, ...)                 replaces the event callback
--    :ui()                              called by /ui when activating
--
-- Embedded:
--    NOTES:
--      * `name' is passed as is into CreateFrame, so using nil produce an anonymous frame
--      * `coord' is a 4 or 8 size table unpacked into Texture:SetTexCoords()
--
--    tab = frame:tab(name, tooltip, texture, coord)
--      produces a serial button that changes display tabs
--
--    button = frame:button(name, text, tooltip, onClick)
--      produces a button with OnClick script
--
--    uibutton = frame:uibutton(name, text, tooltip, onClick, texture, coord)
--      produces a header button with desired onClick
--
--
]]--

local LIBKT_MAJOR, LIBKT_MINOR = "LibKraken", 2
local KT = LibStub:NewLibrary(LIBKT_MAJOR, LIBKT_MINOR)

--GLOBALS: KTErrorFrame, LibKTError, SlashCmdList, SLASH_RL1, SLASH_UI1
local CreateFrame, debugstack, tostring, select = CreateFrame, debugstack, tostring, select
local max, unpack, tinsert, tremove = max, unpack, tinsert, tremove
local ipairs, pairs, xpcall = ipairs, pairs, xpcall
local type, assert = type, assert
local IsLoggedIn = IsLoggedIn
local db

local print = DEVIAN_WORKSPACE and function(...) _G.print('LKT', ...) end or function() end
local noFunc = function() end

KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent)
KT.addons = {}
KT.initStack = {}
local libInitialized = false
local registeredHandles = {}
local initialized = {}
local enabled = {}
local handlers = {}



local debuggers = {}
local pending = {}


local Embed = function (target, template)
  for k,v in pairs(template) do
    if not target[k] then
      target[k] = template[k]
    end
  end
end

local LibKT_Error = function(msg)
  local dstack = debugstack(2)
  :gsub("Interface\\AddOns\\",'')
  :gsub("<(.-)>", function(a) return '|cFF00FFFF<'.. a ..'>|r' end)



  KTErrorFrame.errmsg:SetText(msg)
  KTErrorFrame.debugstack:SetText(dstack)
  KTErrorFrame:SetHeight(KTErrorFrame.debugstack:GetStringHeight() + KTErrorFrame.errmsg:GetStringHeight() + 12)
  KTErrorFrame:Show()
end

local LibKT_OnLoad = function(self)
  --- /rl
  -- ReloadUI shortcut
  SLASH_RL1 = "/rl"
  SlashCmdList.RL = function ()
    ReloadUI()
  end
  libInitialized = true
end

local LibKT_OnEvent = function(self, event, arg1)
  print(event, arg1)
  if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
    if not libInitialized then
      LibKT_OnLoad(self)
    end


      -- run any init blocks left in the queue
    for i, addon in  ipairs(KT.initStack) do
      if not initialized[addon] then
        print('|cFF0088FF'..tostring(addon)..'|r:init()')
        if addon.init then
          xpcall(addon.init, LibKT_Error)
        end

        if addon.event then
          addon:SetScript('OnEvent', addon.event)
        end

        initialized[addon] = true
      end

      if #addon.modules >= 1 then
        for i, module in ipairs(addon.modules) do
          if not initialized[module] then
            print(i .. ' |cFF0088FF'..tostring(addon)..'|r.|cFF00FFFF'..tostring(module)..'|r:init()')
            if module.init then
              xpcall(module.init, LibKT_Error)
            end

            if module.event then
              module:SetScript('OnEvent', module.event)
            end
            initialized[module] = true
          end
        end
      end

    end

    -- run any variables blocks if player variables are ready 
    if IsLoggedIn() then

      for i, addon in  ipairs(KT.initStack) do
        print('|cFF88FF00'..tostring(addon)..'|r')
        if initialized[addon] then
          if not enabled[addon] then
            print('|cFF88FF00'..tostring(addon)..'|r:variables()')
            if addon.variables then
              xpcall(addon.variables, LibKT_Error)
            end
            enabled[addon] = true
          end

          if addon.modules and enabled[addon] then
            for i, module in ipairs(addon.modules) do
              print(i .. ' |cFF88FF00'..tostring(module)..'|r')
              if not enabled[module] then
                if module.variables then
                  print(i..' |cFF88FF00'..tostring(addon)..'|r.|cFF00FFFF'.. tostring(module)..'|r:variables()')
                  xpcall(module.variables, LibKT_Error)
                end
                enabled[module] = true
              end
            end
          end
        end
      end
    end
  end
end

--- GUI bits
local defaultGUIAddon = {}
do
  local GetButtonTemplate = function(name, parent, template, onClick)
    if _G[name] then
      return _G[name]
    end

    local button = CreateFrame('Button', name, parent, template)
    button:RegisterForClicks('AnyUp')
    button:SetScript('OnClick', onClick)
    return button
  end

  local SetButtonAnchor = function(self, collector, anchor, growth)
    if self:GetID() == 0 then
      self:SetID(#collector)
      print('registered TabButton #', self:GetID())
    end

    if self:GetID() == 1 then
      self:SetPoint(unpack(anchor))
    else
      growth[2] = collector[self:GetID()-1]
      self:SetPoint(unpack(growth))
    end
  end

  defaultGUIAddon.tab = function(self, name, tooltip, texture, coords)
    local button = GetButtonTemplate(name, self,  'KTTabButton', self.SelectTab)
    button.icon:SetTexture(texture)
    button.tooltip = tooltip
    button:SetSize(unpack(self.tabSize))
    if coords then
      button.icon:SetTexCoord(unpack(coords))
    end
    SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth)
    return button
  end

  defaultGUIAddon.button = function(self, name, text, tooltip, onClick)
    local button = GetButtonTemplate(name, self, 'KTButton', onClick)

    button.tooltip = tooltip
    button:SetText(text)
    button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12))

    SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth)
    return button
  end

  defaultGUIAddon.uibutton = function(self, name, text, tooltip, onClick, texture, coords)
    local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick)

    button.tooltip = tooltip
    button:SetText(text)

    if self.UIPanelIcon then
      local w, h, anchor, x, y = unpack(self.UIPanelIcon)
      button.icon:SetTexture(texture)
      button.icon:SetSize(w, h)
      button.icon:ClearAllPoints()
      button.icon:SetPoint(anchor, button, anchor, x, y)
    end

    if not self.UIPanelSize then
      button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5)
    else
      button:SetSize(unpack(self.UIPanelSize))
    end
    if coords then
      button.icon:SetTexCoord(unpack(coords))
    end
    SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth)
    return button
  end
end


local defaultAddon = {}
do
  defaultAddon.print = function(module, ...)
    local msg = '|cFF00FFFF'..module:GetName()..'|r:'
    for i = 1, select('#', ...) do
      msg = msg .. ' ' .. tostring(select(i, ...))
    end
    DEFAULT_CHAT_FRAME:AddMessage(msg)
  end


  local tickerQueue = {}
  local ticker
  defaultAddon.tick = function()

    if #tickerQueue == 0 then
      ticker:Cancel()
      ticker = nil
    end
    local func = tremove(tickerQueue, 1)
    if func then
      --print('#', #tickerQueue)
      func()
    end
  end

  defaultAddon.next = function(f)
    if not ticker then
      --print('create ticker')
      ticker = C_Timer.NewTicker(.001, defaultAddon.tick)
    end
    tinsert(tickerQueue, f)

    return #tickerQueue
  end


--- default OnEvent

  local processing = false
  local isHandled = false
  local nodebug = false
  defaultAddon.event = function (addon, event, ...)
    if processing then
      local args = {...}
      C_Timer.After(0, function() LibKT_OnEvent(addon, event, unpack(args)) end)
      return
    else

    end
    --- reset state
    processing = true
    isHandled = false
    nodebug = false


    if addon.event then
      nodebug = addon.event(addon, event, ...)
    elseif addon[event] then
      nodebug = addon[event](addon, event, ...) or nodebug
      addon.missed = 0
      addon.handled = addon.handled + 1
      isHandled = true
    else
      addon.firstEvent = false
      addon.unhandled = addon.unhandled + 1
      addon.missed = addon.missed + 1
    end

    if addon.modules then
      for i, module in ipairs(addon.modules) do
        --print(i, module, event)
        if module.event then
          module.event(module, event, ...)
        elseif module[event] then
          nodebug = module[event](addon, event, ...) or nodebug
          addon.missed = 0
          addon.handled = addon.handled + 1
          isHandled = true
        else
          addon.firstEvent = false
          addon.unhandled = addon.unhandled + 1
          addon.missed = addon.missed + 1
        end
      end
    end
    --if nodebug then
    processing = false
    return
    --else
    --  KT.UpdateEventStatus(addon, event, ...)
    --  processing = false
    --end

  end
  defaultAddon.wrap = function(addon, module, name)
    local moduleName = name or tostring(module)
    print(addon, module)
    print('|cFF0088FF'..tostring(addon)..'|r:wrap(|cFF00FFFF'.. moduleName .. '|r|cFFFFFF00)|r')

    addon.modules = addon.modules or {}
    tinsert(addon.modules, module)


    if (module.DEVIAN_PNAME and DEVIAN_PNAME == module.DEVIAN_PNAME) or ((not module.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
      debuggers[module] = function(...) _G.print(moduleName, ...) end
    else
      debuggers[module] = noFunc
    end
    return debuggers[module]
  end

  defaultAddon.GetName = function(self) return tostring(self) end
end

--- Frame registration
KT.register = function(addon, arg, noGUI)
  --print('register(', addon, arg, ')')
  local name, handler
  if type(addon) == 'string' and type(arg) == 'table' then
    name = addon
    -- it's a string, i.e. file vararg was passed
    if _G[addon] then
      -- check if it's the name of a frame and use that
      handler = _G[addon]
    else
      -- re-arrange
      handler = arg
    end
  else
    handler = addon
    assert(type(handler) == 'table', 'Usage: KT.register(name, table) or KT.register(table, plugin)')
  end


  local printName
  local isModule

  local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua")
  if registeredHandles[handler] then
    -- addon is already register; treat second argument as plugin target
    isModule = true
    if type(arg) == 'table' then
      local mt = getmetatable(arg)
      setmetatable(arg, {__index = mt.__index, __tostring = function() return scriptName end})
      local debugger = handler:wrap(arg)
      return handler, debugger
    else
      print(' + "|cFF00FFFF'..scriptName..'|r"')
    end
  else
    -- new addon
    --local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua")
    local mt = getmetatable(handler)
    local nmt = {__index = mt.__index, __tostring = function() return scriptName end }
    handler = setmetatable(handler, nmt)
    Embed(handler, defaultAddon)
    name = tostring(handler)
    registeredHandles[handler] = name
    if handler.SetScript then
      handler:SetScript('OnEvent', handler.event)
    end
    handler.unhandled = 0
    handler.missed = 0
    handler.handled = 0
    handler.firstEvent = false
    handler.modules = {}
    tinsert(KT.initStack, handler)

    if not noGUI then
      handler.UIPanelAnchor = {'TOPLEFT', handler, 'TOPLEFT', 12, -12 }
      handler.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
      Embed(handler, defaultGUIAddon)
    end

    print('|cFF0088FF'..tostring(addon)..'|r')
  end

  local debugFunc = noFunc
  --@debug@
  local debugID = isModule and name or handler
  if (handler.DEVIAN_PNAME and DEVIAN_PNAME == handler.DEVIAN_PNAME) or ((not handler.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
    debuggers[debugID] = debuggers[debugID] or function(...) _G.print(name, ...) end
    debugFunc = debuggers[debugID]
  end
  --@end-debug@
  return handler, debugFunc
end

KT.handler:RegisterEvent('ADDON_LOADED')
KT.handler:RegisterEvent('PLAYER_LOGIN')
KT.handler:SetScript('OnEvent', LibKT_OnEvent)