diff Bar.lua @ 72:aa88aed52124

Fixed bugs with state keybinds. Simplified state driver API
author Flick <flickerstreak@gmail.com>
date Thu, 05 Jun 2008 18:34:36 +0000
parents 3d2cef5dc459
children dd01feae0d89
line wrap: on
line diff
--- a/Bar.lua	Wed Jun 04 21:46:51 2008 +0000
+++ b/Bar.lua	Thu Jun 05 18:34:36 2008 +0000
@@ -51,6 +51,9 @@
   f:SetParent(UIParent)
   f:ClearAllPoints()
   ReAction.UnregisterAllCallbacks(self)
+  if self.statedriver then
+    UnregisterStateDriver(f, "reaction")
+  end
   self.labelString = nil
   self.controlFrame = nil
   self.frame = nil
@@ -176,6 +179,49 @@
   return self.config.nPages or 1
 end
 
+  --
+  -- 'rule' is a rule-string to pass to RegisterStateDriver
+  -- 'states' is a { ["statename"] = <don't care> } table of all state names
+  -- 'keybinds' is a { ["statename"] = keybind } table of all keybound states
+  --
+function Bar:SetStateDriver( rule, states, keybinds )
+  local f = self.frame
+  local kbprefix = ""
+  do
+    local tmp = { }
+    for s, k in pairs(keybinds) do
+      if k and #k > 0 then -- filter out false table entries
+        -- if in a keybound state, set the stack to the new state but stay in the keybound state.
+        -- use $s as a placeholder for the current state, it will be gsub()'d in later
+        table.insert(tmp,("%s:$s set() %s"):format(s,s))
+      end
+    end
+    table.insert(tmp,kbprefix)  -- to get a trailing ';' if the table is not empty
+    kbprefix = table.concat(tmp,";")
+  end
+  for state in pairs(states) do
+    -- For all states: if in a keybound state, stay there (with stack manipulation, see above).
+    -- Otherwise, go to the state
+    f:SetAttribute(("statemap-reaction-%s"):format(state),("%s%s"):format(kbprefix:gsub("%$s",state),state))
+
+    local binding = keybinds[state]
+    self:SetStateKeybind(binding, state) -- set the binding even if nil, to clear it unconditionally
+    if binding then
+      -- for key bindings, use the state-stack to toggle between the last state and the keybound state
+      -- use a different 'virtual state' passed to attribute 'reaction-state' for key bindings, "<state>_binding"
+      f:SetAttribute(("statemap-reaction-%s_binding"):format(state), ("%s:pop();*:set(%s)"):format(state,state))
+    end
+  end
+
+  if rule and #rule > 0 then
+    self.stateDriver = true
+    RegisterStateDriver(f, "reaction", rule)
+  elseif self.statedriver then
+    self.statedriver = false
+    UnregisterStateDriver(f, "reaction")
+  end
+end
+
 function Bar:SetHideStates(s)
   for f in pairs(self.buttons) do
     if f:GetParent() == self.frame then
@@ -186,36 +232,36 @@
 end
 
 function Bar:SetStateKeybind(key, state, defaultstate)
-  -- set a keybind to toggle transitioning unconditionally to a state
-  -- use a tiny offscreen button to get around making the bar itself a clickable button
+  -- Lazily create a tiny offscreen button which sends "<state>_binding" values to the
+  -- bar frame's state-reaction attribute, by using an override binding to generate a 
+  -- click on the button with a virtual mouse button "state". 
+  -- This gets around making the bar itself a clickable button, which is not desirable
   local f = self.statebuttonframe
-  local off = ("%s_off"):format(state)
   if key then
     if not f then
-      f = CreateFrame("Button",self:GetName().."_statebutton",UIParent,"SecureActionButtonTemplate")
+      f = CreateFrame("Button",self:GetName().."_statebutton",self.frame,"SecureActionButtonTemplate")
       f:SetPoint("BOTTOMRIGHT",UIParent,"TOPLEFT")
       f:SetWidth(1)
       f:SetHeight(1)
-      f:SetAttribute("attribute-name", "state")
-      f:SetAttribute("attribute-frame",self.frame)
-      f:SetAttribute("stateheader",self.frame)
+      f:SetAttribute("type*","attribute")
+      f:SetAttribute("attribute-name*","state-reaction")
+      f:SetAttribute("attribute-frame*",self.frame)
       f:Show()
+      f.bindings = { }
       self.statebuttonframe = f
     end
-    -- map two virtual buttons to toggle between the state and the default
-    f:SetAttribute(("statebutton-%s"):format(state),("%s:%s;%s"):format(state,off,state))
-    f:SetAttribute(("type-%s"):format(state),"attribute")
-    f:SetAttribute(("type-%s"):format(off),"attribute")
-    f:SetAttribute(("attribute-value-%s"):format(state), state)
-    f:SetAttribute(("attribute-value-%s"):format(off), defaultstate)
-    SetBindingClick(key, f:GetName(), state)
+    f:SetAttribute(("attribute-value-%s"):format(state),("%s_binding"):format(state))
+      -- clear the old binding, if any, for this state
+    if f.bindings[state] then
+      SetOverrideBinding(self.frame, false, f.bindings[state], nil)
+    end
+    SetOverrideBindingClick(self.frame, false, key, f:GetName(), state) -- the state name is used as the virtual button
+    f.bindings[state] = key
   elseif f then
-    f:SetAttribute(("type-%s"):format(state),ATTRIBUTE_NOOP)
-    f:SetAttribute(("type-%s"):format(off),ATTRIBUTE_NOOP)
-    local action = ("CLICK %s:%s"):format(f:GetName(),state)
-    key = GetBindingKey(action)
+    key = f.bindings[state]
     if key then
-      SetBinding(key,nil)
+      SetOverrideBinding(self.frame, false, key, nil)
+      f.bindings[state] = nil
     end
   end
 end
@@ -227,7 +273,10 @@
     table.insert(tmp, ("%s:page%d"):format(s,p))
   end
   local spec = table.concat(tmp,";")
-  f:SetAttribute("statebutton",spec)
+  local current = f:GetAttribute("statebutton")
+  if spec ~= f:GetAttribute("statebutton") then
+    f:SetAttribute("statebutton", spec)
+  end
   SecureStateHeader_Refresh(f)
 end