view classes/Bar.lua @ 122:a2d2f23137c8

- Rearranged and consolidated some files in modules directory - Added 'classes' directory, moved Bar and Overlay there - Added Button, ActionButton, and GridProxy classes, not in use yet
author Flick <flickerstreak@gmail.com>
date Mon, 23 Feb 2009 18:56:57 +0000
parents
children 943eed2c7def
line wrap: on
line source
local ReAction = ReAction
local L = ReAction.L
local _G = _G
local CreateFrame = CreateFrame
local floor = math.floor
local fmod = math.fmod
local format = string.format

ReAction:UpdateRevision("$Revision$")

local Bar   = { }
local proto = { __index = Bar }
local weak  = { __mode = "k" }

ReAction.Bar = Bar -- export to ReAction

function Bar:New( name, config )
  if type(config) ~= "table" then
    error("ReAction.Bar: config table required")
  end

  -- create new self
  self = setmetatable( 
    { 
      config  = config,
      name    = name,
      buttons = setmetatable( { }, weak ),
      width   = config.width or 480,
      height  = config.height or 40
    }, 
    proto )
  
  -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is
  -- not mouse-clickable by the user.
  local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
  local f = CreateFrame("Button", name and format("ReAction-%s",name), parent,
                        "SecureHandlerStateTemplate, SecureHandlerClickTemplate")
  f:SetFrameStrata("MEDIUM")
  f:SetWidth(self.width)
  f:SetWidth(self.height)
  f:SetAlpha(config.alpha or 1.0)
  f:Show()
  f:EnableMouse(false)
  f:SetClampedToScreen(true)

  f:SetAttribute("_onstate-showgrid",
    -- function(self,stateid,newstate)
    [[
      control:ChildUpdate(stateid,newstate)
      control:CallMethod("UpdateShowGrid")
    ]])
  f.UpdateShowGrid = function(frame)
    for button in self:IterateButtons() do
      button:UpdateShowGrid()
    end
  end
  ReAction.gridProxy:AddFrame(f)

  -- Override the default frame accessor to provide strict read-only access
  function self:GetFrame()
    return f
  end

  self:ApplyAnchor()
  ReAction.RegisterCallback(self, "OnConfigModeChanged")

  return self
end

function Bar:Destroy()
  local f = self:GetFrame()
  f:UnregisterAllEvents()
  ReAction.UnregisterAllCallbacks(self)
  ReAction.gridProxy:RemoveFrame(f)
  f:Hide()
  f:SetParent(UIParent)
  f:ClearAllPoints()
end

function Bar:OnConfigModeChanged(event, mode)
  self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua
end

function Bar:ApplyAnchor()
  local f = self:GetFrame()
  local c = self.config
  local p = c.point

  f:SetWidth(c.width)
  f:SetHeight(c.height)
  f:ClearAllPoints()
  
  if p then
    local a = f:GetParent()
    if c.anchor then
      local bar = ReAction:GetBar(c.anchor)
      if bar then
        a = bar:GetFrame()
      else
        a = _G[c.anchor]
      end
    end
    local fr = a or f:GetParent()
    f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0)
  else
    f:SetPoint("CENTER")
  end
end

function Bar:SetAnchor(point, frame, relativePoint, x, y)
  local c = self.config
  c.point = point or c.point
  c.anchor = frame or c.anchor
  c.relpoint = relativePoint or c.relpoint
  c.x = x or c.x
  c.y = y or c.y
  self:ApplyAnchor()
  ReAction:RefreshBar(self)
end

function Bar:GetAnchor()
  local c = self.config
  return (c.point or "CENTER"), 
         (c.anchor or self:GetFrame():GetParent():GetName()), 
         (c.relpoint or c.point or "CENTER"), 
         (c.x or 0), 
         (c.y or 0)
end

function Bar:GetSize()
  local f = self:GetFrame()
  return f:GetWidth(), f:GetHeight()
end

function Bar:SetSize(w,h)
  local f = self:GetFrame()
  self.config.width = w
  self.config.height = h
  f:SetWidth(w)
  f:SetHeight(h)
end

function Bar:GetButtonSize()
  local w = self.config.btnWidth or 32
  local h = self.config.btnHeight or 32
  -- TODO: get from modules?
  return w,h
end

function Bar:SetButtonSize(w,h)
  if w > 0 and h > 0 then
    self.config.btnWidth = w
    self.config.btnHeight = h
  end
  ReAction:RefreshBar(self)
end

function Bar:GetButtonGrid()
  local cfg = self.config
  local r = cfg.btnRows or 1
  local c = cfg.btnColumns or 1
  local s = cfg.spacing or 4
  return r,c,s
end

function Bar:GetNumButtons()
  local r,c = self:GetButtonGrid()
  return r*c
end

function Bar:SetButtonGrid(r,c,s)
  if r > 0 and c > 0 and s > 0 then
    local cfg = self.config
    cfg.btnRows = r
    cfg.btnColumns = c
    cfg.spacing = s
  end
  ReAction:RefreshBar(self)
end

function Bar:ClipNButtons( n )
  local cfg = self.config
  local r = cfg.btnRows or 1
  local c = cfg.btnColumns or 1

  cfg.btnRows = ceil(n/c)
  cfg.btnColumns = min(n,c)
end

function Bar:GetName()
  return self.name
end

function Bar:GetFrame()
  -- this method is included for documentation purposes. It is overridden
  -- for each object in the :New() method.
  error("Invalid Bar object: used without initialization")
end

-- only ReAction:RenameBar() should call this function. Calling from any other
-- context will desync the bar list in the ReAction class.
function Bar:SetName(name)
  self.name = name
  self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua
end

function Bar:GetAlpha()
  return self.config.alpha or 1.0
end

function Bar:SetAlpha(value)
  self.config.alpha = value
  self:GetFrame():SetAlpha(value or 1.0)
  ReAction:RefreshBar(self)
end

function Bar:AddButton(idx, button)
  local f = self:GetFrame()

  -- store in a weak reverse-index array
  self.buttons[button] = idx

  -- Store a properly wrapped reference to the child frame as an attribute 
  -- (accessible via "frameref-btn#")
  f:SetFrameRef(format("btn%d",idx), button:GetFrame())
end

function Bar:RemoveButton(button)
  local idx = self.buttons[button]
  if idx then
    self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil)
    self.buttons[button] = nil
  end
end

-- iterator returns button, idx and does NOT iterate in index order
function Bar:IterateButtons()
  return pairs(self.buttons)
end

function Bar:PlaceButton(button, baseW, baseH)
  local idx = self.buttons[button]
  if idx then 
    local r, c, s = self:GetButtonGrid()
    local bh, bw = self:GetButtonSize()
    local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
    local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s)
    local scale = bw/baseW
    local b = button:GetFrame()

    b:ClearAllPoints()
    b:SetPoint("TOPLEFT",x/scale,y/scale)
    b:SetScale(scale)
  end
end

function Bar:SkinButton()
  -- does nothing by default
end