view classes/ReAction_ActionDisplay.lua @ 9:650f75d08952

Version 0.32
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:35:57 +0000
parents c05fd3e18b4f
children f3a7bfebc283
line wrap: on
line source
-- The ReAction.ActionDisplay mixin defines 'regular' action button display functionality
-- and is an implementation of the ReAction.IDisplay and ReAction.ActionType.IDisplay interfaces.
--
-- This Mixin assumes that it has been mixed in with a ReAction-derived class which implements
-- the ReAction.IActionType and ReAction.IColorScheme interfaces.
--
-- This mixin uses properties of self.config to define display elements:
--
-- self.config = {
--   keyBindLoc = "POSITION",         -- keybind anchor location
--   stackCountLoc = "POSITION",      -- stack count anchor location
--   showKeyBind = true/false,        -- show keybind labels
--   showStackCount = true/false,     -- show stack count labels
--   showMacroText = true/false,      -- show macro name labels
--   showGrid = true/false,           -- always show empty buttons
--   hideCooldown = true/false,       -- hide the cooldown timer
--   hideGlobalCooldown = true/false, -- hide cooldown timers if duration < 1.5 seconds (global)
--   opacity = {
--     default   = 0-100 [100],       -- button opacity when the action is usable (default opacity)
--     notUsable = 0-100 [100],       -- button opacity when the action is not usable
--     oom       = 0-100 [notUsable], -- button opacity when the action is not usable due to OOM
--     ooRange   = 0-100 [notUsable], -- button opacity when the action is not usable due to out of range
--     empty     = 0-100 [0],         -- button opacity when the action slot is empty
--   },
--   hideEmptySlots = true/false,   -- show/hide empty buttons rather than change opacity to 0
-- }
--

local AceOO = AceLibrary("AceOO-2.0")

ReAction.ActionDisplay = AceOO.Mixin {
  -- ReAction.IDisplay interface
  "SetupDisplay",
  "UpdateDisplay",
  "TempShow",
  "GetActionFrame",
  "GetBaseButtonSize",
  "DisplayID",
  "DisplayHotkey",

  -- ReAction.ActionType.IDisplay interface
  "DisplayUsable",
  "DisplayEquipped",
  "DisplayAutoRepeat",
  "DisplayInUse",
  "DisplayIcon",
  "DisplayName",
  "DisplayCount",
  "DisplayCooldown",

  -- Event handlers
  "PostClick",
  "OnDragStart",
  "OnReceiveDrag",
  "OnEnter",
  "OnLeave",
  "OnUpdate",

  -- internal functions
  "ApplyLayout",
  "ApplyStyle",
  "StartFlash",
  "StopFlash",
  "IsFlashing",
  "DisplayVisibility",
}

local RAAD = ReAction.ActionDisplay


-- private constants
local _G = getfenv(0)

local equippedActionBorderColor  = { r=0.00,  g=1.00,  b=0.00,  a=0.35 } -- transparent green
local actionIDColor              = { r=1.00,  g=0.82,  b=0.00,  a=1.00 } -- gold

-- private functions
-- extract and return color fields from a table, to be fed into SetVertexColor()/SetTextColor()
local function tcolor(c)
  return c.r, c.g, c.b, c.a
end


