view LibKraken/LibKraken-1.0.lua @ 7:a2fc77fa4c73

- make sure key bindings honor the `ActionButtonUseKeyDown' CVar
author Nenue
date Thu, 28 Jul 2016 17:27:40 -0400
parents 9ac29fe77455
children 82170735e67c
line wrap: on
line source
--[[
-- KrakynTools
-- AddOn prototyping library.
--
-- Implements
--  KT.register(frame) to hook the following (all optional):
--    frame:init()                            run immediately after KT sets itself up
--    frame:profile("Name-TruncatedRealm")    called the first time SavedVars data becomes available
--    frame:variables()                       called upon variables being available
--    frame:event(event, ...)                 replaces the event callback
--    frame:ui()                              called by /ui when activating
--
--    frame:tab(name, tooltip, texture, coord)
--      produces a serial button that changes display tabs
--
--    frame:button(name, text, tooltip, onClick)
--      produces a button with OnClick script
--
--    frame:uibutton(name, text, tooltip, onClick, texture, coord)
--      produces a header button with desired onClick
--
]]--

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

--GLOBALS: LibKT, KrakTool, KTErrorFrame, LibKTError, SlashCmdList, SLASH_RL1, SLASH_UI1
local CreateFrame, debugstack, tostring, select = CreateFrame, debugstack, tostring, select
local print, max, unpack, tinsert = print, max, unpack, tinsert
local ipairs, xpcall, next, safecall = ipairs, xpcall, next, safecall
local UI_TOGGLE = false
local db


KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent)
KT.addons = {}
KT.initStack = {}
KT.varsStack = {}
local print = DEVIAN_WORKSPACE and function(...) print('LKT', ...) end or function() end
local registeredHandles = {}

--- /rl
-- ReloadUI shortcut
SLASH_RL1 = "/rl"
SlashCmdList.RL = function ()
  ReloadUI()
end

--- /kv addon ...
-- Dumps table values from "addon", using the arguments as index values.
-- Numerics are converted, and names that end with "()" will be called as such
SLASH_KV1 = "/kv"
SlashCmdList.KV = function (editbox, input)

end


--- /ui
-- Run any addon:ui() methods
SLASH_UI1 = "/ui"
SlashCmdList.UI = function ()
  if UI_TOGGLE then
    UI_TOGGLE = false
  else
    UI_TOGGLE = true
  end
  for i, frame in pairs(KT.frames) do
    if UI_TOGGLE then
      if frame.close then
        frame.close()
      else
        frame:Hide()
      end
    else
      if frame.ui then
        frame.ui()
      end
      frame:Show()
    end
  end
end

LibKTError = 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 pending = {}
local processing = false
local isHandled = false
local nodebug = false
function KT.OnEvent (addon, event, ...)
  if processing then
    local args = {...}
    C_Timer.After(0, function() KT.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, ...)
  end

  if 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

  for i, module in ipairs(addon.modules) do
    --print(i, module)
    if 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
  if nodebug then
    processing = false
    return
  else
    KT.UpdateEventStatus(addon, event, ...)
    processing = false
  end

end

KT.UpdateEventStatus = function(addon, event, ...)
  print(addon:GetName(), event, ...)

  -- debug outputs
  if addon.status then
    addon.status:SetText(event .. '\n|cFF00FF00' .. addon.handled .. '|r |cFFFF8800' .. addon.missed .. '|r |cFFFF4400' .. addon.unhandled .. '|r')
    if isHandled then
      addon.status:SetTextColor(0,1,0)
      if addon.log then
        local logtext = event
        for i = 1, select('#',...) do
          logtext = logtext .. '\n' .. i .. ':' .. tostring(select(i,...))
        end
        addon.log:SetText('|cFFFFFF00last|r\n' .. logtext)
        local newWidth = addon.log:GetStringWidth()

        if addon.logfirst then
          if not addon.firstEvent then
            addon.firstEvent = event
            addon.logfirst:SetText('|cFF00FF88first|r\n' .. logtext)
          end

          newWidth = newWidth + addon.logfirst:GetStringWidth()
        end
        if addon.logdiff then
          if not event ~= addon.firstEvent then
            addon.firstEvent = event
            addon.logdiff:SetText('|cFF0088FFdiff|r\n' .. logtext)
          end
          newWidth = newWidth + addon.logdiff:GetStringWidth()
        end
        --addon:SetWidth(newWidth)
      end
    else
      addon.status:SetTextColor(1,0,0)
    end
  end
end

KT.register = function(addon, nameOrModule, noGUI)
  local name
  if registeredHandles[addon] then
    if type(nameOrModule) == 'table' then
      tinsert(addon.modules, nameOrModule)
      name = debugstack(2,1,0):match(".+\\(%S+)%.lua")

      print('auto-resolved module name', name, tostring(nameOrModule))
    else
      name = nameOrModule
    end
  else
    if not nameOrModule then
      assert(type(addon) == 'table', 'Need a valid table.')
      if addon.GetName then
        name = addon:GetName()
      else
        name = debugstack(2,1,0):match(".+AddOns\\(%S+)\\")
        print('auto-resolved addon name', name, tostring(nameOrModule))
      end
      assert(type(name) == 'string', 'Unable to resolve a valid stub name.')
    end
    -- if calling again, assume name is a file handle

    registeredHandles[addon] = name
    KT.addons[name] = addon
    if addon.SetScript then
      addon:SetScript('OnEvent', KT.OnEvent)
    end
    addon.unhandled = 0
    addon.missed = 0
    addon.handled = 0
    addon.firstEvent = false
    addon.modules = {}
    tinsert(KT.initStack, addon)
    tinsert(KT.varsStack, addon)

    if addon.GetName and (not noGUI) then
      addon.UIPanelAnchor = {'TOPLEFT', addon, 'TOPLEFT', 12, -12 }
      addon.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
      addon.button = KT.button
      addon.uibutton = KT.uibutton
      addon.tab = KT.tab
      addon.print = KT.print
    end
  end

  return addon, (DEVIAN_WORKSPACE and function(...) _G.print(name, ...) end or function() end)
end



local onEvent = function(self, event, arg1)
  if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
    -- run any init blocks left in the queue
    while #KT.initStack >= 1 do
      local addon = tremove(KT.initStack, 1)
      print('KT', addon:GetName(), 'init')
      if addon.init then
        xpcall(addon.init, LibKTError)
        for i, module in ipairs(addon.modules) do
          if module.init then
            xpcall(module.init, LibKTError)
          end
        end
      end
    end

    -- run any variables blocks if player variables are ready 
    if IsLoggedIn() and #KT.varsStack >= 1 then
      while #KT.varsStack >= 1 do
        local addon = tremove(KT.varsStack, 1)
        print(addon:GetName())
        if addon.variables then
          xpcall(addon.variables, LibKTError)
          for i, module in ipairs(addon.modules) do
            if module.variables then
              xpcall(module.variables, LibKTError)
            end
          end
        end
      end
    end
  end
end

KT.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

--- Button generators

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

KT.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

KT.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

KT.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


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