diff Bar.lua @ 77:da8ba8783924

- added revision updater to each code file - Changed button/bar class mechanic to metatable-based - Changed buttons to live within a sub-frame, to play nicely between show-empty-buttons and hidestates - bar frame is now available only via accessor - Changed some semantics with AddButton/PlaceButton - Cleaned up action buttons options, fixed hide-when-empty option - moved show-action-ID-label as a button method - converted drag overlay from nested-frame to :Raise() - fixed ReAction:SetConfigMode() to not call event when mode doesn't change - Fixed ordering for dynamic state tab (always last)
author Flick <flickerstreak@gmail.com>
date Mon, 23 Jun 2008 22:27:50 +0000
parents 06cd74bdc7da
children 57f8151ea0f0
line wrap: on
line diff
--- a/Bar.lua	Thu Jun 19 17:48:57 2008 +0000
+++ b/Bar.lua	Mon Jun 23 22:27:50 2008 +0000
@@ -7,18 +7,16 @@
 local format = string.format
 local SecureStateHeader_Refresh = SecureStateHeader_Refresh
 
-
--- update ReAction revision if this file is newer
-local revision = tonumber(("$Revision$"):match("%d+"))
-if revision > ReAction.revision then
-  ReAction.revision = revision
-end
+ReAction:UpdateRevision("$Revision$")
 
 
 ------ BAR CLASS ------
 local Bar = { _classID = {} }
+ReAction.Bar = Bar -- export to ReAction
 
-local function Constructor( self, name, config )
+function Bar:New( name, config )
+  -- create new self
+  self = setmetatable( { }, {__index = Bar} )
   if type(config) ~= "table" then
     error("ReAction.Bar: config table required")
   end
@@ -32,24 +30,43 @@
 
   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,"SecureStateHeaderTemplate, SecureActionButtonTemplate")
-
-  -- The frame itself is read-only
-  function self:GetFrame()
-    return f
-  end
+  f:SetFrameStrata("MEDIUM")
+  f:SetWidth(config.width)
+  f:SetWidth(config.height)
+  f:Show()
 
   -- The bar itself is also a Button derived from SecureActionButtonTemplate, so it has an OnClick handler
   -- which we can use as a virtual button for keybinds, which will send attribute-value changes to itself.
   -- However, we don't ever want the user to be able to click it directly.
   f:EnableMouse(false)
   f:SetAttribute("type","attribute")
-  f:SetFrameStrata("MEDIUM")
-  f:SetWidth(config.width)
-  f:SetWidth(config.height)
-  f:Show()
+
+  -- Buttons are contained in an anonymous intermediate sub-frame. This arrangement is to specifically
+  -- address the issue of the interaction with hidestates and auto-hiding empty action buttons (the two
+  -- don't play nicely together). It also has the fringe benefit of making show/hide faster because a
+  -- single frame is shown/hidden instead of potentially dozens. Unfortunately it does add an extra layer
+  -- of indirection to all state changes, as a secondary (trivial) statemap must be invoked. This
+  -- complicates frame setup slightly.
+  local bf = CreateFrame("Frame", nil, f, "SecureStateHeaderTemplate")
+  bf:SetAllPoints()
+  bf:Show()
+  bf:SetAttribute("useparent*",true)       -- this facilitates SecureButton_GetModifiedAttribute()
+  bf:SetAttribute("statemap-parent","*:=") -- however some methods don't use it, so propagate the state too
+  f:SetAttribute("addchild",bf)
+
+  -- Both frames are read-only. Override the default accessors for this object.
+  function self:GetFrame()
+    return f
+  end
+
+  function self:GetButtonFrame()
+    return bf
+  end
 
   self:ApplyAnchor()
   ReAction.RegisterCallback(self, "OnConfigModeChanged")
+
+  return self
 end
 
 function Bar:Destroy()
@@ -158,6 +175,18 @@
   return self.name
 end
 
+function Bar:GetFrame()
+  -- this method is included for documentation purposes. It is overridden
+  -- in the New method for each object.
+  error("Invalid Bar object: used without initialization")
+end
+
+function Bar:GetButtonFrame()
+  -- this method is included for documentation purposes. It is overridden
+  -- in the New method for each object.
+  error("Invalid Bar object: used without initialization")
+end
+
 -- only ReAction:RenameBar() should call this function
 function Bar:SetName(name)
   self.name = name
@@ -168,7 +197,9 @@
 end
 
 function Bar:AddButton(idx, button)
+  -- store in a reverse-index array
   self.buttons[button] = idx
+  self:GetButtonFrame():SetAttribute("addchild",button:GetFrame())
   SecureStateHeader_Refresh(self:GetFrame())
 end
 
@@ -234,7 +265,7 @@
   end
 end
 
--- Set an attribute on the frame (or its buttons if 'doButtons' = true)
+-- Set an attribute on the frame (or buttonFrame if 'buttonFrame' = true)
 -- Either or both 'map' and 'default' can be passed:
 --   - If 'map' is omitted, then 'default' is set to the attribute.
 --   - If 'map' is provided, then it is interpreted as an unordered
@@ -243,7 +274,8 @@
 --     string, e.g. "<state1>:<value1>;<state2>:<value2>". If 'default'
 --     is also provided, then its value will be converted to a string
 --     and appended.
-function Bar:SetStateAttribute( attribute, map, default, doButtons )
+function Bar:SetStateAttribute( attribute, map, default, buttonFrame )
+  local f = buttonFrame and self:GetButtonFrame() or self:GetFrame()
   local value = default
   if map then
     local tmp = { }
@@ -255,29 +287,6 @@
     end
     value = table.concat(tmp,";")
   end
-  if doButtons then
-    for b in pairs(self.buttons) do
-      local f = b.GetFrame and b:GetFrame()
-      if f then
-        f:SetAttribute(attribute, value)
-      end
-    end
-  else
-    self:GetFrame():SetAttribute(attribute, value)
-  end
-  SecureStateHeader_Refresh(self:GetFrame())
+  f:SetAttribute(attribute, value)
+  SecureStateHeader_Refresh(f)
 end
-
-
------- Export as a class-factory ------
-ReAction.Bar = {
-  prototype = Bar,
-  New = function(self, ...)
-    local x = { }
-    for k,v in pairs(Bar) do
-      x[k] = v
-    end
-    Constructor(x, ...)
-    return x
-  end
-}