view Button.lua @ 263:e0071c480445

Added tag 1.1 beta 6 for changeset b2e401183d36
author Flick
date Fri, 06 May 2011 15:51:36 -0700
parents c918ff9ac787
children 36a29870bf34
line wrap: on
line source
--[[
  ReAction Button base class
--]]

-- local imports
local addonName, addonTable = ...
local ReAction = addonTable.ReAction
local L = ReAction.L
local LKB = ReAction.LKB
local _G = _G
local CreateFrame = CreateFrame
local GetBindingKey = GetBindingKey
local format = string.format

-- private
local trash = CreateFrame("Frame")
local frameList = { }
local idPools = { }

local function kb_onEnter( frame )
  LKB:Set(frame)
end

-- Button class
local buttonTypeID = "Button"
local Button = { 
  defaultBarConfig = {
    type = buttonTypeID,
    btnWidth = 36,
    btnHeight = 36,
    btnRows = 1,
    btnColumns = 12,
    spacing = 3
  },
  barType = L["Button Bar"]
} 

ReAction.Button = Button -- export to ReAction

function Button:New( name, config, bar, idx, inherits, buttonType )
  buttonType = buttonType or "CheckButton"

  -- create new self
  self = setmetatable( 
    { 
      bar = bar,
      idx = idx,
      config = config,
      name = name,
    }, 
    { __index = self } )

  -- have to recycle frames with the same name: CreateFrame() doesn't overwrite
  -- existing globals. Can't set to nil in the global because it's then tainted.
  -- Caller is responsible for ensuring global uniqueness of names.
  local f = name and frameList[name]
  if f then
    f:SetParent(bar:GetFrame())
  else
    f = CreateFrame(buttonType, name, bar:GetFrame(), inherits)
    if name then
      frameList[name] = f
    end
  end

  self.frame = f

  local frames = { }
  self.frames = frames
  frames.icon          = _G[name.."Icon"]
  frames.flash         = _G[name.."Flash"]
  frames.hotkey        = _G[name.."HotKey"]
  frames.count         = _G[name.."Count"]
  frames.name          = _G[name.."Name"]
  frames.border        = _G[name.."Border"]
  frames.cooldown      = _G[name.."Cooldown"]
  frames.normalTexture = _G[name.."NormalTexture"]

  if config then
    config.name = name
  end

  -- install LibKeyBound handlers onto frame
  function f:GetActionName()
    return format("%s:%s", bar:GetName(), idx)
  end

  -- mouse and clicking
  -- set click handlers in subclasses
  f:EnableMouse(true)
  f:RegisterForClicks( bar:GetConfig().clickDown and "AnyDown" or "AnyUp" )
  local clickBinding = format("CLICK %s:LeftButton", name)
  function f:GetHotkey()
    return LKB:ToShortKey(GetBindingKey(clickBinding))
  end

  return self
end

function Button:Destroy()
  local f = self:GetFrame()
  f:UnregisterAllEvents()
  self:ReleaseActionID(self:GetActionID())
  if f then
    f:Hide()
    f:SetParent(trash)
    f:ClearAllPoints()
  end
end

function Button:GetBar()
  return self.bar
end

function Button:GetFrame()
  return self.frame
end

function Button:GetIndex()
  return self.idx
end

function Button:GetName()
  return self.name
end

function Button:GetDefaultBarConfig()
  return self.defaultBarConfig
end

function Button:GetBarType()
  return self.barType
end

function Button:GetButtonTypeID()
  return self.buttonTypeID
end

function Button:GetConfig()
  return self.config
end

function Button:GetActionID()
  -- derived classes should override this
  return nil
end

function Button:SetActionIDPool( poolID, maxID )
  self.actionPoolID = poolID
  self.actionMaxID = maxID
end

function Button:SetupBar( bar )
  local config = bar:GetConfig()
  if not config.buttons then
    config.buttons = { }
  end
  local btnCfg = config.buttons

  local r, c = bar:GetButtonGrid()
  local n = r*c
  local cfgN = n

  local hint = nil
  local i = 1
  repeat
    local b = bar:GetButton(i)
    if b then
      if i > n then
        bar:RemoveButton(b)
        b:Destroy()
        if i > cfgN then
          btnCfg[i] = nil
        end
      else
        b:Refresh()
        hint = b:GetActionID()
      end
    elseif i <= n then
      local cfg = btnCfg[i] or { }
      local success, r = pcall(self.New, self, cfg, bar, i, hint)  -- note call semantics for derived class constructors
      if success and r then
        b = r
        bar:AddButton(i,b)
        btnCfg[i] = cfg
        b:Refresh()
        hint = b:GetActionID()
      else
        n = i - 1
        if not success then
          bar:ClipNButtons(n)
          cfgN = n
          geterrorhandler()(r)
        end
      end
    end
    i = i + 1
  until b == nil