-----------------------------------
-- Interface Implementation Methods
-----------------------------------
function RAAD:SetupDisplay( name )
  -- create the button widget
  local b = CreateFrame("CheckButton", name, nil, "SecureActionButtonTemplate, ActionButtonTemplate")

  -- store references to the various sub-frames of ActionButtonTemplate so we don't have to look it up all the time
  self.frames = {
    button         = b,
    hotkey         = _G[name.."HotKey"],
    count          = _G[name.."Count"],
    cooldown       = _G[name.."Cooldown"],
    macro          = _G[name.."Name"],
    icon           = _G[name.."Icon"],
    border         = _G[name.."Border"],
    flash          = _G[name.."Flash"],
    normalTexture  = _G[name.."NormalTexture"],
    actionID       = nil,    -- defer creating actionID font string until it's actually requested
  }

  -- ??? odd: why do we have to increment the cooldown frame level to get it to show? 
  -- (otherwise it's behind the icon). The default UI doesn't have to (or at least I can't
  -- find where it does) but for some reason we have to here.
  self.frames.cooldown:SetFrameLevel(self.frames.cooldown:GetFrameLevel() + 1)

  b:EnableMouse()
  b:RegisterForDrag("LeftButton", "RightButton")
  b:RegisterForClicks("AnyUp")
  b:SetScript("PostClick",           function(arg1) self:PostClick(arg1) end)
  b:SetScript("OnDragStart",         function(arg1) self:OnDragStart(arg1) end)
  b:SetScript("OnReceiveDrag",       function() self:OnReceiveDrag() end)
  b:SetScript("OnEnter",             function() self:OnEnter() end)
  b:SetScript("OnLeave",             function() self:OnLeave() end)
  -- defer setting OnUpdate until actions are actually attached

  self.tmpShow_ = 0
end

function RAAD:UpdateDisplay()
  self:ApplyLayout()
  self:ApplyStyle()
  self:DisplayVisibility()
  -- refresh the action ID display
  if ReAction.showIDs_ then
    self:DisplayID(true)
  end
end

function RAAD:TempShow( visible )
  visible = visible and true or false -- force data integrity
  self.showTmp_ = max(0, (self.showTmp_ or 0) + (visible and 1 or -1))
  self:DisplayVisibility()
end

function RAAD:GetActionFrame()
  return self.frames.button
end

function RAAD:GetBaseButtonSize()
  return 36
end

function RAAD:DisplayID( show )
  local f = self.frames.actionID
  if show then
    if not f then
      -- create the actionID label
      f = self.frames.button:CreateFontString(nil,"ARTWORK","NumberFontNormalSmall")
      f:SetPoint("BOTTOMLEFT")
      f:SetTextColor( tcolor(actionIDColor) )
      self.frames.actionID = f
    end
    f:SetText(tostring(self:GetID()))
    f:Show()
  elseif f then
    f:Hide()
  end
end

function RAAD:DisplayHotkey(txt)
  self.frames.hotkey:SetText(string.upper(txt or ""))
  self:UpdateUsable()
end

function RAAD:DisplayUsable( isUsable, notEnoughMana, outOfRange )
  local f = self.frames
  f.icon:SetVertexColor(          self:GetIconColor(isUsable, notEnoughMana, outOfRange) )
  f.button:GetNormalTexture():SetVertexColor( self:GetBorderColor(isUsable, notEnoughMana, outOfRange) )
  f.hotkey:SetTextColor(          self:GetHotkeyColor(isUsable, notEnoughMana, outOfRange, f.hotkey:GetText()) )

  local o 
  if isUsable then
    o = self.config.opacity and self.config.opacity.usable or 1
  else
    o = self.config.opacity and self.config.opacity.notUsable or 1
    if notEnoughMana then
      o = self.config.opacity and self.config.opacity.oom or o
    elseif outOfRange then
      o = self.config.opacity and self.config.opacity.ooRange or o
    end
  end

  self.currentOpacity = o  -- store for use in DisplayVisibility
  self:DisplayVisibility()
end

function RAAD:DisplayEquipped( equipped )
  local b = self.frames.border
  if equipped then
    b:Show()
  else
    b:Hide()
  end
end

function RAAD:DisplayAutoRepeat( r )
  if r then
    self:StartFlash()
  else
    self:StopFlash()
  end
end

function RAAD:DisplayInUse( inUse )
  self.frames.button:SetChecked( inUse and 1 or 0 )
end

function RAAD:DisplayIcon( texture )
  local f = self.frames.button
  local icon = self.frames.icon
  if texture then
    icon:SetTexture(texture)
    icon:Show()
    self.rangeTimer = -1
    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
    if f:GetScript("OnUpdate") == nil then
      f:SetScript("OnUpdate", function(frame, elapsed) self:OnUpdate(elapsed) end)
    end
  else
    icon:Hide()
    self.rangeTimer = nil
    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
    f:SetScript("OnUpdate",nil)
  end
  self:DisplayVisibility()
end

function RAAD:DisplayCooldown( start, duration, enable )
  enable = (enable > 0 ) and not self.config.hideCooldown and (not self.config.hideGlobalCooldown or duration > 1.5) and 1 or 0
  CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enable)
end

function RAAD:DisplayName( name )
  self.frames.macro:SetText(name and tostring(name) or "")
end

function RAAD:DisplayCount( count )
  self.frames.count:SetText(count and tostring(count) or "")
end





----------------------
-- Event Handlers
----------------------
function RAAD:PostClick()
  self:UpdateInUse()
end