end

function Button:AcquireActionID( id, hint, unique )
  local poolID = self.actionPoolID
  local maxID = self.actionMaxID
  if not poolID or not maxID then
    error("AcquireActionID: must setup pool first with SetActionIDPool")
  end
  local pool = idPools[poolID]
  if not pool then
    pool = { nWraps = 0, useCount = { } }
    for i = 1, maxID do
      pool.useCount[i] = 0
    end
    idPools[poolID] = pool
  end
  local useCount = pool.useCount
  if id == nil then
    repeat
      local nWraps = pool.nWraps or 0
      if hint and (useCount[hint] == nil or useCount[hint] == nWraps) then
        id = hint
      else
        local start = hint or 1
        for i = start, maxID do
          if useCount[i] == nil or useCount[i] == nWraps then
            id = i
            break
          end
        end
        if not id then
          for i = 1, start do
            if useCount[i] == nil or useCount[i] == nWraps then
              id = i
              break
            end
          end
        end
      end
      if id == nil then
        if unique then
          error(("All action IDs for bars of type '%s' are in use, cannot create any more buttons"):format(self.config.barType))
        end
        pool.nWraps = nWraps + 1
      end
    until id ~= nil
  end
  useCount[id] = (useCount[id] or 0) + 1
  return id
end

function Button:ReleaseActionID( id )
  local poolID = self.actionPoolID
  if not poolID  then
    error("ReleaseActionID: must setup pool first with SetActionIDPool")
  end
  local pool = idPools[poolID]
  if pool and id and pool.useCount[id] then
    pool.useCount[id] = pool.useCount[id] - 1
    pool.nWraps = min(pool.useCount[id], pool.nWraps)
  end
end

function Button:Refresh()
  local f = self:GetFrame()
  self.bar:PlaceButton( self, f:GetWidth(), f:GetHeight() )
end

function Button:SetKeybindMode( mode )
  local f = self.frame
  if mode then
    self.oldOnEnter = f:GetScript("OnEnter")
    f:SetScript("OnEnter", kb_onEnter)
  elseif self.oldOnEnter then
    f:SetScript("OnEnter", self.oldOnEnter)
    self.oldOnEnter = nil
  end
  self:ShowGridTemp(mode)
  self:UpdateKeybindModeDisplay( mode )
end

function Button:UpdateKeybindModeDisplay( mode )
  local border = self.frames.border or _G[format("%sBorder",tostring(self:GetName()))]
  if border then
    if mode then
      border:SetVertexColor(LKB:GetColorKeyBoundMode())
      border:Show()
    else
      border:Hide()
    end
  end
end

function Button:UpdateHotkey( hotkey )
  hotkey = hotkey or self.frames.hotkey
  if not hotkey then
    hotkey = _G[self:GetName().."HotKey"]
    self.frames.hotkey = hotkey
  end
  if hotkey then
    local txt = self.frame:GetHotkey()
    hotkey:SetText( txt )
    if txt == nil or txt == "" then
      hotkey:Hide()
    else
      hotkey:Show()
    end
  end
end

function Button:GetActionIDLabel( create )
  local f = self:GetFrame()
  if not f.actionIDLabel and create then
    local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
    label:SetAllPoints()
    label:SetJustifyH("CENTER")
    label:SetShadowColor(0,0,0,1)
    label:SetShadowOffset(2,-2)
    f.actionIDLabel = label -- store the label with the frame for recycling
  end
  return f.actionIDLabel
end

function Button:UpdateActionIDLabel( show )
  local label = self:GetActionIDLabel( show )
  if label then
    if show then
      local id = self:GetActionID()
      if id then
        label:SetText(tostring(id))
        label:Show()
        return
      end
    end
    label:Hide()
  end
end

function Button:SetNormalVertexColor( r, g, b, a )
  if ReAction.LBF then
    ReAction.LBF:SetNormalVertexColor(self:GetFrame(), r, g, b, a)
  else
    self:GetFrame():GetNormalTexture():SetVertexColor(r,g,b,a)
  end
end

function Button:GetNormalVertexColor()
  if ReAction.LBF then
    return ReAction.LBF:GetNormalVertexColor(self:GetFrame())
  else
    return self:GetFrame():GetNormalTexture():GetVertexColor()
  end
end

function Button:UpdateShowGrid()
 -- does nothing by default
end

function Button:ShowGridTemp(show)
  -- does nothing by default
end

function Button:ShowGrid(show)
  -- does nothing by default
end