function RAAD:OnDragStart()
  if LOCK_ACTIONBAR ~= "1" or IsShiftKeyDown() then
    self:PickupAction()
  end
end

function RAAD:OnReceiveDrag()
  self:PlaceAction()
end

function RAAD:OnEnter()
  self:SetTooltip() -- from ReAction base class
  self.tooltipTime = TOOLTIP_UPDATE_TIME
end

function RAAD:OnLeave()
  self:ClearTooltip() -- from ReAction base class
  self.tooltipTime = nil
end

function RAAD:OnUpdate(elapsed)
  -- handle flashing
	if self:IsFlashing() then
		self.flashtime = self.flashtime - elapsed
		if self.flashtime <= 0 then
			local overtime = -self.flashtime
			if overtime >= ATTACK_BUTTON_FLASH_TIME then
				overtime = 0
			end
			self.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
			
			local f = self.frames.flash
			if f then
        if f:IsVisible() then
          f:Hide()
        else
          f:Show()
        end
      end
		end
	end
	
	-- Handle range indicator
	if self.rangeTimer then
		self.rangeTimer = self.rangeTimer - elapsed
		if self.rangeTimer <= 0 then
      self:UpdateUsable()
			self.rangeTimer = TOOLTIP_UPDATE_TIME
		end
	end

  -- handle tooltip update
  if self.tooltipTime then
    self.tooltipTime = self.tooltipTime - elapsed
    if self.tooltipTime <= 0 then
      if GameTooltip:IsOwned(self.frames.button) then
        self:UpdateTooltip()
        self.tooltipTime = TOOLTIP_UPDATE_TIME
      else
        self.tooltipTime = nil
      end
    end
  end
end



----------------------
-- Internal methods
----------------------

local function placeLabel( label, anchor )
  local top = string.match(anchor,"TOP")
  local bottom = string.match(anchor, "BOTTOM")
  label:ClearAllPoints()
  label:SetWidth(40)
  label:SetPoint(top or bottom or "CENTER",0,top and 2 or bottom and -2 or 0)
  local j
  if string.match(anchor,"LEFT") then
    j = "LEFT"
  elseif string.match(anchor,"RIGHT") then
    j = "RIGHT"
  else
    j = "CENTER"
  end
  label:SetJustifyH(j)
end


function RAAD:ApplyLayout()
  local f = self.frames

  if self.config.keyBindLoc then
    placeLabel(f.hotkey, self.config.keyBindLoc)
  end

  if self.config.stackCountLoc then
    placeLabel(f.count, self.config.stackCountLoc)
  end

  if self.config.showKeyBind then
    f.hotkey:Show()
  else
    f.hotkey:Hide()
  end
    
  if self.config.showStackCount then
    f.count:Show()
  else
    f.count:Hide()
  end

  if self.config.showMacroName then
    f.macro:Show()
  else
    f.macro:Hide()
  end

end

function RAAD:ApplyStyle()
  local f = self.frames
  -- for now, just a static style
  f.hotkey:SetFontObject(NumberFontNormal)
  f.count:SetFontObject(NumberFontNormalYellow)
  f.border:SetVertexColor( tcolor(equippedActionBorderColor) )
end

function RAAD:StartFlash()
  self.flashing = true
  self.flashtime = 0
end

function RAAD:StopFlash()
  self.flashing = false
  self.frames.flash:Hide()
end

function RAAD:IsFlashing()
  return self.flashing
end

function RAAD:DisplayVisibility()
  local b = self.frames.button

  if b:GetAttribute("statehidden") then
    -- can't hide/show in combat
    if not InCombatLockdown() then
      b:Hide()
    end
  elseif self.showTmp_ and self.showTmp_ > 0 then
    b:GetNormalTexture():SetAlpha(0.5)
    if self:IsActionEmpty() then
      self.frames.cooldown:Hide()
      if not InCombatLockdown() and not b:IsShown() then
        b:Show()
      end
    end
    b:SetAlpha(1)
  elseif not self:IsActionEmpty() then
    b:GetNormalTexture():SetAlpha(1.0)
    b:SetAlpha(self.currentOpacity or (self.config.opacity and self.config.opacity.usable) or 1)
  else
    if self.config.hideEmptySlots then
      if not InCombatLockdown() then
        b:Hide()
      end
    else
      b:SetAlpha(self.config.opacity and self.config.opacity.empty or 0)
    end
  end
end